Skip to content

Commit

Permalink
Refactor modal system
Browse files Browse the repository at this point in the history
  • Loading branch information
LASER-Yi committed Mar 27, 2022
1 parent 87c5d0d commit 658237d
Show file tree
Hide file tree
Showing 32 changed files with 674 additions and 586 deletions.
1 change: 0 additions & 1 deletion frontend/src/components/index.tsx
Expand Up @@ -131,6 +131,5 @@ export * from "./buttons";
export * from "./header";
export * from "./inputs";
export * from "./LanguageSelector";
export * from "./modals";
export * from "./SearchBar";
export * from "./tables";
51 changes: 0 additions & 51 deletions frontend/src/components/modals/BaseModal.tsx

This file was deleted.

32 changes: 19 additions & 13 deletions frontend/src/components/modals/HistoryModal.tsx
Expand Up @@ -4,18 +4,17 @@ import {
useMovieAddBlacklist,
useMovieHistory,
} from "@/apis/hooks";
import { usePayload } from "@/modules/redux/hooks/modal";
import { useModal, usePayload, withModal } from "@/modules/modals";
import { FunctionComponent, useMemo } from "react";
import { Column } from "react-table";
import { HistoryIcon, PageTable, QueryOverlay, TextPopover } from "..";
import Language from "../bazarr/Language";
import { BlacklistButton } from "../inputs/blacklist";
import BaseModal, { BaseModalProps } from "./BaseModal";

