Split out entity creation from view pages (#1884)

* Split performerCreate page into separate page
* Split studioCreate into a separate page
* Remove Partial types from performer/studio
* Split tagCreate into a separate page
* Split movieCreate into a separate page
* Split out galleryCreate into its own page
* Add loader to scene page
* Fix performer name fallback
* Fix movie layout shift
* Fix prompt comment and switch studio prompt to localized string
This commit is contained in:
InfiniteTF
2021-10-26 00:43:45 +02:00
committed by GitHub
parent c8182bdb4c
commit 1fffc0519a
41 changed files with 894 additions and 683 deletions

View File

@@ -38,12 +38,12 @@ import { SceneGenerateDialog } from "../SceneGenerateDialog";
import { SceneVideoFilterPanel } from "./SceneVideoFilterPanel";
import { OrganizedButton } from "./OrganizedButton";
interface ISceneParams {
id?: string;
interface IProps {
scene: GQL.SceneDataFragment;
refetch: () => void;
}
export const Scene: React.FC = () => {
const { id = "new" } = useParams<ISceneParams>();
const ScenePage: React.FC<IProps> = ({ scene, refetch }) => {
const location = useLocation();
const history = useHistory();
const Toast = useToast();
@@ -53,18 +53,16 @@ export const Scene: React.FC = () => {
const [timestamp, setTimestamp] = useState<number>(getInitialTimestamp());
const [collapsed, setCollapsed] = useState(false);
const { data, error, loading, refetch } = useFindScene(id);
const scene = data?.findScene;
const {
data: sceneStreams,
error: streamableError,
loading: streamableLoading,
} = useSceneStreams(id);
} = useSceneStreams(scene.id);
const [oLoading, setOLoading] = useState(false);
const [incrementO] = useSceneIncrementO(scene?.id ?? "0");
const [decrementO] = useSceneDecrementO(scene?.id ?? "0");
const [resetO] = useSceneResetO(scene?.id ?? "0");
const [incrementO] = useSceneIncrementO(scene.id);
const [decrementO] = useSceneDecrementO(scene.id);
const [resetO] = useSceneResetO(scene.id);
const [organizedLoading, setOrganizedLoading] = useState(false);
@@ -85,7 +83,7 @@ export const Scene: React.FC = () => {
const queryParams = queryString.parse(location.search);
const autoplay = queryParams?.autoplay === "true";
const currentQueueIndex = queueScenes.findIndex((s) => s.id === id);
const currentQueueIndex = queueScenes.findIndex((s) => s.id === scene.id);
async function getQueueFilterScenes(filter: ListFilterModel) {
const query = await queryFindScenes(filter);
@@ -113,7 +111,7 @@ export const Scene: React.FC = () => {
useEffect(() => {
setRerenderPlayer(true);
}, [id]);
}, [scene.id]);
useEffect(() => {
setSceneQueue(SceneQueue.fromQueryParameters(location.search));
@@ -142,8 +140,8 @@ export const Scene: React.FC = () => {
await updateScene({
variables: {
input: {
id: scene?.id ?? "",
organized: !scene?.organized,
id: scene.id,
organized: !scene.organized,
},
},
});
@@ -192,10 +190,6 @@ export const Scene: React.FC = () => {
}
async function onRescan() {
if (!scene) {
return;
}
await mutateMetadataScan({
paths: [scene.path],
});
@@ -214,10 +208,6 @@ export const Scene: React.FC = () => {
}
async function onGenerateScreenshot(at?: number) {
if (!scene) {
return;
}
await generateScreenshot({
variables: {
id: scene.id,
@@ -323,7 +313,7 @@ export const Scene: React.FC = () => {
}
function maybeRenderDeleteDialog() {
if (isDeleteAlertOpen && scene) {
if (isDeleteAlertOpen) {
return (
<DeleteScenesDialog selected={[scene]} onClose={onDeleteDialogClosed} />
);
@@ -331,7 +321,7 @@ export const Scene: React.FC = () => {
}
function maybeRenderSceneGenerateDialog() {
if (isGenerateDialogOpen && scene) {
if (isGenerateDialogOpen) {
return (
<SceneGenerateDialog
selectedIds={[scene.id]}
@@ -343,215 +333,204 @@ export const Scene: React.FC = () => {
}
}
function renderOperations() {
return (
<Dropdown>
<Dropdown.Toggle
variant="secondary"
id="operation-menu"
className="minimal"
title="Operations"
>
<Icon icon="ellipsis-v" />
</Dropdown.Toggle>
<Dropdown.Menu className="bg-secondary text-white">
<Dropdown.Item
key="rescan"
className="bg-secondary text-white"
onClick={() => onRescan()}
>
<FormattedMessage id="actions.rescan" />
</Dropdown.Item>
<Dropdown.Item
key="generate"
className="bg-secondary text-white"
onClick={() => setIsGenerateDialogOpen(true)}
>
<FormattedMessage id="actions.generate" />
</Dropdown.Item>
<Dropdown.Item
key="generate-screenshot"
className="bg-secondary text-white"
onClick={() =>
onGenerateScreenshot(JWUtils.getPlayer().getPosition())
}
>
<FormattedMessage id="actions.generate_thumb_from_current" />
</Dropdown.Item>
<Dropdown.Item
key="generate-default"
className="bg-secondary text-white"
onClick={() => onGenerateScreenshot()}
>
<FormattedMessage id="actions.generate_thumb_default" />
</Dropdown.Item>
<Dropdown.Item
key="delete-scene"
className="bg-secondary text-white"
onClick={() => setIsDeleteAlertOpen(true)}
>
<FormattedMessage
id="actions.delete_entity"
values={{ entityType: intl.formatMessage({ id: "scene" }) }}
/>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
}
function renderTabs() {
if (!scene) {
return;
}
return (
<Tab.Container
activeKey={activeTabKey}
onSelect={(k) => k && setActiveTabKey(k)}
const renderOperations = () => (
<Dropdown>
<Dropdown.Toggle
variant="secondary"
id="operation-menu"
className="minimal"
title="Operations"
>
<div>
<Nav variant="tabs" className="mr-auto">
<Nav.Item>
<Nav.Link eventKey="scene-details-panel">
<FormattedMessage id="scenes" />
</Nav.Link>
</Nav.Item>
{(queueScenes ?? []).length > 0 ? (
<Nav.Item>
<Nav.Link eventKey="scene-queue-panel">
<FormattedMessage id="queue" />
</Nav.Link>
</Nav.Item>
) : (
""
)}
<Nav.Item>
<Nav.Link eventKey="scene-markers-panel">
<FormattedMessage id="markers" />
</Nav.Link>
</Nav.Item>
{scene.movies.length > 0 ? (
<Nav.Item>
<Nav.Link eventKey="scene-movie-panel">
<FormattedMessage
id="countables.movies"
values={{ count: scene.movies.length }}
/>
</Nav.Link>
</Nav.Item>
) : (
""
)}
{scene.galleries.length >= 1 ? (
<Nav.Item>
<Nav.Link eventKey="scene-galleries-panel">
<FormattedMessage
id="countables.galleries"
values={{ count: scene.galleries.length }}
/>
</Nav.Link>
</Nav.Item>
) : undefined}
<Nav.Item>
<Nav.Link eventKey="scene-video-filter-panel">
<FormattedMessage id="effect_filters.name" />
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="scene-file-info-panel">
<FormattedMessage id="file_info" />
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="scene-edit-panel">
<FormattedMessage id="actions.edit" />
</Nav.Link>
</Nav.Item>
<ButtonGroup className="ml-auto">
<Nav.Item className="ml-auto">
<ExternalPlayerButton scene={scene} />
</Nav.Item>
<Nav.Item className="ml-auto">
<OCounterButton
loading={oLoading}
value={scene.o_counter || 0}
onIncrement={onIncrementClick}
onDecrement={onDecrementClick}
onReset={onResetClick}
/>
</Nav.Item>
<Nav.Item>
<OrganizedButton
loading={organizedLoading}
organized={scene.organized}
onClick={onOrganizedClick}
/>
</Nav.Item>
<Nav.Item>{renderOperations()}</Nav.Item>
</ButtonGroup>
</Nav>
</div>
<Icon icon="ellipsis-v" />
</Dropdown.Toggle>
<Dropdown.Menu className="bg-secondary text-white">
<Dropdown.Item
key="rescan"
className="bg-secondary text-white"
onClick={() => onRescan()}
>
<FormattedMessage id="actions.rescan" />
</Dropdown.Item>
<Dropdown.Item
key="generate"
className="bg-secondary text-white"
onClick={() => setIsGenerateDialogOpen(true)}
>
<FormattedMessage id="actions.generate" />
</Dropdown.Item>
<Dropdown.Item
key="generate-screenshot"
className="bg-secondary text-white"
onClick={() =>
onGenerateScreenshot(JWUtils.getPlayer().getPosition())
}
>
<FormattedMessage id="actions.generate_thumb_from_current" />
</Dropdown.Item>
<Dropdown.Item
key="generate-default"
className="bg-secondary text-white"
onClick={() => onGenerateScreenshot()}
>
<FormattedMessage id="actions.generate_thumb_default" />
</Dropdown.Item>
<Dropdown.Item
key="delete-scene"
className="bg-secondary text-white"
onClick={() => setIsDeleteAlertOpen(true)}
>
<FormattedMessage
id="actions.delete_entity"
values={{ entityType: intl.formatMessage({ id: "scene" }) }}
/>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
<Tab.Content>
<Tab.Pane eventKey="scene-details-panel">
<SceneDetailPanel scene={scene} />
</Tab.Pane>
<Tab.Pane eventKey="scene-queue-panel">
<QueueViewer
scenes={queueScenes}
currentID={scene.id}
onSceneClicked={(sceneID) => playScene(sceneID)}
onNext={onQueueNext}
onPrevious={onQueuePrevious}
onRandom={onQueueRandom}
start={queueStart}
hasMoreScenes={queueHasMoreScenes()}
onLessScenes={() => onQueueLessScenes()}
onMoreScenes={() => onQueueMoreScenes()}
/>
</Tab.Pane>
<Tab.Pane eventKey="scene-markers-panel">
<SceneMarkersPanel
scene={scene}
onClickMarker={onClickMarker}
isVisible={activeTabKey === "scene-markers-panel"}
/>
</Tab.Pane>
<Tab.Pane eventKey="scene-movie-panel">
<SceneMoviePanel scene={scene} />
</Tab.Pane>
{scene.galleries.length === 1 && (
<Tab.Pane eventKey="scene-galleries-panel">
<GalleryViewer galleryId={scene.galleries[0].id} />
</Tab.Pane>
const renderTabs = () => (
<Tab.Container
activeKey={activeTabKey}
onSelect={(k) => k && setActiveTabKey(k)}
>
<div>
<Nav variant="tabs" className="mr-auto">
<Nav.Item>
<Nav.Link eventKey="scene-details-panel">
<FormattedMessage id="scenes" />
</Nav.Link>
</Nav.Item>
{(queueScenes ?? []).length > 0 ? (
<Nav.Item>
<Nav.Link eventKey="scene-queue-panel">
<FormattedMessage id="queue" />
</Nav.Link>
</Nav.Item>
) : (
""
)}
{scene.galleries.length > 1 && (
<Tab.Pane eventKey="scene-galleries-panel">
<SceneGalleriesPanel galleries={scene.galleries} />
</Tab.Pane>
<Nav.Item>
<Nav.Link eventKey="scene-markers-panel">
<FormattedMessage id="markers" />
</Nav.Link>
</Nav.Item>
{scene.movies.length > 0 ? (
<Nav.Item>
<Nav.Link eventKey="scene-movie-panel">
<FormattedMessage
id="countables.movies"
values={{ count: scene.movies.length }}
/>
</Nav.Link>
</Nav.Item>
) : (
""
)}
<Tab.Pane eventKey="scene-video-filter-panel">
<SceneVideoFilterPanel scene={scene} />
{scene.galleries.length >= 1 ? (
<Nav.Item>
<Nav.Link eventKey="scene-galleries-panel">
<FormattedMessage
id="countables.galleries"
values={{ count: scene.galleries.length }}
/>
</Nav.Link>
</Nav.Item>
) : undefined}
<Nav.Item>
<Nav.Link eventKey="scene-video-filter-panel">
<FormattedMessage id="effect_filters.name" />
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="scene-file-info-panel">
<FormattedMessage id="file_info" />
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="scene-edit-panel">
<FormattedMessage id="actions.edit" />
</Nav.Link>
</Nav.Item>
<ButtonGroup className="ml-auto">
<Nav.Item className="ml-auto">
<ExternalPlayerButton scene={scene} />
</Nav.Item>
<Nav.Item className="ml-auto">
<OCounterButton
loading={oLoading}
value={scene.o_counter || 0}
onIncrement={onIncrementClick}
onDecrement={onDecrementClick}
onReset={onResetClick}
/>
</Nav.Item>
<Nav.Item>
<OrganizedButton
loading={organizedLoading}
organized={scene.organized}
onClick={onOrganizedClick}
/>
</Nav.Item>
<Nav.Item>{renderOperations()}</Nav.Item>
</ButtonGroup>
</Nav>
</div>
<Tab.Content>
<Tab.Pane eventKey="scene-details-panel">
<SceneDetailPanel scene={scene} />
</Tab.Pane>
<Tab.Pane eventKey="scene-queue-panel">
<QueueViewer
scenes={queueScenes}
currentID={scene.id}
onSceneClicked={(sceneID) => playScene(sceneID)}
onNext={onQueueNext}
onPrevious={onQueuePrevious}
onRandom={onQueueRandom}
start={queueStart}
hasMoreScenes={queueHasMoreScenes()}
onLessScenes={() => onQueueLessScenes()}
onMoreScenes={() => onQueueMoreScenes()}
/>
</Tab.Pane>
<Tab.Pane eventKey="scene-markers-panel">
<SceneMarkersPanel
scene={scene}
onClickMarker={onClickMarker}
isVisible={activeTabKey === "scene-markers-panel"}
/>
</Tab.Pane>
<Tab.Pane eventKey="scene-movie-panel">
<SceneMoviePanel scene={scene} />
</Tab.Pane>
{scene.galleries.length === 1 && (
<Tab.Pane eventKey="scene-galleries-panel">
<GalleryViewer galleryId={scene.galleries[0].id} />
</Tab.Pane>
<Tab.Pane
className="file-info-panel"
eventKey="scene-file-info-panel"
>
<SceneFileInfoPanel scene={scene} />
)}
{scene.galleries.length > 1 && (
<Tab.Pane eventKey="scene-galleries-panel">
<SceneGalleriesPanel galleries={scene.galleries} />
</Tab.Pane>
<Tab.Pane eventKey="scene-edit-panel">
<SceneEditPanel
isVisible={activeTabKey === "scene-edit-panel"}
scene={scene}
onDelete={() => setIsDeleteAlertOpen(true)}
onUpdate={() => refetch()}
/>
</Tab.Pane>
</Tab.Content>
</Tab.Container>
);
}
)}
<Tab.Pane eventKey="scene-video-filter-panel">
<SceneVideoFilterPanel scene={scene} />
</Tab.Pane>
<Tab.Pane className="file-info-panel" eventKey="scene-file-info-panel">
<SceneFileInfoPanel scene={scene} />
</Tab.Pane>
<Tab.Pane eventKey="scene-edit-panel">
<SceneEditPanel
isVisible={activeTabKey === "scene-edit-panel"}
scene={scene}
onDelete={() => setIsDeleteAlertOpen(true)}
onUpdate={() => refetch()}
/>
</Tab.Pane>
</Tab.Content>
</Tab.Container>
);
// set up hotkeys
useEffect(() => {
@@ -582,10 +561,8 @@ export const Scene: React.FC = () => {
return collapsed ? ">" : "<";
}
if (loading || streamableLoading) return <LoadingIndicator />;
if (error) return <ErrorMessage error={error.message} />;
if (streamableLoading) return <LoadingIndicator />;
if (streamableError) return <ErrorMessage error={streamableError.message} />;
if (!scene) return <ErrorMessage error={`No scene found with id ${id}.`} />;
return (
<div className="row">
@@ -638,3 +615,17 @@ export const Scene: React.FC = () => {
</div>
);
};
const SceneLoader: React.FC = () => {
const { id } = useParams<{ id?: string }>();
const { data, loading, error, refetch } = useFindScene(id ?? "");
if (loading) return <LoadingIndicator />;
if (error) return <ErrorMessage error={error.message} />;
if (!data?.findScene)
return <ErrorMessage error={`No scene found with id ${id}.`} />;
return <ScenePage scene={data.findScene} refetch={refetch} />;
};
export default SceneLoader;