1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-04-26 22:56:23 -04:00

Convert Page components to TypeScript

This commit is contained in:
Mark McDowall
2024-12-16 06:51:45 -08:00
parent 4e65669c48
commit f35a27449d
69 changed files with 2423 additions and 2755 deletions
+123
View File
@@ -0,0 +1,123 @@
import { useEffect, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import { fetchTranslations } from 'Store/Actions/appActions';
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
import { fetchSeries } from 'Store/Actions/seriesActions';
import {
fetchImportLists,
fetchIndexerFlags,
fetchLanguages,
fetchQualityProfiles,
fetchUISettings,
} from 'Store/Actions/settingsActions';
import { fetchStatus } from 'Store/Actions/systemActions';
import { fetchTags } from 'Store/Actions/tagActions';
const createErrorsSelector = () =>
createSelector(
(state: AppState) => state.series.error,
(state: AppState) => state.customFilters.error,
(state: AppState) => state.tags.error,
(state: AppState) => state.settings.ui.error,
(state: AppState) => state.settings.qualityProfiles.error,
(state: AppState) => state.settings.languages.error,
(state: AppState) => state.settings.importLists.error,
(state: AppState) => state.settings.indexerFlags.error,
(state: AppState) => state.system.status.error,
(state: AppState) => state.app.translations.error,
(
seriesError,
customFiltersError,
tagsError,
uiSettingsError,
qualityProfilesError,
languagesError,
importListsError,
indexerFlagsError,
systemStatusError,
translationsError
) => {
const hasError = !!(
seriesError ||
customFiltersError ||
tagsError ||
uiSettingsError ||
qualityProfilesError ||
languagesError ||
importListsError ||
indexerFlagsError ||
systemStatusError ||
translationsError
);
return {
hasError,
errors: {
seriesError,
customFiltersError,
tagsError,
uiSettingsError,
qualityProfilesError,
languagesError,
importListsError,
indexerFlagsError,
systemStatusError,
translationsError,
},
};
}
);
const useAppPage = () => {
const dispatch = useDispatch();
const isPopulated = useSelector(
(state: AppState) =>
state.series.isPopulated &&
state.customFilters.isPopulated &&
state.tags.isPopulated &&
state.settings.ui.isPopulated &&
state.settings.qualityProfiles.isPopulated &&
state.settings.languages.isPopulated &&
state.settings.importLists.isPopulated &&
state.settings.indexerFlags.isPopulated &&
state.system.status.isPopulated &&
state.app.translations.isPopulated
);
const { hasError, errors } = useSelector(createErrorsSelector());
const isLocalStorageSupported = useMemo(() => {
const key = 'sonarrTest';
try {
localStorage.setItem(key, key);
localStorage.removeItem(key);
return true;
} catch {
return false;
}
}, []);
useEffect(() => {
dispatch(fetchSeries());
dispatch(fetchCustomFilters());
dispatch(fetchTags());
dispatch(fetchQualityProfiles());
dispatch(fetchLanguages());
dispatch(fetchImportLists());
dispatch(fetchIndexerFlags());
dispatch(fetchUISettings());
dispatch(fetchStatus());
dispatch(fetchTranslations());
}, [dispatch]);
return useMemo(() => {
return { errors, hasError, isLocalStorageSupported, isPopulated };
}, [errors, hasError, isLocalStorageSupported, isPopulated]);
};
export default useAppPage;
@@ -0,0 +1,122 @@
import Mousetrap, { MousetrapInstance } from 'mousetrap';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import translate from 'Utilities/String/translate';
export interface Shortcut {
key: string;
name: string;
}
interface BindingOptions {
isGlobal?: boolean;
}
export const shortcuts = {
openKeyboardShortcutsModal: {
key: '?',
get name() {
return translate('KeyboardShortcutsOpenModal');
},
},
closeModal: {
key: 'Esc',
get name() {
return translate('KeyboardShortcutsCloseModal');
},
},
acceptConfirmModal: {
key: 'Enter',
get name() {
return translate('KeyboardShortcutsConfirmModal');
},
},
focusSeriesSearchInput: {
key: 's',
get name() {
return translate('KeyboardShortcutsFocusSearchBox');
},
},
saveSettings: {
key: 'mod+s',
get name() {
return translate('KeyboardShortcutsSaveSettings');
},
},
};
function useKeyboardShortcuts() {
const bindings = useRef<Record<string, BindingOptions>>({});
const mouseTrap = useRef<MousetrapInstance | null>();
const handleStop = useCallback(
(_e: Mousetrap.ExtendedKeyboardEvent, element: Element, combo: string) => {
const binding = bindings.current[combo];
if (!binding || binding.isGlobal) {
return false;
}
return (
element.tagName === 'INPUT' ||
element.tagName === 'SELECT' ||
element.tagName === 'TEXTAREA' ||
('contentEditable' in element && element.contentEditable === 'true')
);
},
[]
);
const bindShortcut = useCallback(
(
shortcutKey: keyof typeof shortcuts,
callback: (e: Mousetrap.ExtendedKeyboardEvent, combo: string) => void,
options: BindingOptions = {}
) => {
const shortcut = shortcuts[shortcutKey];
mouseTrap.current?.bind(shortcut.key, callback);
bindings.current[shortcut.key] = options;
},
[]
);
const unbindShortcut = useCallback((shortcutKey: keyof typeof shortcuts) => {
const shortcut = shortcuts[shortcutKey];
delete bindings.current[shortcut.key];
mouseTrap.current?.unbind(shortcut.key);
}, []);
useEffect(() => {
mouseTrap.current = new Mousetrap();
mouseTrap.current.stopCallback = handleStop;
const localMouseTrap = mouseTrap.current;
return () => {
const keys = Object.keys(bindings.current);
if (!keys.length) {
return;
}
keys.forEach((binding) => {
localMouseTrap.unbind(binding);
});
bindings.current = {};
mouseTrap.current = null;
};
}, [handleStop]);
return useMemo(
() => ({ bindShortcut, unbindShortcut }),
[bindShortcut, unbindShortcut]
);
}
export default useKeyboardShortcuts;