Move more core logic to new folder

This commit is contained in:
schroda
2024-10-05 23:22:42 +02:00
parent babda1ea57
commit e7e1fbbc4e
71 changed files with 77 additions and 72 deletions

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { createRoot } from 'react-dom/client';
import { ThemeProvider } from '@mui/material/styles';
import { ConfirmDialog } from '@/modules/core/components/ConfirmDialog.tsx';
import { ControlledPromise } from '@/lib/ControlledPromise.ts';
import { getCurrentTheme } from '@/theme.tsx';
export const awaitConfirmation = async (
dialogProps: Omit<React.ComponentProps<typeof ConfirmDialog>, 'onCancel' | 'onConfirm'>,
) => {
const dialogContainer = document.createElement('div');
document.body.appendChild(dialogContainer);
const root = createRoot(dialogContainer);
const confirmationPromise = new ControlledPromise();
const handleConfirmation = (accepted: boolean) => {
if (accepted) {
confirmationPromise.resolve();
} else {
confirmationPromise.reject();
}
root.unmount();
document.body.removeChild(dialogContainer);
};
root.render(
<ThemeProvider theme={getCurrentTheme()}>
<ConfirmDialog
{...dialogProps}
onExtra={() => {
handleConfirmation(false);
dialogProps.onExtra?.();
}}
onCancel={() => handleConfirmation(false)}
onConfirm={() => handleConfirmation(true)}
/>
,
</ThemeProvider>,
);
return confirmationPromise.promise;
};

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { t } from 'i18next';
import { ISOLanguage, IsoLanguages } from '@/modules/core/IsoLanguages.ts';
export enum DefaultLanguage {
ALL = 'all',
OTHER = 'other',
LOCAL_SOURCE = 'localsourcelang',
}
function getISOLanguage(code: string): ISOLanguage | null {
if (IsoLanguages[code]) {
return IsoLanguages[code];
}
if (IsoLanguages[code.toLocaleLowerCase()]) {
return IsoLanguages[code.toLocaleLowerCase()];
}
const whereToCut = code.indexOf('-') !== -1 ? code.indexOf('-') : code.length;
const processedCode = code.toLocaleLowerCase().substring(0, whereToCut);
if (IsoLanguages[processedCode]) {
return IsoLanguages[processedCode];
}
return null;
}
export function getLanguage(code: string): { name: string; nativeName: string } {
const isoLanguage = getISOLanguage(code);
if (isoLanguage) {
return isoLanguage;
}
return {
name: t('global.language.label.language_with_code', { code }),
nativeName: t('global.language.label.language_with_code', { code }),
};
}
export function langCodeToName(code: string): string {
return getLanguage(code).nativeName;
}
function defaultNativeLang() {
return 'en'; // TODO: infer from the browser
}
export function extensionDefaultLangs() {
return [defaultNativeLang(), DefaultLanguage.ALL];
}
export function sourceDefualtLangs() {
return [defaultNativeLang(), DefaultLanguage.LOCAL_SOURCE];
}
export function sourceForcedDefaultLangs(): string[] {
return [DefaultLanguage.LOCAL_SOURCE];
}
export const langSortCmp = (a: string, b: string) => {
// puts english first for convience
const aLang = langCodeToName(a);
const bLang = langCodeToName(b);
if (a === 'en') return -1;
if (b === 'en') return 1;
return aLang.localeCompare(bLang);
};

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import useMediaQuery from '@mui/material/useMediaQuery';
import { Breakpoint } from '@mui/material/styles';
import { useCallback, useState } from 'react';
import { getCurrentTheme } from '@/theme.tsx';
import { ThemeMode } from '@/modules/theme/contexts/ThemeModeContext.tsx';
import { AppStorage } from '@/lib/storage/AppStorage.ts';
import { useResizeObserver } from '@/modules/core/hooks/useResizeObserver.tsx';
export class MediaQuery {
static useIsTouchDevice(): boolean {
return useMediaQuery('not (pointer: fine)');
}
static useIsBelowWidth(breakpoint: Breakpoint): boolean {
return useMediaQuery(getCurrentTheme().breakpoints.down(breakpoint));
}
static useIsMobileWidth(): boolean {
return this.useIsBelowWidth('sm');
}
static useGetScrollbarSize(type: 'height' | 'width'): number {
const [scrollbarSize, setScrollbarSize] = useState(0);
useResizeObserver(
document.documentElement,
useCallback(() => {
const height = window.innerHeight - document.documentElement.clientHeight;
const width = window.innerWidth - document.documentElement.clientWidth;
const size = type === 'height' ? height : width;
setScrollbarSize(size);
}, []),
);
return scrollbarSize;
}
static getSystemThemeMode(): Exclude<ThemeMode, 'system'> {
const prefersDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
return prefersDarkMode ? ThemeMode.DARK : ThemeMode.LIGHT;
}
static getThemeMode(): Exclude<ThemeMode, 'system'> {
const themeMode = AppStorage.local.getItemParsed<ThemeMode>('themeMode', ThemeMode.SYSTEM);
const isSystemMode = themeMode === ThemeMode.SYSTEM;
if (isSystemMode) {
return this.getSystemThemeMode();
}
return themeMode;
}
static listenToSystemThemeChange(onChange: (themeMode: Exclude<ThemeMode, 'system'>) => void): () => void {
const handleSystemThemeModeChange = (e: MediaQueryListEvent) => {
onChange(e.matches ? ThemeMode.DARK : ThemeMode.LIGHT);
};
const matchSystemThemeMode = window.matchMedia('(prefers-color-scheme: dark)');
matchSystemThemeMode.addEventListener('change', handleSystemThemeModeChange);
return () => matchSystemThemeMode.removeEventListener('change', handleSystemThemeModeChange);
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { enqueueSnackbar, OptionsObject, SnackbarKey } from 'notistack';
export function makeToast(message: string, severity?: OptionsObject['variant']): SnackbarKey;
export function makeToast(message: string, options?: OptionsObject): SnackbarKey;
export function makeToast(message: string, options: OptionsObject['variant'] | OptionsObject = 'default'): SnackbarKey {
if (typeof options === 'string') {
return enqueueSnackbar(message, { variant: options });
}
return enqueueSnackbar(message, options);
}