no log: Fix issues on new modal system

This commit is contained in:
LASER-Yi 2021-08-17 02:28:18 +08:00
parent 82a687c8c8
commit c5fdea4a76
13 changed files with 138 additions and 105 deletions

View File

@ -37,7 +37,6 @@ export function useSerieBy(id: number) {
const serie = useEntityItemById(series, String(id));
const update = useCallback(() => {
console.log("try loading", id);
if (!isNaN(id)) {
action([id]);
}

View File

@ -16,8 +16,7 @@ import {
LanguageSelector,
Selector,
SimpleTable,
useCloseModal,
usePayload,
useModalInformation,
} from "../../components";
import { BuildKey } from "../../utilites";
import { Input, Message } from "../components";
@ -40,9 +39,8 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
) => {
const { update, ...modal } = props;
const profile = usePayload<Language.Profile>(modal.modalKey);
const closeModal = useCloseModal();
const { payload: profile, closeModal } =
useModalInformation<Language.Profile>(modal.modalKey);
const languages = useEnabledLanguagesContext();

View File

@ -11,9 +11,8 @@ import {
BaseModal,
BaseModalProps,
Selector,
useCloseModal,
useModalInformation,
useOnModalShow,
usePayload,
useShowModal,
} from "../../components";
import { BuildKey } from "../../utilites";
@ -44,26 +43,29 @@ const NotificationModal: FunctionComponent<ModalProps & BaseModalProps> = ({
"name"
);
const payload = usePayload<Settings.NotificationInfo>(modal.modalKey);
const { payload, closeModal } =
useModalInformation<Settings.NotificationInfo>(modal.modalKey);
const [current, setCurrent] = useState<Nullable<Settings.NotificationInfo>>(
payload ?? null
const [current, setCurrent] =
useState<Nullable<Settings.NotificationInfo>>(payload);
useOnModalShow<Settings.NotificationInfo>(
(p) => setCurrent(p),
modal.modalKey
);
useOnModalShow(() => setCurrent(payload ?? null), modal.modalKey);
const updateUrl = useCallback(
(s: string) => {
const updateUrl = useCallback((url: string) => {
setCurrent((current) => {
if (current) {
const newCurrent = { ...current };
newCurrent.url = s;
setCurrent(newCurrent);
return {
...current,
url,
};
} else {
return current;
}
},
[current]
);
const closeModal = useCloseModal();
});
}, []);
const canSave =
current !== null && current?.url !== null && current?.url.length !== 0;
@ -102,8 +104,7 @@ const NotificationModal: FunctionComponent<ModalProps & BaseModalProps> = ({
disabled={!canSave}
onClick={() => {
if (current) {
current.enabled = true;
update(current);
update({ ...current, enabled: true });
}
closeModal();
}}

View File

@ -11,9 +11,8 @@ import { SelectComponents } from "react-select/src/components";
import {
BaseModal,
Selector,
useCloseModal,
useModalInformation,
useOnModalShow,
usePayload,
useShowModal,
} from "../../components";
import { BuildKey, isReactText } from "../../utilites";
@ -78,18 +77,16 @@ export const ProviderView: FunctionComponent = () => {
};
export const ProviderModal: FunctionComponent = () => {
const payload = usePayload<ProviderInfo>(ModalKey);
const { payload, closeModal } = useModalInformation<ProviderInfo>(ModalKey);
const [staged, setChange] = useState<LooseObject>({});
const [info, setInfo] = useState<Nullable<ProviderInfo>>(payload ?? null);
const [info, setInfo] = useState<Nullable<ProviderInfo>>(payload);
useOnModalShow(() => setInfo(payload ?? null), ModalKey);
useOnModalShow<ProviderInfo>((p) => setInfo(p), ModalKey);
const providers = useLatest<string[]>(ProviderKey, isArray);
const closeModal = useCloseModal();
const updateGlobal = useMultiUpdate();
const deletePayload = useCallback(() => {

View File

@ -1,10 +1,10 @@
import React, { FunctionComponent, useMemo } from "react";
import { BaseModal, BaseModalProps, usePayload } from "../../components";
import { BaseModal, BaseModalProps, useModalPayload } from "../../components";
interface Props extends BaseModalProps {}
const SystemLogModal: FunctionComponent<Props> = ({ ...modal }) => {
const stack = usePayload<string>(modal.modalKey);
const stack = useModalPayload<string>(modal.modalKey);
const result = useMemo(
() =>
stack?.split("\\n").map((v, idx) => (

View File

@ -1,7 +1,6 @@
import React, { FunctionComponent, useCallback, useState } from "react";
import { Modal } from "react-bootstrap";
import { useIsModalShow } from ".";
import { useCloseModal } from "./hooks";
import { useModalInformation } from "./hooks";
export interface BaseModalProps {
modalKey: string;
@ -15,8 +14,7 @@ export const BaseModal: FunctionComponent<BaseModalProps> = (props) => {
const { size, modalKey, title, children, footer } = props;
const [needExit, setExit] = useState(false);
const show = useIsModalShow(modalKey);
const close = useCloseModal();
const { isShow, closeModal } = useModalInformation(modalKey);
const closeable = props.closeable !== false;
@ -25,15 +23,15 @@ export const BaseModal: FunctionComponent<BaseModalProps> = (props) => {
}, []);
const exit = useCallback(() => {
close();
closeModal(modalKey);
setExit(false);
}, [close]);
}, [closeModal, modalKey]);
return (
<Modal
centered
size={size}
show={show && !needExit}
show={isShow && !needExit}
onHide={hide}
onExited={exit}
backdrop={closeable ? undefined : "static"}

View File

@ -6,12 +6,12 @@ import { EpisodesApi, MoviesApi, useAsyncRequest } from "../../apis";
import { BlacklistButton } from "../../generic/blacklist";
import { AsyncOverlay } from "../async";
import BaseModal, { BaseModalProps } from "./BaseModal";
import { usePayload } from "./hooks";
import { useModalPayload } from "./hooks";
export const MovieHistoryModal: FunctionComponent<BaseModalProps> = (props) => {
const { ...modal } = props;
const movie = usePayload<Item.Movie>(modal.modalKey);
const movie = useModalPayload<Item.Movie>(modal.modalKey);
const [history, updateHistory] = useAsyncRequest(
MoviesApi.history.bind(MoviesApi),
@ -111,7 +111,7 @@ interface EpisodeHistoryProps {}
export const EpisodeHistoryModal: FunctionComponent<
BaseModalProps & EpisodeHistoryProps
> = (props) => {
const episode = usePayload<Item.Episode>(props.modalKey);
const episode = useModalPayload<Item.Episode>(props.modalKey);
const [history, updateHistory] = useAsyncRequest(
EpisodesApi.history.bind(EpisodesApi),

View File

@ -4,7 +4,7 @@ import { AsyncButton, Selector } from "../";
import { useLanguageProfiles } from "../../@redux/hooks";
import { GetItemId } from "../../utilites";
import BaseModal, { BaseModalProps } from "./BaseModal";
import { useCloseModal, usePayload } from "./hooks";
import { useModalInformation } from "./hooks";
interface Props {
submit: (form: FormType.ModifyItem) => Promise<void>;
@ -16,9 +16,9 @@ const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {
const profiles = useLanguageProfiles();
const item = usePayload<Item.Base>(modal.modalKey);
const closeModal = useCloseModal();
const { payload, closeModal } = useModalInformation<Item.Base>(
modal.modalKey
);
const profileOptions = useMemo<SelectorOption<number>[]>(
() =>
@ -37,8 +37,8 @@ const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {
noReset
onChange={setUpdating}
promise={() => {
if (item) {
const itemId = GetItemId(item);
if (payload) {
const itemId = GetItemId(payload);
return submit({
id: [itemId],
profileid: [id],
@ -49,20 +49,20 @@ const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {
}}
onSuccess={() => {
closeModal();
onSuccess && item && onSuccess(item);
onSuccess && payload && onSuccess(payload);
}}
>
Save
</AsyncButton>
),
[closeModal, id, item, onSuccess, submit]
[closeModal, id, payload, onSuccess, submit]
);
return (
<BaseModal
closeable={!updating}
footer={footer}
title={item?.title}
title={payload?.title}
{...modal}
>
<Container fluid>
@ -72,7 +72,9 @@ const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {
<Form.Control
type="text"
disabled
defaultValue={item?.audio_language.map((v) => v.name).join(", ")}
defaultValue={payload?.audio_language
.map((v) => v.name)
.join(", ")}
></Form.Control>
</Form.Group>
<Form.Group>
@ -80,7 +82,7 @@ const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {
<Selector
clearable
options={profileOptions}
defaultValue={item?.profileId}
defaultValue={payload?.profileId}
onChange={(v) => setId(v === undefined ? null : v)}
></Selector>
</Form.Group>

View File

@ -31,7 +31,7 @@ import {
LanguageText,
LoadingIndicator,
PageTable,
usePayload,
useModalPayload,
} from "..";
import { ProvidersApi } from "../../apis";
import { isMovie } from "../../utilites";
@ -58,7 +58,7 @@ export const ManualSearchModal: FunctionComponent<Props & BaseModalProps> = (
const [result, setResult] = useState<SearchResultType[]>([]);
const [searchState, setSearchState] = useState(SearchState.Ready);
const item = usePayload<SupportType>(modal.modalKey);
const item = useModalPayload<SupportType>(modal.modalKey);
const search = useCallback(async () => {
if (item) {

View File

@ -1,12 +1,6 @@
import React, { FunctionComponent, useEffect, useMemo, useState } from "react";
import { Container, Form } from "react-bootstrap";
import {
AsyncButton,
FileForm,
LanguageSelector,
useCloseModal,
usePayload,
} from "..";
import { AsyncButton, FileForm, LanguageSelector } from "..";
import {
useEnabledLanguages,
useLanguageBy,
@ -14,6 +8,7 @@ import {
} from "../../@redux/hooks";
import { MoviesApi } from "../../apis";
import BaseModal, { BaseModalProps } from "./BaseModal";
import { useModalInformation } from "./hooks";
interface MovieProps {}
const MovieUploadModal: FunctionComponent<MovieProps & BaseModalProps> = (
@ -23,15 +18,15 @@ const MovieUploadModal: FunctionComponent<MovieProps & BaseModalProps> = (
const availableLanguages = useEnabledLanguages();
const movie = usePayload<Item.Movie>(modal.modalKey);
const closeModal = useCloseModal();
const { payload, closeModal } = useModalInformation<Item.Movie>(
modal.modalKey
);
const [uploading, setUpload] = useState(false);
const [language, setLanguage] = useState<Nullable<Language.Info>>(null);
const profile = useProfileBy(movie?.profileId);
const profile = useProfileBy(payload?.profileId);
const defaultLanguage = useLanguageBy(profile?.items[0]?.language);
@ -50,8 +45,8 @@ const MovieUploadModal: FunctionComponent<MovieProps & BaseModalProps> = (
disabled={!canUpload}
onChange={setUpload}
promise={() => {
if (file && movie && language) {
return MoviesApi.uploadSubtitles(movie.radarrId, {
if (file && payload && language) {
return MoviesApi.uploadSubtitles(payload.radarrId, {
file: file,
forced,
hi: false,
@ -61,7 +56,7 @@ const MovieUploadModal: FunctionComponent<MovieProps & BaseModalProps> = (
return null;
}
}}
onSuccess={closeModal}
onSuccess={() => closeModal()}
>
Upload
</AsyncButton>
@ -69,7 +64,7 @@ const MovieUploadModal: FunctionComponent<MovieProps & BaseModalProps> = (
return (
<BaseModal
title={`Upload - ${movie?.title}`}
title={`Upload - ${payload?.title}`}
closeable={!uploading}
footer={footer}
{...modal}

View File

@ -21,13 +21,12 @@ import {
LanguageSelector,
MessageIcon,
SimpleTable,
useCloseModal,
usePayload,
} from "..";
import { useProfileBy, useProfileItemsToLanguages } from "../../@redux/hooks";
import { EpisodesApi, SubtitlesApi } from "../../apis";
import { Selector } from "../inputs";
import BaseModal, { BaseModalProps } from "./BaseModal";
import { useModalInformation } from "./hooks";
enum State {
Update,
@ -63,17 +62,17 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
episodes,
...modal
}) => {
const series = usePayload<Item.Series>(modal.modalKey);
const { payload, closeModal } = useModalInformation<Item.Series>(
modal.modalKey
);
const [uploading, setUpload] = useState(false);
const closeModal = useCloseModal();
const [pending, setPending] = useState<PendingSubtitle[]>([]);
const [processState, setProcessState] = useState<ProcessState>({});
const profile = useProfileBy(series?.profileId);
const profile = useProfileBy(payload?.profileId);
const avaliableLanguages = useProfileItemsToLanguages(profile);
@ -173,11 +172,11 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
);
const uploadSubtitles = useCallback(async () => {
if (series === null || language === null) {
if (payload === null || language === null) {
return;
}
const { sonarrSeriesId: seriesid } = series;
const { sonarrSeriesId: seriesid } = payload;
let uploadStates = pending.reduce<ProcessState>((prev, curr) => {
prev[curr.file.name] = { state: State.Update, infos: [] };
@ -223,7 +222,7 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
if (exception) {
throw new Error("Error when uploading subtitles");
}
}, [series, pending, language]);
}, [payload, pending, language]);
const canUpload = useMemo(
() =>

View File

@ -39,7 +39,8 @@ import {
LanguageText,
Selector,
SimpleTable,
usePayload,
useCloseModalIfCovered,
useModalPayload,
useShowModal,
} from "..";
import { useEnabledLanguages } from "../../@redux/hooks";
@ -48,7 +49,6 @@ import { isMovie, submodProcessColor } from "../../utilites";
import { log } from "../../utilites/logger";
import { useCustomSelection } from "../tables/plugins";
import BaseModal, { BaseModalProps } from "./BaseModal";
import { useCloseModalUntil } from "./hooks";
import { availableTranslation, colorOptions } from "./toolOptions";
type SupportType = Item.Episode | Item.Movie;
@ -331,18 +331,18 @@ const TranslateModal: FunctionComponent<BaseModalProps & ToolModalProps> = ({
interface STMProps {}
const STM: FunctionComponent<BaseModalProps & STMProps> = ({ ...props }) => {
const items = usePayload<SupportType[]>(props.modalKey);
const payload = useModalPayload<SupportType[]>(props.modalKey);
const [updating, setUpdate] = useState<boolean>(false);
const [processState, setProcessState] = useState<ProcessState>({});
const [selections, setSelections] = useState<TableColumnType[]>([]);
const closeUntil = useCloseModalUntil();
const closeModal = useCloseModalIfCovered();
const process = useCallback(
async (action: string, override?: Partial<FormType.ModifySubtitle>) => {
log("info", "executing action", action);
closeUntil(props.modalKey);
closeModal(props.modalKey);
setUpdate(true);
let states = selections.reduce<ProcessState>(
@ -374,7 +374,7 @@ const STM: FunctionComponent<BaseModalProps & STMProps> = ({ ...props }) => {
}
setUpdate(false);
},
[closeUntil, selections, props.modalKey]
[closeModal, selections, props.modalKey]
);
const showModal = useShowModal();
@ -431,7 +431,7 @@ const STM: FunctionComponent<BaseModalProps & STMProps> = ({ ...props }) => {
const data = useMemo<TableColumnType[]>(
() =>
items?.flatMap((item) => {
payload?.flatMap((item) => {
const [id, type] = getIdAndType(item);
return item.subtitles.flatMap((v) => {
if (v.path !== null) {
@ -449,7 +449,7 @@ const STM: FunctionComponent<BaseModalProps & STMProps> = ({ ...props }) => {
}
});
}) ?? [],
[items]
[payload]
);
const plugins = [useRowSelect, useCustomSelection];

View File

@ -3,6 +3,27 @@ import { useDidUpdate } from "rooks";
import { log } from "../../utilites/logger";
import { ModalContext } from "./provider";
interface ModalInformation<T> {
isShow: boolean;
payload: T | null;
closeModal: ReturnType<typeof useCloseModal>;
}
export function useModalInformation<T>(key: string): ModalInformation<T> {
const isShow = useIsModalShow(key);
const payload = useModalPayload<T>(key);
const closeModal = useCloseModal();
return useMemo(
() => ({
isShow,
payload,
closeModal,
}),
[isShow, payload, closeModal]
);
}
export function useShowModal() {
const {
control: { push },
@ -20,30 +41,46 @@ export function useShowModal() {
export function useCloseModal() {
const {
control: { pop },
control: { pop, peek },
} = useContext(ModalContext);
return pop;
return useCallback(
(key?: string) => {
const modal = peek();
if (key) {
if (modal?.key === key) {
pop();
}
} else {
pop();
}
},
[pop, peek]
);
}
export function useCloseModalUntil() {
export function useCloseModalIfCovered() {
const {
control: { pop, peek },
} = useContext(ModalContext);
return useCallback(
(key: string) => {
let modal = peek();
while (modal) {
if (modal.key === key) {
break;
} else {
modal = pop();
}
if (modal && modal.key !== key) {
pop();
}
},
[pop, peek]
);
}
export function useModalIsCovered(key: string) {
const { modals } = useContext(ModalContext);
return useMemo(() => {
const idx = modals.findIndex((v) => v.key === key);
return idx !== -1 && idx !== 0;
}, [modals, key]);
}
export function useIsModalShow(key: string) {
const {
control: { peek },
@ -52,23 +89,30 @@ export function useIsModalShow(key: string) {
return key === modal?.key;
}
export function useOnModalShow(callback: () => void, key: string) {
const isShow = useIsModalShow(key);
export function useOnModalShow<T>(
callback: (payload: T | null) => void,
key: string
) {
const {
modals,
control: { peek },
} = useContext(ModalContext);
useDidUpdate(() => {
if (isShow) {
callback();
const modal = peek();
if (modal && modal.key === key) {
callback(modal.payload ?? null);
}
}, [isShow]);
}, [modals.length, key]);
}
export function usePayload<T>(key: string): T | null {
export function useModalPayload<T>(key: string): T | null {
const {
control: { peek },
} = useContext(ModalContext);
return useMemo(() => {
const modal = peek();
if (modal && modal.key === key) {
return modal.payload as T;
return (modal.payload as T) ?? null;
} else {
return null;
}