export const MovieHistoryModal: FunctionComponent<BaseModalProps> = (props) => {
const { ...modal } = props;
const MovieHistoryView: FunctionComponent = () => {
const movie = usePayload<Item.Movie>();

const movie = usePayload<Item.Movie>(modal.modalKey);
const Modal = useModal({ size: "lg" });

const history = useMovieHistory(movie?.radarrId);

Expand Down Expand Up @@ -84,22 +83,24 @@ export const MovieHistoryModal: FunctionComponent<BaseModalProps> = (props) => {
);

return (
<BaseModal title={`History - ${movie?.title ?? ""}`} {...modal}>
<Modal title={`History - ${movie?.title ?? ""}`}>
<QueryOverlay result={history}>
<PageTable
emptyText="No History Found"
columns={columns}
data={data ?? []}
></PageTable>
</QueryOverlay>
</BaseModal>
</Modal>
);
};

export const EpisodeHistoryModal: FunctionComponent<BaseModalProps> = (
props
) => {
const episode = usePayload<Item.Episode>(props.modalKey);
export const MovieHistoryModal = withModal(MovieHistoryView, "movie-history");

const EpisodeHistoryView: FunctionComponent = () => {
const episode = usePayload<Item.Episode>();

const Modal = useModal({ size: "lg" });

const history = useEpisodeHistory(episode?.sonarrEpisodeId);

Expand Down Expand Up @@ -175,14 +176,19 @@ export const EpisodeHistoryModal: FunctionComponent<BaseModalProps> = (
);

return (
<BaseModal title={`History - ${episode?.title ?? ""}`} {...props}>
<Modal title={`History - ${episode?.title ?? ""}`}>
<QueryOverlay result={history}>
<PageTable
emptyText="No History Found"
columns={columns}
data={data ?? []}
></PageTable>
</QueryOverlay>
</BaseModal>
</Modal>
);
};

export const EpisodeHistoryModal = withModal(
EpisodeHistoryView,
"episode-history"
);
44 changes: 21 additions & 23 deletions frontend/src/components/modals/ItemEditorModal.tsx
@@ -1,26 +1,28 @@
import { useIsAnyActionRunning, useLanguageProfiles } from "@/apis/hooks";
import { useModalControl, usePayload } from "@/modules/redux/hooks/modal";
import {
useModal,
useModalControl,
usePayload,
withModal,
} from "@/modules/modals";
import { GetItemId } from "@/utilities";
import { FunctionComponent, useEffect, useMemo, useState } from "react";
import { FunctionComponent, useMemo, useState } from "react";
import { Container, Form } from "react-bootstrap";
import { UseMutationResult } from "react-query";
import { AsyncButton, Selector, SelectorOption } from "..";
import BaseModal, { BaseModalProps } from "./BaseModal";

interface Props {
mutation: UseMutationResult<void, unknown, FormType.ModifyItem, unknown>;
}

const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {
const { mutation, ...modal } = props;

const Editor: FunctionComponent<Props> = ({ mutation }) => {
const { data: profiles } = useLanguageProfiles();

const payload = usePayload<Item.Base>(modal.modalKey);
const { hide } = useModalControl();

const payload = usePayload<Item.Base>();
const { mutateAsync, isLoading } = mutation;

const { hide } = useModalControl();

const hasTask = useIsAnyActionRunning();

const profileOptions = useMemo<SelectorOption<number>[]>(
Expand All @@ -33,9 +35,12 @@ const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {

const [id, setId] = useState<Nullable<number>>(payload?.profileId ?? null);

useEffect(() => {
setId(payload?.profileId ?? null);
}, [payload]);
const Modal = useModal({
closeable: !isLoading,
onMounted: () => {
setId(payload?.profileId ?? null);
},
});

const footer = (
<AsyncButton
Expand All @@ -56,21 +61,14 @@ const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {
return null;
}
}}
onSuccess={() => {
hide();
}}
onSuccess={() => hide()}
>
Save
</AsyncButton>
);

return (
<BaseModal
closeable={!isLoading}
footer={footer}
title={payload?.title}
{...modal}
>
<Modal title={payload?.title ?? "Item Editor"} footer={footer}>
<Container fluid>
<Form>
<Form.Group>
Expand All @@ -95,8 +93,8 @@ const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {
</Form.Group>
</Form>
</Container>
</BaseModal>
</Modal>
);
};

export default Editor;
export default withModal(Editor, "edit");
69 changes: 34 additions & 35 deletions frontend/src/components/modals/ManualSearchModal.tsx
@@ -1,4 +1,4 @@
import { usePayload } from "@/modules/redux/hooks/modal";
import { useModal, usePayload, withModal } from "@/modules/modals";
import { createAndDispatchTask } from "@/modules/task/utilities";
import { GetItemId, isMovie } from "@/utilities";
import {
Expand All @@ -10,13 +10,7 @@ import {
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import clsx from "clsx";
import {
FunctionComponent,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import { FunctionComponent, useCallback, useMemo, useState } from "react";
import {
Badge,
Button,
Expand All @@ -29,7 +23,7 @@ import {
} from "react-bootstrap";
import { UseQueryResult } from "react-query";
import { Column } from "react-table";
import { BaseModal, BaseModalProps, LoadingIndicator, PageTable } from "..";
import { LoadingIndicator, PageTable } from "..";
import Language from "../bazarr/Language";

type SupportType = Item.Movie | Item.Episode;
Expand All @@ -41,24 +35,15 @@ interface Props<T extends SupportType> {
) => UseQueryResult<SearchResultType[] | undefined, unknown>;
}

export function ManualSearchModal<T extends SupportType>(
props: Props<T> & BaseModalProps
) {
const { download, query: useSearch, ...modal } = props;
function ManualSearchView<T extends SupportType>(props: Props<T>) {
const { download, query: useSearch } = props;

const item = usePayload<T>(modal.modalKey);
const item = usePayload<T>();

const itemId = useMemo(() => GetItemId(item ?? {}), [item]);

const [id, setId] = useState<number | undefined>(undefined);

// Cleanup the ID when user switches episode / movie
useEffect(() => {
if (itemId !== undefined && itemId !== id) {
setId(undefined);
}
}, [id, itemId]);

const results = useSearch(id);

const isStale = results.data === undefined;
Expand Down Expand Up @@ -225,12 +210,6 @@ export function ManualSearchModal<T extends SupportType>(
}
};

const footer = (
<Button variant="light" hidden={isStale} onClick={search}>
Search Again
</Button>
);

const title = useMemo(() => {
let title = "Unknown";

Expand All @@ -246,19 +225,39 @@ export function ManualSearchModal<T extends SupportType>(
return `Search - ${title}`;
}, [item]);

const Modal = useModal({
size: "xl",
closeable: results.isFetching === false,
onMounted: () => {
// Cleanup the ID when user switches episode / movie
if (itemId !== id) {
setId(undefined);
}
},
});

const footer = (
<Button variant="light" hidden={isStale} onClick={search}>
Search Again
</Button>
);

return (
<BaseModal
closeable={results.isFetching === false}
size="xl"
title={title}
footer={footer}
{...modal}
>
<Modal title={title} footer={footer}>
{content()}
</BaseModal>
</Modal>
);
}

export const MovieSearchModal = withModal<Props<Item.Movie>>(
ManualSearchView,
"movie-manual-search"
);
export const EpisodeSearchModal = withModal<Props<Item.Episode>>(
ManualSearchView,
"episode-manual-search"
);

const StateIcon: FunctionComponent<{ matches: string[]; dont: string[] }> = ({
matches,
dont,
Expand Down
18 changes: 7 additions & 11 deletions frontend/src/components/modals/MovieUploadModal.tsx
@@ -1,21 +1,18 @@
import { useMovieSubtitleModification } from "@/apis/hooks";
import { usePayload } from "@/modules/redux/hooks/modal";
import { usePayload, withModal } from "@/modules/modals";
import { createTask, dispatchTask } from "@/modules/task/utilities";
import {
useLanguageProfileBy,
useProfileItemsToLanguages,
} from "@/utilities/languages";
import { FunctionComponent, useCallback } from "react";
import { BaseModalProps } from "./BaseModal";
import SubtitleUploadModal, {
import SubtitleUploader, {
PendingSubtitle,
Validator,
} from "./SubtitleUploadModal";

const MovieUploadModal: FunctionComponent<BaseModalProps> = (props) => {
const modal = props;

const payload = usePayload<Item.Movie>(modal.modalKey);
const MovieUploadModal: FunctionComponent = () => {
const payload = usePayload<Item.Movie>();

const profile = useLanguageProfileBy(payload?.profileId);

Expand Down Expand Up @@ -87,17 +84,16 @@ const MovieUploadModal: FunctionComponent<BaseModalProps> = (props) => {
);

return (
<SubtitleUploadModal
<SubtitleUploader
hideAllLanguages
initial={{ forced: false }}
availableLanguages={availableLanguages}
columns={[]}
upload={upload}
update={update}
validate={validate}
{...modal}
></SubtitleUploadModal>
></SubtitleUploader>
);
};

export default MovieUploadModal;
export default withModal(MovieUploadModal, "movie-upload");

0 comments on commit 658237d

Please sign in to comment.