diff options
author | crupest <crupest@outlook.com> | 2023-09-21 23:49:12 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2023-09-21 23:49:12 +0800 |
commit | d477c7270c90b190ed82b13f48f39a05d83503d2 (patch) | |
tree | 572f74b2adc75247b9d1c9a8965e3de2559d4160 /FrontEnd/src | |
parent | a8a8385cd959e4d9d57b8a35381d2851049c07ff (diff) | |
download | timeline-d477c7270c90b190ed82b13f48f39a05d83503d2.tar.gz timeline-d477c7270c90b190ed82b13f48f39a05d83503d2.tar.bz2 timeline-d477c7270c90b190ed82b13f48f39a05d83503d2.zip |
Fix #1394.
Diffstat (limited to 'FrontEnd/src')
37 files changed, 187 insertions, 198 deletions
diff --git a/FrontEnd/src/common.ts b/FrontEnd/src/common.ts deleted file mode 100644 index 1ca796c3..00000000 --- a/FrontEnd/src/common.ts +++ /dev/null @@ -1,9 +0,0 @@ -// This error is thrown when ui goes wrong with bad logic. -// Such as a variable should not be null, but it does. -// This error should never occur. If it does, it indicates there is some logic bug in codes. -export class UiLogicError extends Error {} - -export type { I18nText } from "./i18n"; -export type { I18nText as Text } from "./i18n"; -export { c, convertI18nText } from "./i18n"; -export { default as useC } from "./utilities/hooks/use-c"; diff --git a/FrontEnd/src/components/AppBar.tsx b/FrontEnd/src/components/AppBar.tsx index d40c8105..6ea8bdac 100644 --- a/FrontEnd/src/components/AppBar.tsx +++ b/FrontEnd/src/components/AppBar.tsx @@ -37,7 +37,8 @@ function AppBarNavLink({ className={({ isActive }) => classnames(className, isActive && "active")} onClick={onClick} > - {children != null ? children : c(label)} + {children} + {label && c(label)} </NavLink> ); } diff --git a/FrontEnd/src/components/SearchInput.tsx b/FrontEnd/src/components/SearchInput.tsx index b1de6227..04341245 100644 --- a/FrontEnd/src/components/SearchInput.tsx +++ b/FrontEnd/src/components/SearchInput.tsx @@ -1,6 +1,6 @@ import classNames from "classnames"; -import { useC, Text } from "./common"; +import { useC, I18nText } from "./common"; import { LoadingButton } from "./button"; import "./SearchInput.css"; @@ -11,7 +11,7 @@ interface SearchInputProps { onButtonClick: () => void; loading?: boolean; className?: string; - buttonText?: Text; + buttonText?: I18nText; } export default function SearchInput({ diff --git a/FrontEnd/src/components/alert/AlertHost.tsx b/FrontEnd/src/components/alert/AlertHost.tsx index 59f8f27c..8dca42d5 100644 --- a/FrontEnd/src/components/alert/AlertHost.tsx +++ b/FrontEnd/src/components/alert/AlertHost.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import classNames from "classnames"; -import { ThemeColor, useC, Text } from "../common"; +import { ThemeColor, useC, I18nText } from "../common"; import IconButton from "../button/IconButton"; import { alertService, AlertInfoWithId } from "./AlertService"; @@ -10,7 +10,7 @@ import "./alert.css"; interface AutoCloseAlertProps { color: ThemeColor; - message: Text; + message: I18nText; onDismiss?: () => void; onIn?: () => void; onOut?: () => void; diff --git a/FrontEnd/src/components/alert/AlertService.ts b/FrontEnd/src/components/alert/AlertService.ts index b9cda752..8e98cc4d 100644 --- a/FrontEnd/src/components/alert/AlertService.ts +++ b/FrontEnd/src/components/alert/AlertService.ts @@ -1,10 +1,10 @@ -import { ThemeColor, Text } from "../common"; +import { ThemeColor, I18nText } from "../common"; const defaultDismissTime = 5000; export interface AlertInfo { color?: ThemeColor; - message: Text; + message: I18nText; dismissTime?: number | "never"; } diff --git a/FrontEnd/src/components/button/Button.tsx b/FrontEnd/src/components/button/Button.tsx index 30ea8c11..bdb7bb2d 100644 --- a/FrontEnd/src/components/button/Button.tsx +++ b/FrontEnd/src/components/button/Button.tsx @@ -1,13 +1,13 @@ import { ComponentPropsWithoutRef, Ref } from "react"; import classNames from "classnames"; -import { Text, useC, ClickableColor } from "../common"; +import { I18nText, useC, ClickableColor } from "../common"; import "./Button.css"; interface ButtonProps extends ComponentPropsWithoutRef<"button"> { color?: ClickableColor; - text?: Text; + text?: I18nText; outline?: boolean; buttonRef?: Ref<HTMLButtonElement> | null; } diff --git a/FrontEnd/src/components/button/ButtonRowV2.tsx b/FrontEnd/src/components/button/ButtonRowV2.tsx index a54425cc..75f2ad9d 100644 --- a/FrontEnd/src/components/button/ButtonRowV2.tsx +++ b/FrontEnd/src/components/button/ButtonRowV2.tsx @@ -1,7 +1,7 @@ import { ComponentPropsWithoutRef, Ref } from "react"; import classNames from "classnames"; -import { Text, ClickableColor } from "../common"; +import { I18nText, ClickableColor } from "../common"; import Button from "./Button"; import FlatButton from "./FlatButton"; @@ -22,21 +22,21 @@ interface ButtonRowV2ButtonBase { interface ButtonRowV2ButtonWithNoType extends ButtonRowV2ButtonBase { type?: undefined | null; - text: Text; + text: I18nText; outline?: boolean; props?: ComponentPropsWithoutRef<typeof Button>; } interface ButtonRowV2NormalButton extends ButtonRowV2ButtonBase { type: "normal"; - text: Text; + text: I18nText; outline?: boolean; props?: ComponentPropsWithoutRef<typeof Button>; } interface ButtonRowV2FlatButton extends ButtonRowV2ButtonBase { type: "flat"; - text: Text; + text: I18nText; props?: ComponentPropsWithoutRef<typeof FlatButton>; } @@ -48,7 +48,7 @@ interface ButtonRowV2IconButton extends ButtonRowV2ButtonBase { interface ButtonRowV2LoadingButton extends ButtonRowV2ButtonBase { type: "loading"; - text: Text; + text: I18nText; loading?: boolean; props?: ComponentPropsWithoutRef<typeof LoadingButton>; } diff --git a/FrontEnd/src/components/button/FlatButton.tsx b/FrontEnd/src/components/button/FlatButton.tsx index aad02e76..e15b8c2b 100644 --- a/FrontEnd/src/components/button/FlatButton.tsx +++ b/FrontEnd/src/components/button/FlatButton.tsx @@ -1,13 +1,13 @@ import { ComponentPropsWithoutRef, Ref } from "react"; import classNames from "classnames"; -import { Text, useC, ClickableColor } from "../common"; +import { I18nText, useC, ClickableColor } from "../common"; import "./FlatButton.css"; interface FlatButtonProps extends ComponentPropsWithoutRef<"button"> { color?: ClickableColor; - text?: Text; + text?: I18nText; buttonRef?: Ref<HTMLButtonElement> | null; } diff --git a/FrontEnd/src/components/common.ts b/FrontEnd/src/components/common.ts index a6c3e705..840e1be5 100644 --- a/FrontEnd/src/components/common.ts +++ b/FrontEnd/src/components/common.ts @@ -1,7 +1,9 @@ import "./index.css"; -export type { Text, I18nText } from "~src/common"; -export { UiLogicError, c, convertI18nText, useC } from "~src/common"; +export type { I18nText } from "~src/i18n"; +export { convertI18nText, useC } from "~src/i18n"; + +export class UiLogicError extends Error {} export const themeColors = [ "primary", @@ -18,5 +20,4 @@ export { breakpoints } from "./breakpoints"; export * as geometry from "~src/utilities/geometry"; -export * as array from "~src/utilities/array" - +export * as array from "~src/utilities/array"; diff --git a/FrontEnd/src/components/dialog/ConfirmDialog.tsx b/FrontEnd/src/components/dialog/ConfirmDialog.tsx index 4ee0ec03..199eee6b 100644 --- a/FrontEnd/src/components/dialog/ConfirmDialog.tsx +++ b/FrontEnd/src/components/dialog/ConfirmDialog.tsx @@ -1,4 +1,4 @@ -import { useC, Text, ThemeColor } from "../common"; +import { useC, I18nText, ThemeColor } from "../common"; import Dialog from "./Dialog"; import DialogContainer from "./DialogContainer"; @@ -14,8 +14,8 @@ export default function ConfirmDialog({ open: boolean; onClose: () => void; onConfirm: () => void; - title: Text; - body: Text; + title: I18nText; + body: I18nText; color?: ThemeColor; bodyColor?: ThemeColor; }) { diff --git a/FrontEnd/src/components/dialog/DialogContainer.tsx b/FrontEnd/src/components/dialog/DialogContainer.tsx index 6ee4e134..844d8ddd 100644 --- a/FrontEnd/src/components/dialog/DialogContainer.tsx +++ b/FrontEnd/src/components/dialog/DialogContainer.tsx @@ -1,14 +1,14 @@ import { ComponentProps, Ref, ReactNode } from "react"; import classNames from "classnames"; -import { ThemeColor, Text, useC } from "../common"; +import { ThemeColor, I18nText, useC } from "../common"; import { ButtonRow, ButtonRowV2 } from "../button"; import "./DialogContainer.css"; interface DialogContainerBaseProps { className?: string; - title: Text; + title: I18nText; titleColor?: ThemeColor; titleClassName?: string; titleRef?: Ref<HTMLDivElement>; diff --git a/FrontEnd/src/components/dialog/OperationDialog.tsx b/FrontEnd/src/components/dialog/OperationDialog.tsx index feaf5c79..4541d6f8 100644 --- a/FrontEnd/src/components/dialog/OperationDialog.tsx +++ b/FrontEnd/src/components/dialog/OperationDialog.tsx @@ -1,7 +1,7 @@ import { useState, ReactNode, ComponentProps } from "react"; import classNames from "classnames"; -import { useC, Text, ThemeColor } from "../common"; +import { useC, I18nText, ThemeColor } from "../common"; import { useInputs, InputGroup, @@ -15,8 +15,8 @@ import DialogContainer from "./DialogContainer"; import "./OperationDialog.css"; interface OperationDialogPromptProps { - message?: Text; - customMessage?: Text; + message?: I18nText; + customMessage?: I18nText; customMessageNode?: ReactNode; className?: string; } @@ -39,12 +39,12 @@ export interface OperationDialogProps<TData> { onClose: () => void; color?: ThemeColor; inputColor?: ThemeColor; - title: Text; - inputPrompt?: Text; + title: I18nText; + inputPrompt?: I18nText; inputPromptNode?: ReactNode; - successPrompt?: (data: TData) => Text; + successPrompt?: (data: TData) => I18nText; successPromptNode?: (data: TData) => ReactNode; - failurePrompt?: (error: unknown) => Text; + failurePrompt?: (error: unknown) => I18nText; failurePromptNode?: (error: unknown) => ReactNode; inputs: InputInitializer; diff --git a/FrontEnd/src/components/hooks/useWindowLeave.ts b/FrontEnd/src/components/hooks/useWindowLeave.ts index ecd999d4..b829a92f 100644 --- a/FrontEnd/src/components/hooks/useWindowLeave.ts +++ b/FrontEnd/src/components/hooks/useWindowLeave.ts @@ -1,10 +1,10 @@ import { useEffect } from "react"; -import { useC, Text } from "../common"; +import { useC, I18nText } from "../common"; export default function useWindowLeave( allow: boolean, - message: Text = "timeline.confirmLeave", + message: I18nText = "timeline.confirmLeave", ) { const c = useC(); diff --git a/FrontEnd/src/components/input/InputGroup.tsx b/FrontEnd/src/components/input/InputGroup.tsx index 47a43b38..be6cd577 100644 --- a/FrontEnd/src/components/input/InputGroup.tsx +++ b/FrontEnd/src/components/input/InputGroup.tsx @@ -26,16 +26,16 @@ import { useState, Ref, useId } from "react"; import classNames from "classnames"; -import { useC, Text, ThemeColor } from "../common"; +import { useC, I18nText, ThemeColor } from "../common"; import "./InputGroup.css"; export interface InputBase { key: string; - label: Text; - helper?: Text; + label: I18nText; + helper?: I18nText; disabled?: boolean; - error?: Text; + error?: I18nText; } export interface TextInput extends InputBase { @@ -51,7 +51,7 @@ export interface BoolInput extends InputBase { export interface SelectInputOption { value: string; - label: Text; + label: I18nText; icon?: string; } @@ -66,14 +66,14 @@ export type Input = TextInput | BoolInput | SelectInput; export type InputValue = Input["value"]; export type InputValueDict = Record<string, InputValue>; -export type InputErrorDict = Record<string, Text>; +export type InputErrorDict = Record<string, I18nText>; export type InputDisabledDict = Record<string, boolean>; export type InputDirtyDict = Record<string, boolean>; // use never so you don't have to cast everywhere export type InputConfirmValueDict = Record<string, never>; export type GeneralInputErrorDict = { - [key: string]: Text | null | undefined; + [key: string]: I18nText | null | undefined; }; type MakeInputInfo<I extends Input> = Omit<I, "value" | "error" | "disabled">; diff --git a/FrontEnd/src/components/menu/Menu.tsx b/FrontEnd/src/components/menu/Menu.tsx index 1a196a69..6093a56f 100644 --- a/FrontEnd/src/components/menu/Menu.tsx +++ b/FrontEnd/src/components/menu/Menu.tsx @@ -1,7 +1,7 @@ import { MouseEvent, CSSProperties } from "react"; import classNames from "classnames"; -import { useC, Text, ThemeColor } from "../common"; +import { useC, I18nText, ThemeColor } from "../common"; import Icon from "../Icon"; import "./Menu.css"; @@ -12,7 +12,7 @@ export type MenuItem = } | { type: "button"; - text: Text; + text: I18nText; icon?: string; color?: ThemeColor; onClick?: (e: MouseEvent<HTMLButtonElement>) => void; diff --git a/FrontEnd/src/components/tab/TabBar.tsx b/FrontEnd/src/components/tab/TabBar.tsx index 601f664d..6957b700 100644 --- a/FrontEnd/src/components/tab/TabBar.tsx +++ b/FrontEnd/src/components/tab/TabBar.tsx @@ -2,13 +2,13 @@ import { ReactNode } from "react"; import { Link } from "react-router-dom"; import classNames from "classnames"; -import { Text, ThemeColor, useC } from "../common"; +import { I18nText, ThemeColor, useC } from "../common"; import "./TabBar.css"; export interface Tab { name: string; - text: Text; + text: I18nText; link?: string; onClick?: () => void; } diff --git a/FrontEnd/src/components/tab/TabPages.tsx b/FrontEnd/src/components/tab/TabPages.tsx index ab45ffdf..71065b01 100644 --- a/FrontEnd/src/components/tab/TabPages.tsx +++ b/FrontEnd/src/components/tab/TabPages.tsx @@ -1,7 +1,7 @@ import { ReactNode, useState } from "react"; import classNames from "classnames"; -import { Text, UiLogicError } from "../common"; +import { I18nText, UiLogicError } from "../common"; import Tabs from "./TabBar"; @@ -9,7 +9,7 @@ import "./TabPages.css"; interface TabPage { name: string; - text: Text; + text: I18nText; page: ReactNode; } diff --git a/FrontEnd/src/i18n.ts b/FrontEnd/src/i18n.ts deleted file mode 100644 index 3166ec3c..00000000 --- a/FrontEnd/src/i18n.ts +++ /dev/null @@ -1,114 +0,0 @@ -import i18n, { BackendModule } from "i18next"; -import LanguageDetector from "i18next-browser-languagedetector"; -import { initReactI18next } from "react-i18next"; - -const backend: BackendModule = { - type: "backend", - init() { - /* do nothing */ - }, - // eslint-disable-next-line @typescript-eslint/no-misused-promises - async read(language, namespace) { - if (namespace === "translation") { - if (language === "en") { - return await import("./locales/en/translation.json"); - } else if (language === "zh") { - return await import("./locales/zh/translation.json"); - } else { - throw Error(`Language ${language} is not supported.`); - } - } else if (namespace === "admin") { - if (language === "en") { - return await import("./locales/en/admin.json"); - } else if (language === "zh") { - return await import("./locales/zh/admin.json"); - } else { - throw Error(`Language ${language} is not supported.`); - } - } else { - throw Error(`Namespace ${namespace} is not supported.`); - } - }, -}; - -export const i18nPromise = i18n - .use(LanguageDetector) - .use(backend) - .use(initReactI18next) // bind react-i18next to the instance - .init({ - fallbackLng: false, - lowerCaseLng: true, - - debug: process.env.NODE_ENV === "development", - - interpolation: { - escapeValue: false, // not needed for react!! - }, - - // react i18next special options (optional) - // override if needed - omit if ok with defaults - /* - react: { - bindI18n: 'languageChanged', - bindI18nStore: '', - transEmptyNodeValue: '', - transSupportBasicHtmlNodes: true, - transKeepBasicHtmlNodesFor: ['br', 'strong', 'i'], - useSuspense: true, - } - */ - }); - -if (module.hot) { - module.hot.accept( - [ - "./locales/en/translation.json", - "./locales/zh/translation.json", - "./locales/en/admin.json", - "./locales/zh/admin.json", - ], - () => { - void i18n.reloadResources(); - }, - ); -} - -export default i18n; - -export type I18nText = - | string - | { type: "text" | "custom"; value: string } - | { type: "i18n"; value: string }; - -type T = typeof i18n.t; - -export function convertI18nText(text: I18nText, t: T): string; -export function convertI18nText( - text: I18nText | null | undefined, - t: T, -): string | null; -export function convertI18nText( - text: I18nText | null | undefined, - t: T, -): string | null { - if (text == null) { - return null; - } else if (typeof text === "string") { - return t(text); - } else if (text.type === "i18n") { - return t(text.value); - } else { - return text.value; - } -} - -export interface C { - (text: I18nText): string; - (text: I18nText | null | undefined): string | null; -} - -export function createC(t: T): C { - return ((text) => convertI18nText(text, t)) as C; -} - -export const c = createC(i18n.t); diff --git a/FrontEnd/src/i18n/backend.ts b/FrontEnd/src/i18n/backend.ts new file mode 100644 index 00000000..92f0c12f --- /dev/null +++ b/FrontEnd/src/i18n/backend.ts @@ -0,0 +1,33 @@ +import { BackendModule } from "i18next"; + + const backend: BackendModule = { + type: "backend", + init() { + /* do nothing */ + }, + // eslint-disable-next-line @typescript-eslint/no-misused-promises + async read(language, namespace) { + if (namespace === "translation") { + if (language === "en") { + return await import("./translations/en/index.json"); + } else if (language === "zh") { + return await import("./translations/zh/index.json"); + } else { + throw Error(`Language ${language} is not supported.`); + } + } else if (namespace === "admin") { + if (language === "en") { + return await import("./translations/en/admin.json"); + } else if (language === "zh") { + return await import("./translations/zh/admin.json"); + } else { + throw Error(`Language ${language} is not supported.`); + } + } else { + throw Error(`Namespace ${namespace} is not supported.`); + } + }, +}; + +export default backend; + diff --git a/FrontEnd/src/i18n/index.ts b/FrontEnd/src/i18n/index.ts new file mode 100644 index 00000000..4bd6dc28 --- /dev/null +++ b/FrontEnd/src/i18n/index.ts @@ -0,0 +1,3 @@ +import "./setup"; +export { default as i18n } from "i18next"; +export * from "./text"; diff --git a/FrontEnd/src/i18n/setup.ts b/FrontEnd/src/i18n/setup.ts new file mode 100644 index 00000000..63dd40ed --- /dev/null +++ b/FrontEnd/src/i18n/setup.ts @@ -0,0 +1,50 @@ +import i18n from "i18next"; +import LanguageDetector from "i18next-browser-languagedetector"; +import { initReactI18next } from "react-i18next"; + +import backend from "./backend"; + +void i18n + .use(LanguageDetector) + .use(backend) + .use(initReactI18next) // bind react-i18next to the instance + .init({ + fallbackLng: false, + lowerCaseLng: true, + + debug: process.env.NODE_ENV === "development", + + interpolation: { + escapeValue: false, // not needed for react!! + }, + + // react i18next special options (optional) + // override if needed - omit if ok with defaults + /* + react: { + bindI18n: 'languageChanged', + bindI18nStore: '', + transEmptyNodeValue: '', + transSupportBasicHtmlNodes: true, + transKeepBasicHtmlNodesFor: ['br', 'strong', 'i'], + useSuspense: true, + } + */ + }); + +if (module.hot) { + module.hot.accept( + [ + "./translations/en/index.json", + "./translations/zh/index.json", + "./translations/en/admin.json", + "./translations/zh/admin.json", + ], + () => { + void i18n.reloadResources(); + }, + ); +} + +export default i18n; + diff --git a/FrontEnd/src/i18n/text.ts b/FrontEnd/src/i18n/text.ts new file mode 100644 index 00000000..f8f7e7e6 --- /dev/null +++ b/FrontEnd/src/i18n/text.ts @@ -0,0 +1,35 @@ +import i18n from "i18next"; +import { useTranslation } from "react-i18next"; + +export type I18nText = + | string + | { type: "text" | "custom"; value: string } + | { type: "i18n"; value: string }; + +type T = typeof i18n.t; + +export function convertI18nText(text: I18nText, t: T): string { + if (typeof text === "string") { + return t(text); + } else if (text.type === "i18n") { + return t(text.value); + } else { + return text.value; + } +} + +export interface C { + (text: I18nText): string; +} + +export function createC(t: T): C { + return ((text) => convertI18nText(text, t)) as C; +} + +export const c = createC(i18n.t); + +export function useC(ns?: string): C { + const { t } = useTranslation(ns); + return createC(t); +} + diff --git a/FrontEnd/src/locales/en/admin.json b/FrontEnd/src/i18n/translations/en/admin.json index ddb3ffad..ddb3ffad 100644 --- a/FrontEnd/src/locales/en/admin.json +++ b/FrontEnd/src/i18n/translations/en/admin.json diff --git a/FrontEnd/src/locales/en/translation.json b/FrontEnd/src/i18n/translations/en/index.json index 1b43357c..1b43357c 100644 --- a/FrontEnd/src/locales/en/translation.json +++ b/FrontEnd/src/i18n/translations/en/index.json diff --git a/FrontEnd/src/locales/zh/admin.json b/FrontEnd/src/i18n/translations/zh/admin.json index edd1cabd..edd1cabd 100644 --- a/FrontEnd/src/locales/zh/admin.json +++ b/FrontEnd/src/i18n/translations/zh/admin.json diff --git a/FrontEnd/src/locales/zh/translation.json b/FrontEnd/src/i18n/translations/zh/index.json index dc0d6672..dc0d6672 100644 --- a/FrontEnd/src/locales/zh/translation.json +++ b/FrontEnd/src/i18n/translations/zh/index.json diff --git a/FrontEnd/src/utilities/hooks/useReverseScrollPositionRemember.ts b/FrontEnd/src/migrating/hooks/useReverseScrollPositionRemember.ts index 339a12b8..339a12b8 100644 --- a/FrontEnd/src/utilities/hooks/useReverseScrollPositionRemember.ts +++ b/FrontEnd/src/migrating/hooks/useReverseScrollPositionRemember.ts diff --git a/FrontEnd/src/pages/about/index.tsx b/FrontEnd/src/pages/about/index.tsx index bce64322..f95557c2 100644 --- a/FrontEnd/src/pages/about/index.tsx +++ b/FrontEnd/src/pages/about/index.tsx @@ -1,6 +1,6 @@ import "./index.css"; -import { useC } from "~src/common"; +import { useC } from "~src/components/common"; import Page from "~src/components/Page"; interface Credit { diff --git a/FrontEnd/src/pages/setting/ChangeAvatarDialog.tsx b/FrontEnd/src/pages/setting/ChangeAvatarDialog.tsx index 4cdecbbb..9ede593e 100644 --- a/FrontEnd/src/pages/setting/ChangeAvatarDialog.tsx +++ b/FrontEnd/src/pages/setting/ChangeAvatarDialog.tsx @@ -1,11 +1,10 @@ import { useState, ChangeEvent, ComponentPropsWithoutRef } from "react"; -import { useC, Text, UiLogicError } from "~src/common"; - import { useUser } from "~src/services/user"; import { getHttpUserClient } from "~src/http/user"; +import { useC, I18nText, UiLogicError } from "~src/components/common"; import { ImageCropper, useImageCrop } from "~src/components/ImageCropper"; import BlobImage from "~src/components/BlobImage"; import { ButtonRowV2 } from "~src/components/button"; @@ -43,7 +42,7 @@ export default function ChangeAvatarDialog({ }); const [resultBlob, setResultBlob] = useState<Blob | null>(null); - const [message, setMessage] = useState<Text>( + const [message, setMessage] = useState<I18nText>( "settings.dialogChangeAvatar.prompt.select", ); diff --git a/FrontEnd/src/pages/setting/index.tsx b/FrontEnd/src/pages/setting/index.tsx index 3fb18e24..70df1b32 100644 --- a/FrontEnd/src/pages/setting/index.tsx +++ b/FrontEnd/src/pages/setting/index.tsx @@ -11,8 +11,7 @@ import classNames from "classnames"; import { useUser, userService } from "~src/services/user"; import { getHttpUserClient } from "~src/http/user"; -import { useC, Text } from "~src/common"; - +import { useC, I18nText } from "~src/components/common"; import { pushAlert } from "~src/components/alert"; import { useDialog, ConfirmDialog } from "~src/components/dialog"; import Card from "~src/components/Card"; @@ -27,7 +26,7 @@ import "./index.css"; interface SettingSectionProps extends Omit<ComponentPropsWithoutRef<typeof Card>, "title"> { - title: Text; + title: I18nText; children?: ReactNode; } @@ -49,8 +48,8 @@ function SettingSection({ interface SettingItemContainerProps extends Omit<ComponentPropsWithoutRef<"div">, "title"> { - title: Text; - description?: Text; + title: I18nText; + description?: I18nText; danger?: boolean; extraClassName?: string; } @@ -78,7 +77,9 @@ function SettingItemContainer({ > <div className="setting-item-label-area"> <div className="setting-item-label-title">{c(title)}</div> - <small className="setting-item-label-sub">{c(description)}</small> + {description && ( + <small className="setting-item-label-sub">{c(description)}</small> + )} </div> <div className="setting-item-value-area">{children}</div> </div> @@ -97,7 +98,7 @@ interface SelectSettingItemProps extends Omit<SettingItemContainerProps, "onSelect" | "extraClassName"> { options: { value: string; - label: Text; + label: I18nText; }[]; value?: string | null; onSelect: (value: string) => void; diff --git a/FrontEnd/src/pages/timeline/Timeline.tsx b/FrontEnd/src/pages/timeline/Timeline.tsx index e2ab5c71..69dfecea 100644 --- a/FrontEnd/src/pages/timeline/Timeline.tsx +++ b/FrontEnd/src/pages/timeline/Timeline.tsx @@ -115,7 +115,7 @@ export function Timeline(props: TimelineProps) { return () => { subscription.unsubscribe(); }; - }, [timelineOwner, timelineName]); + }, [timelineOwner, timelineName, reloadPosts]); useScrollToBottom(() => { console.log(`Load page ${currentPage + 1}.`); diff --git a/FrontEnd/src/pages/timeline/TimelineMember.tsx b/FrontEnd/src/pages/timeline/TimelineMember.tsx index 0812016f..4fa9cf72 100644 --- a/FrontEnd/src/pages/timeline/TimelineMember.tsx +++ b/FrontEnd/src/pages/timeline/TimelineMember.tsx @@ -1,12 +1,11 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { convertI18nText, I18nText } from "~src/common"; - import { HttpUser } from "~src/http/user"; import { getHttpSearchClient } from "~src/http/search"; import { getHttpTimelineClient, HttpTimelineInfo } from "~src/http/timeline"; +import { convertI18nText, I18nText } from "~src/components/common"; import SearchInput from "~src/components/SearchInput"; import UserAvatar from "~src/components/user/UserAvatar"; import { IconButton } from "~src/components/button"; diff --git a/FrontEnd/src/pages/timeline/edit/TimelinePostCreateView.tsx b/FrontEnd/src/pages/timeline/edit/TimelinePostCreateView.tsx index c0a80ad0..fe04bfa2 100644 --- a/FrontEnd/src/pages/timeline/edit/TimelinePostCreateView.tsx +++ b/FrontEnd/src/pages/timeline/edit/TimelinePostCreateView.tsx @@ -1,8 +1,6 @@ import { useState } from "react"; import classNames from "classnames"; -import { UiLogicError } from "~src/common"; - import { getHttpTimelineClient, HttpTimelineInfo, @@ -12,7 +10,7 @@ import { import base64 from "~src/utilities/base64"; -import { useC } from "~/src/components/common"; +import { UiLogicError, useC } from "~/src/components/common"; import { pushAlert } from "~src/components/alert"; import { IconButton, LoadingButton } from "~src/components/button"; import PopupMenu from "~src/components/menu/PopupMenu"; diff --git a/FrontEnd/src/pages/timeline/index.tsx b/FrontEnd/src/pages/timeline/index.tsx index 6cd1ded0..ee792d93 100644 --- a/FrontEnd/src/pages/timeline/index.tsx +++ b/FrontEnd/src/pages/timeline/index.tsx @@ -1,6 +1,6 @@ import { useParams } from "react-router-dom"; -import { UiLogicError } from "~src/common"; +import { UiLogicError } from "~src/components/common"; import Timeline from "./Timeline"; diff --git a/FrontEnd/src/services/alert.ts b/FrontEnd/src/services/alert.ts index 0fa37848..e968af76 100644 --- a/FrontEnd/src/services/alert.ts +++ b/FrontEnd/src/services/alert.ts @@ -1,7 +1,6 @@ import pull from "lodash/pull"; -import { I18nText } from "~src/common"; -import { ThemeColor } from "~src/components/common"; +import { I18nText, ThemeColor } from "~src/components/common"; export interface AlertInfo { type?: ThemeColor; diff --git a/FrontEnd/src/services/user.ts b/FrontEnd/src/services/user.ts index 5f682a36..0fd363f5 100644 --- a/FrontEnd/src/services/user.ts +++ b/FrontEnd/src/services/user.ts @@ -2,12 +2,12 @@ import { useState, useEffect } from "react"; import { BehaviorSubject, Observable } from "rxjs"; import { AxiosError } from "axios"; -import { UiLogicError } from "~src/common"; import { setHttpToken, axios, HttpBadRequestError } from "~src/http/common"; import { getHttpTokenClient } from "~src/http/token"; import { getHttpUserClient, HttpUser, UserPermission } from "~src/http/user"; +import { UiLogicError } from "~src/components/common"; import { pushAlert } from "~src/components/alert"; interface IAuthUser extends HttpUser { diff --git a/FrontEnd/src/utilities/hooks/use-c.ts b/FrontEnd/src/utilities/hooks/use-c.ts deleted file mode 100644 index 96195ae2..00000000 --- a/FrontEnd/src/utilities/hooks/use-c.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useTranslation } from "react-i18next"; -import { C, createC } from "../../i18n"; - -export default function useC(ns?: string): C { - const { t } = useTranslation(ns); - return createC(t); -} |