mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Fix scene marker issues (#3955)
* Fix scene marker NOT NULL constraint error * similar changes to gallery chapters * Fix NULL conversion error if names are NULL in DB * Fix scene marker form resetting
This commit is contained in:
@@ -14,8 +14,8 @@ type SceneMarker struct {
|
|||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SceneMarkerPartial represents part of a SceneMarker object. It is used to update
|
// SceneMarkerPartial represents part of a SceneMarker object.
|
||||||
// the database entry.
|
// It is used to update the database entry.
|
||||||
type SceneMarkerPartial struct {
|
type SceneMarkerPartial struct {
|
||||||
Title OptionalString
|
Title OptionalString
|
||||||
Seconds OptionalFloat64
|
Seconds OptionalFloat64
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const (
|
|||||||
|
|
||||||
type galleryChapterRow struct {
|
type galleryChapterRow struct {
|
||||||
ID int `db:"id" goqu:"skipinsert"`
|
ID int `db:"id" goqu:"skipinsert"`
|
||||||
Title string `db:"title"`
|
Title string `db:"title"` // TODO: make db schema (and gql schema) nullable
|
||||||
ImageIndex int `db:"image_index"`
|
ImageIndex int `db:"image_index"`
|
||||||
GalleryID int `db:"gallery_id"`
|
GalleryID int `db:"gallery_id"`
|
||||||
CreatedAt Timestamp `db:"created_at"`
|
CreatedAt Timestamp `db:"created_at"`
|
||||||
@@ -54,7 +54,12 @@ type galleryChapterRowRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *galleryChapterRowRecord) fromPartial(o models.GalleryChapterPartial) {
|
func (r *galleryChapterRowRecord) fromPartial(o models.GalleryChapterPartial) {
|
||||||
r.setString("title", o.Title)
|
// TODO: replace with setNullString after schema is made nullable
|
||||||
|
// r.setNullString("title", o.Title)
|
||||||
|
// saves a null input as the empty string
|
||||||
|
if o.Title.Set {
|
||||||
|
r.set("title", o.Title.Value)
|
||||||
|
}
|
||||||
r.setInt("image_index", o.ImageIndex)
|
r.setInt("image_index", o.ImageIndex)
|
||||||
r.setInt("gallery_id", o.GalleryID)
|
r.setInt("gallery_id", o.GalleryID)
|
||||||
r.setTimestamp("created_at", o.CreatedAt)
|
r.setTimestamp("created_at", o.CreatedAt)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const (
|
|||||||
|
|
||||||
type performerRow struct {
|
type performerRow struct {
|
||||||
ID int `db:"id" goqu:"skipinsert"`
|
ID int `db:"id" goqu:"skipinsert"`
|
||||||
Name string `db:"name"`
|
Name null.String `db:"name"` // TODO: make schema non-nullable
|
||||||
Disambigation zero.String `db:"disambiguation"`
|
Disambigation zero.String `db:"disambiguation"`
|
||||||
Gender zero.String `db:"gender"`
|
Gender zero.String `db:"gender"`
|
||||||
URL zero.String `db:"url"`
|
URL zero.String `db:"url"`
|
||||||
@@ -65,7 +65,7 @@ type performerRow struct {
|
|||||||
|
|
||||||
func (r *performerRow) fromPerformer(o models.Performer) {
|
func (r *performerRow) fromPerformer(o models.Performer) {
|
||||||
r.ID = o.ID
|
r.ID = o.ID
|
||||||
r.Name = o.Name
|
r.Name = null.StringFrom(o.Name)
|
||||||
r.Disambigation = zero.StringFrom(o.Disambiguation)
|
r.Disambigation = zero.StringFrom(o.Disambiguation)
|
||||||
if o.Gender != nil && o.Gender.IsValid() {
|
if o.Gender != nil && o.Gender.IsValid() {
|
||||||
r.Gender = zero.StringFrom(o.Gender.String())
|
r.Gender = zero.StringFrom(o.Gender.String())
|
||||||
@@ -101,7 +101,7 @@ func (r *performerRow) fromPerformer(o models.Performer) {
|
|||||||
func (r *performerRow) resolve() *models.Performer {
|
func (r *performerRow) resolve() *models.Performer {
|
||||||
ret := &models.Performer{
|
ret := &models.Performer{
|
||||||
ID: r.ID,
|
ID: r.ID,
|
||||||
Name: r.Name,
|
Name: r.Name.String,
|
||||||
Disambiguation: r.Disambigation.String,
|
Disambiguation: r.Disambigation.String,
|
||||||
URL: r.URL.String,
|
URL: r.URL.String,
|
||||||
Twitter: r.Twitter.String,
|
Twitter: r.Twitter.String,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ GROUP BY scene_markers.id
|
|||||||
|
|
||||||
type sceneMarkerRow struct {
|
type sceneMarkerRow struct {
|
||||||
ID int `db:"id" goqu:"skipinsert"`
|
ID int `db:"id" goqu:"skipinsert"`
|
||||||
Title string `db:"title"`
|
Title string `db:"title"` // TODO: make db schema (and gql schema) nullable
|
||||||
Seconds float64 `db:"seconds"`
|
Seconds float64 `db:"seconds"`
|
||||||
PrimaryTagID int `db:"primary_tag_id"`
|
PrimaryTagID int `db:"primary_tag_id"`
|
||||||
SceneID int `db:"scene_id"`
|
SceneID int `db:"scene_id"`
|
||||||
@@ -62,7 +62,12 @@ type sceneMarkerRowRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *sceneMarkerRowRecord) fromPartial(o models.SceneMarkerPartial) {
|
func (r *sceneMarkerRowRecord) fromPartial(o models.SceneMarkerPartial) {
|
||||||
r.setNullString("title", o.Title)
|
// TODO: replace with setNullString after schema is made nullable
|
||||||
|
// r.setNullString("title", o.Title)
|
||||||
|
// saves a null input as the empty string
|
||||||
|
if o.Title.Set {
|
||||||
|
r.set("title", o.Title.Value)
|
||||||
|
}
|
||||||
r.setFloat64("seconds", o.Seconds)
|
r.setFloat64("seconds", o.Seconds)
|
||||||
r.setInt("primary_tag_id", o.PrimaryTagID)
|
r.setInt("primary_tag_id", o.PrimaryTagID)
|
||||||
r.setInt("scene_id", o.SceneID)
|
r.setInt("scene_id", o.SceneID)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/doug-martin/goqu/v9"
|
"github.com/doug-martin/goqu/v9"
|
||||||
"github.com/doug-martin/goqu/v9/exp"
|
"github.com/doug-martin/goqu/v9/exp"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"gopkg.in/guregu/null.v4"
|
||||||
"gopkg.in/guregu/null.v4/zero"
|
"gopkg.in/guregu/null.v4/zero"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
@@ -27,7 +28,7 @@ const (
|
|||||||
|
|
||||||
type tagRow struct {
|
type tagRow struct {
|
||||||
ID int `db:"id" goqu:"skipinsert"`
|
ID int `db:"id" goqu:"skipinsert"`
|
||||||
Name string `db:"name"`
|
Name null.String `db:"name"` // TODO: make schema non-nullable
|
||||||
Description zero.String `db:"description"`
|
Description zero.String `db:"description"`
|
||||||
IgnoreAutoTag bool `db:"ignore_auto_tag"`
|
IgnoreAutoTag bool `db:"ignore_auto_tag"`
|
||||||
CreatedAt Timestamp `db:"created_at"`
|
CreatedAt Timestamp `db:"created_at"`
|
||||||
@@ -39,7 +40,7 @@ type tagRow struct {
|
|||||||
|
|
||||||
func (r *tagRow) fromTag(o models.Tag) {
|
func (r *tagRow) fromTag(o models.Tag) {
|
||||||
r.ID = o.ID
|
r.ID = o.ID
|
||||||
r.Name = o.Name
|
r.Name = null.StringFrom(o.Name)
|
||||||
r.Description = zero.StringFrom(o.Description)
|
r.Description = zero.StringFrom(o.Description)
|
||||||
r.IgnoreAutoTag = o.IgnoreAutoTag
|
r.IgnoreAutoTag = o.IgnoreAutoTag
|
||||||
r.CreatedAt = Timestamp{Timestamp: o.CreatedAt}
|
r.CreatedAt = Timestamp{Timestamp: o.CreatedAt}
|
||||||
@@ -49,7 +50,7 @@ func (r *tagRow) fromTag(o models.Tag) {
|
|||||||
func (r *tagRow) resolve() *models.Tag {
|
func (r *tagRow) resolve() *models.Tag {
|
||||||
ret := &models.Tag{
|
ret := &models.Tag{
|
||||||
ID: r.ID,
|
ID: r.ID,
|
||||||
Name: r.Name,
|
Name: r.Name.String,
|
||||||
Description: r.Description.String,
|
Description: r.Description.String,
|
||||||
IgnoreAutoTag: r.IgnoreAutoTag,
|
IgnoreAutoTag: r.IgnoreAutoTag,
|
||||||
CreatedAt: r.CreatedAt.Timestamp,
|
CreatedAt: r.CreatedAt.Timestamp,
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ import isEqual from "lodash-es/isEqual";
|
|||||||
|
|
||||||
interface IGalleryChapterForm {
|
interface IGalleryChapterForm {
|
||||||
galleryID: string;
|
galleryID: string;
|
||||||
editingChapter?: GQL.GalleryChapterDataFragment;
|
chapter?: GQL.GalleryChapterDataFragment;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GalleryChapterForm: React.FC<IGalleryChapterForm> = ({
|
export const GalleryChapterForm: React.FC<IGalleryChapterForm> = ({
|
||||||
galleryID,
|
galleryID,
|
||||||
editingChapter,
|
chapter,
|
||||||
onClose,
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@@ -30,6 +30,8 @@ export const GalleryChapterForm: React.FC<IGalleryChapterForm> = ({
|
|||||||
const [galleryChapterDestroy] = useGalleryChapterDestroy();
|
const [galleryChapterDestroy] = useGalleryChapterDestroy();
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
|
|
||||||
|
const isNew = chapter === undefined;
|
||||||
|
|
||||||
const schema = yup.object({
|
const schema = yup.object({
|
||||||
title: yup.string().ensure(),
|
title: yup.string().ensure(),
|
||||||
image_index: yup
|
image_index: yup
|
||||||
@@ -41,8 +43,8 @@ export const GalleryChapterForm: React.FC<IGalleryChapterForm> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
title: editingChapter?.title ?? "",
|
title: chapter?.title ?? "",
|
||||||
image_index: editingChapter?.image_index ?? 1,
|
image_index: chapter?.image_index ?? 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
type InputValues = yup.InferType<typeof schema>;
|
type InputValues = yup.InferType<typeof schema>;
|
||||||
@@ -56,7 +58,7 @@ export const GalleryChapterForm: React.FC<IGalleryChapterForm> = ({
|
|||||||
|
|
||||||
async function onSave(input: InputValues) {
|
async function onSave(input: InputValues) {
|
||||||
try {
|
try {
|
||||||
if (!editingChapter) {
|
if (isNew) {
|
||||||
await galleryChapterCreate({
|
await galleryChapterCreate({
|
||||||
variables: {
|
variables: {
|
||||||
gallery_id: galleryID,
|
gallery_id: galleryID,
|
||||||
@@ -66,7 +68,7 @@ export const GalleryChapterForm: React.FC<IGalleryChapterForm> = ({
|
|||||||
} else {
|
} else {
|
||||||
await galleryChapterUpdate({
|
await galleryChapterUpdate({
|
||||||
variables: {
|
variables: {
|
||||||
id: editingChapter.id,
|
id: chapter.id,
|
||||||
gallery_id: galleryID,
|
gallery_id: galleryID,
|
||||||
...input,
|
...input,
|
||||||
},
|
},
|
||||||
@@ -80,10 +82,10 @@ export const GalleryChapterForm: React.FC<IGalleryChapterForm> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onDelete() {
|
async function onDelete() {
|
||||||
if (!editingChapter) return;
|
if (isNew) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await galleryChapterDestroy({ variables: { id: editingChapter.id } });
|
await galleryChapterDestroy({ variables: { id: chapter.id } });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Toast.error(e);
|
Toast.error(e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -130,9 +132,7 @@ export const GalleryChapterForm: React.FC<IGalleryChapterForm> = ({
|
|||||||
<div className="col d-flex">
|
<div className="col d-flex">
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
disabled={
|
disabled={(!isNew && !formik.dirty) || !isEqual(formik.errors, {})}
|
||||||
(editingChapter && !formik.dirty) || !isEqual(formik.errors, {})
|
|
||||||
}
|
|
||||||
onClick={() => formik.submitForm()}
|
onClick={() => formik.submitForm()}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="actions.save" />
|
<FormattedMessage id="actions.save" />
|
||||||
@@ -145,7 +145,7 @@ export const GalleryChapterForm: React.FC<IGalleryChapterForm> = ({
|
|||||||
>
|
>
|
||||||
<FormattedMessage id="actions.cancel" />
|
<FormattedMessage id="actions.cancel" />
|
||||||
</Button>
|
</Button>
|
||||||
{editingChapter && (
|
{!isNew && (
|
||||||
<Button
|
<Button
|
||||||
variant="danger"
|
variant="danger"
|
||||||
className="ml-auto"
|
className="ml-auto"
|
||||||
|
|||||||
@@ -12,22 +12,24 @@ interface IGalleryChapterPanelProps {
|
|||||||
onClickChapter: (index: number) => void;
|
onClickChapter: (index: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GalleryChapterPanel: React.FC<IGalleryChapterPanelProps> = (
|
export const GalleryChapterPanel: React.FC<IGalleryChapterPanelProps> = ({
|
||||||
props: IGalleryChapterPanelProps
|
gallery,
|
||||||
) => {
|
isVisible,
|
||||||
|
onClickChapter,
|
||||||
|
}) => {
|
||||||
const [isEditorOpen, setIsEditorOpen] = useState<boolean>(false);
|
const [isEditorOpen, setIsEditorOpen] = useState<boolean>(false);
|
||||||
const [editingChapter, setEditingChapter] =
|
const [editingChapter, setEditingChapter] =
|
||||||
useState<GQL.GalleryChapterDataFragment>();
|
useState<GQL.GalleryChapterDataFragment>();
|
||||||
|
|
||||||
// set up hotkeys
|
// set up hotkeys
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.isVisible) {
|
if (!isVisible) return;
|
||||||
|
|
||||||
Mousetrap.bind("n", () => onOpenEditor());
|
Mousetrap.bind("n", () => onOpenEditor());
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Mousetrap.unbind("n");
|
Mousetrap.unbind("n");
|
||||||
};
|
};
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function onOpenEditor(chapter?: GQL.GalleryChapterDataFragment) {
|
function onOpenEditor(chapter?: GQL.GalleryChapterDataFragment) {
|
||||||
@@ -35,10 +37,6 @@ export const GalleryChapterPanel: React.FC<IGalleryChapterPanelProps> = (
|
|||||||
setEditingChapter(chapter ?? undefined);
|
setEditingChapter(chapter ?? undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClickChapter(image_index: number) {
|
|
||||||
props.onClickChapter(image_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeEditor = () => {
|
const closeEditor = () => {
|
||||||
setEditingChapter(undefined);
|
setEditingChapter(undefined);
|
||||||
setIsEditorOpen(false);
|
setIsEditorOpen(false);
|
||||||
@@ -47,8 +45,8 @@ export const GalleryChapterPanel: React.FC<IGalleryChapterPanelProps> = (
|
|||||||
if (isEditorOpen)
|
if (isEditorOpen)
|
||||||
return (
|
return (
|
||||||
<GalleryChapterForm
|
<GalleryChapterForm
|
||||||
galleryID={props.gallery.id}
|
galleryID={gallery.id}
|
||||||
editingChapter={editingChapter}
|
chapter={editingChapter}
|
||||||
onClose={closeEditor}
|
onClose={closeEditor}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -60,7 +58,7 @@ export const GalleryChapterPanel: React.FC<IGalleryChapterPanelProps> = (
|
|||||||
</Button>
|
</Button>
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<ChapterEntries
|
<ChapterEntries
|
||||||
galleryChapters={props.gallery.chapters}
|
galleryChapters={gallery.chapters}
|
||||||
onClickChapter={onClickChapter}
|
onClickChapter={onClickChapter}
|
||||||
onEdit={onOpenEditor}
|
onEdit={onOpenEditor}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useMemo } from "react";
|
||||||
import { Button, Form } from "react-bootstrap";
|
import { Button, Form } from "react-bootstrap";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useFormik } from "formik";
|
import { useFormik } from "formik";
|
||||||
@@ -17,13 +17,13 @@ import isEqual from "lodash-es/isEqual";
|
|||||||
|
|
||||||
interface ISceneMarkerForm {
|
interface ISceneMarkerForm {
|
||||||
sceneID: string;
|
sceneID: string;
|
||||||
editingMarker?: GQL.SceneMarkerDataFragment;
|
marker?: GQL.SceneMarkerDataFragment;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
||||||
sceneID,
|
sceneID,
|
||||||
editingMarker,
|
marker,
|
||||||
onClose,
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
const [sceneMarkerCreate] = useSceneMarkerCreate();
|
const [sceneMarkerCreate] = useSceneMarkerCreate();
|
||||||
@@ -31,6 +31,8 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
|||||||
const [sceneMarkerDestroy] = useSceneMarkerDestroy();
|
const [sceneMarkerDestroy] = useSceneMarkerDestroy();
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
|
|
||||||
|
const isNew = marker === undefined;
|
||||||
|
|
||||||
const schema = yup.object({
|
const schema = yup.object({
|
||||||
title: yup.string().ensure(),
|
title: yup.string().ensure(),
|
||||||
seconds: yup.number().required().integer(),
|
seconds: yup.number().required().integer(),
|
||||||
@@ -38,12 +40,16 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
|||||||
tag_ids: yup.array(yup.string().required()).defined(),
|
tag_ids: yup.array(yup.string().required()).defined(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const initialValues = {
|
// useMemo to only run getPlayerPosition when the input marker actually changes
|
||||||
title: editingMarker?.title ?? "",
|
const initialValues = useMemo(
|
||||||
seconds: editingMarker?.seconds ?? Math.round(getPlayerPosition() ?? 0),
|
() => ({
|
||||||
primary_tag_id: editingMarker?.primary_tag.id ?? "",
|
title: marker?.title ?? "",
|
||||||
tag_ids: editingMarker?.tags.map((tag) => tag.id) ?? [],
|
seconds: marker?.seconds ?? Math.round(getPlayerPosition() ?? 0),
|
||||||
};
|
primary_tag_id: marker?.primary_tag.id ?? "",
|
||||||
|
tag_ids: marker?.tags.map((tag) => tag.id) ?? [],
|
||||||
|
}),
|
||||||
|
[marker]
|
||||||
|
);
|
||||||
|
|
||||||
type InputValues = yup.InferType<typeof schema>;
|
type InputValues = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
@@ -56,7 +62,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
|||||||
|
|
||||||
async function onSave(input: InputValues) {
|
async function onSave(input: InputValues) {
|
||||||
try {
|
try {
|
||||||
if (!editingMarker) {
|
if (isNew) {
|
||||||
await sceneMarkerCreate({
|
await sceneMarkerCreate({
|
||||||
variables: {
|
variables: {
|
||||||
scene_id: sceneID,
|
scene_id: sceneID,
|
||||||
@@ -66,7 +72,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
|||||||
} else {
|
} else {
|
||||||
await sceneMarkerUpdate({
|
await sceneMarkerUpdate({
|
||||||
variables: {
|
variables: {
|
||||||
id: editingMarker.id,
|
id: marker.id,
|
||||||
scene_id: sceneID,
|
scene_id: sceneID,
|
||||||
...input,
|
...input,
|
||||||
},
|
},
|
||||||
@@ -80,10 +86,10 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onDelete() {
|
async function onDelete() {
|
||||||
if (!editingMarker) return;
|
if (isNew) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sceneMarkerDestroy({ variables: { id: editingMarker.id } });
|
await sceneMarkerDestroy({ variables: { id: marker.id } });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Toast.error(e);
|
Toast.error(e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -169,9 +175,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
|||||||
<div className="col d-flex">
|
<div className="col d-flex">
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
disabled={
|
disabled={(!isNew && !formik.dirty) || !isEqual(formik.errors, {})}
|
||||||
(editingMarker && !formik.dirty) || !isEqual(formik.errors, {})
|
|
||||||
}
|
|
||||||
onClick={() => formik.submitForm()}
|
onClick={() => formik.submitForm()}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="actions.save" />
|
<FormattedMessage id="actions.save" />
|
||||||
@@ -184,7 +188,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
|||||||
>
|
>
|
||||||
<FormattedMessage id="actions.cancel" />
|
<FormattedMessage id="actions.cancel" />
|
||||||
</Button>
|
</Button>
|
||||||
{editingMarker && (
|
{!isNew && (
|
||||||
<Button
|
<Button
|
||||||
variant="danger"
|
variant="danger"
|
||||||
className="ml-auto"
|
className="ml-auto"
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ interface ISceneMarkersPanelProps {
|
|||||||
onClickMarker: (marker: GQL.SceneMarkerDataFragment) => void;
|
onClickMarker: (marker: GQL.SceneMarkerDataFragment) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SceneMarkersPanel: React.FC<ISceneMarkersPanelProps> = (
|
export const SceneMarkersPanel: React.FC<ISceneMarkersPanelProps> = ({
|
||||||
props: ISceneMarkersPanelProps
|
sceneId,
|
||||||
) => {
|
isVisible,
|
||||||
|
onClickMarker,
|
||||||
|
}) => {
|
||||||
const { data, loading } = GQL.useFindSceneMarkerTagsQuery({
|
const { data, loading } = GQL.useFindSceneMarkerTagsQuery({
|
||||||
variables: {
|
variables: { id: sceneId },
|
||||||
id: props.sceneId,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
const [isEditorOpen, setIsEditorOpen] = useState<boolean>(false);
|
const [isEditorOpen, setIsEditorOpen] = useState<boolean>(false);
|
||||||
const [editingMarker, setEditingMarker] =
|
const [editingMarker, setEditingMarker] =
|
||||||
@@ -27,13 +27,13 @@ export const SceneMarkersPanel: React.FC<ISceneMarkersPanelProps> = (
|
|||||||
|
|
||||||
// set up hotkeys
|
// set up hotkeys
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.isVisible) {
|
if (!isVisible) return;
|
||||||
|
|
||||||
Mousetrap.bind("n", () => onOpenEditor());
|
Mousetrap.bind("n", () => onOpenEditor());
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Mousetrap.unbind("n");
|
Mousetrap.unbind("n");
|
||||||
};
|
};
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (loading) return null;
|
if (loading) return null;
|
||||||
@@ -43,10 +43,6 @@ export const SceneMarkersPanel: React.FC<ISceneMarkersPanelProps> = (
|
|||||||
setEditingMarker(marker ?? undefined);
|
setEditingMarker(marker ?? undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClickMarker(marker: GQL.SceneMarkerDataFragment) {
|
|
||||||
props.onClickMarker(marker);
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeEditor = () => {
|
const closeEditor = () => {
|
||||||
setEditingMarker(undefined);
|
setEditingMarker(undefined);
|
||||||
setIsEditorOpen(false);
|
setIsEditorOpen(false);
|
||||||
@@ -55,8 +51,8 @@ export const SceneMarkersPanel: React.FC<ISceneMarkersPanelProps> = (
|
|||||||
if (isEditorOpen)
|
if (isEditorOpen)
|
||||||
return (
|
return (
|
||||||
<SceneMarkerForm
|
<SceneMarkerForm
|
||||||
sceneID={props.sceneId}
|
sceneID={sceneId}
|
||||||
editingMarker={editingMarker}
|
marker={editingMarker}
|
||||||
onClose={closeEditor}
|
onClose={closeEditor}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user