fix: rerender i18n in host components on lang change (#6224)

This commit is contained in:
David Luzar
2023-02-22 15:01:23 +01:00
committed by GitHub
parent e4506be3e8
commit 04a8c22f39
14 changed files with 88 additions and 72 deletions

View File

@ -0,0 +1,3 @@
import { unstable_createStore } from "jotai";
export const appJotaiStore = unstable_createStore();

View File

@ -70,7 +70,7 @@ import { decryptData } from "../../data/encryption";
import { resetBrowserStateVersions } from "../data/tabSync";
import { LocalData } from "../data/LocalData";
import { atom, useAtom } from "jotai";
import { jotaiStore } from "../../jotai";
import { appJotaiStore } from "../app-jotai";
export const collabAPIAtom = atom<CollabAPI | null>(null);
export const collabDialogShownAtom = atom(false);
@ -167,7 +167,7 @@ class Collab extends PureComponent<Props, CollabState> {
setUsername: this.setUsername,
};
jotaiStore.set(collabAPIAtom, collabAPI);
appJotaiStore.set(collabAPIAtom, collabAPI);
this.onOfflineStatusToggle();
if (
@ -185,7 +185,7 @@ class Collab extends PureComponent<Props, CollabState> {
}
onOfflineStatusToggle = () => {
jotaiStore.set(isOfflineAtom, !window.navigator.onLine);
appJotaiStore.set(isOfflineAtom, !window.navigator.onLine);
};
componentWillUnmount() {
@ -208,10 +208,10 @@ class Collab extends PureComponent<Props, CollabState> {
}
}
isCollaborating = () => jotaiStore.get(isCollaboratingAtom)!;
isCollaborating = () => appJotaiStore.get(isCollaboratingAtom)!;
private setIsCollaborating = (isCollaborating: boolean) => {
jotaiStore.set(isCollaboratingAtom, isCollaborating);
appJotaiStore.set(isCollaboratingAtom, isCollaborating);
};
private onUnload = () => {
@ -804,7 +804,7 @@ class Collab extends PureComponent<Props, CollabState> {
);
handleClose = () => {
jotaiStore.set(collabDialogShownAtom, false);
appJotaiStore.set(collabDialogShownAtom, false);
};
setUsername = (username: string) => {

View File

@ -10,13 +10,13 @@ import {
shareWindows,
} from "../../components/icons";
import { ToolButton } from "../../components/ToolButton";
import { t } from "../../i18n";
import "./RoomDialog.scss";
import Stack from "../../components/Stack";
import { AppState } from "../../types";
import { trackEvent } from "../../analytics";
import { getFrame } from "../../utils";
import DialogActionButton from "../../components/DialogActionButton";
import { useI18n } from "../../i18n";
const getShareIcon = () => {
const navigator = window.navigator as any;
@ -51,6 +51,7 @@ const RoomDialog = ({
setErrorMessage: (message: string) => void;
theme: AppState["theme"];
}) => {
const { t } = useI18n();
const roomLinkInput = useRef<HTMLInputElement>(null);
const copyRoomLink = async () => {

View File

@ -1,12 +1,13 @@
import React from "react";
import { PlusPromoIcon } from "../../components/icons";
import { t } from "../../i18n";
import { useI18n } from "../../i18n";
import { WelcomeScreen } from "../../packages/excalidraw/index";
import { isExcalidrawPlusSignedUser } from "../app_constants";
export const AppWelcomeScreen: React.FC<{
setCollabDialogShown: (toggle: boolean) => any;
}> = React.memo((props) => {
const { t } = useI18n();
let headingContent;
if (isExcalidrawPlusSignedUser) {

View File

@ -1,17 +1,21 @@
import { shield } from "../../components/icons";
import { Tooltip } from "../../components/Tooltip";
import { t } from "../../i18n";
import { useI18n } from "../../i18n";
export const EncryptedIcon = () => (
<a
className="encrypted-icon tooltip"
href="https://blog.excalidraw.com/end-to-end-encryption/"
target="_blank"
rel="noopener noreferrer"
aria-label={t("encrypted.link")}
>
<Tooltip label={t("encrypted.tooltip")} long={true}>
{shield}
</Tooltip>
</a>
);
export const EncryptedIcon = () => {
const { t } = useI18n();
return (
<a
className="encrypted-icon tooltip"
href="https://blog.excalidraw.com/end-to-end-encryption/"
target="_blank"
rel="noopener noreferrer"
aria-label={t("encrypted.link")}
>
<Tooltip label={t("encrypted.tooltip")} long={true}>
{shield}
</Tooltip>
</a>
);
};

View File

@ -6,7 +6,7 @@ import { loadFirebaseStorage, saveFilesToFirebase } from "../data/firebase";
import { FileId, NonDeletedExcalidrawElement } from "../../element/types";
import { AppState, BinaryFileData, BinaryFiles } from "../../types";
import { nanoid } from "nanoid";
import { t } from "../../i18n";
import { useI18n } from "../../i18n";
import { excalidrawPlusIcon } from "./icons";
import { encryptData, generateEncryptionKey } from "../../data/encryption";
import { isInitializedImageElement } from "../../element/typeChecks";
@ -79,6 +79,7 @@ export const ExportToExcalidrawPlus: React.FC<{
files: BinaryFiles;
onError: (error: Error) => void;
}> = ({ elements, appState, files, onError }) => {
const { t } = useI18n();
return (
<Card color="primary">
<div className="Card-icon">{excalidrawPlusIcon}</div>

View File

@ -1,22 +1,23 @@
import { useAtom } from "jotai";
import { useSetAtom } from "jotai";
import React from "react";
import { langCodeAtom } from "..";
import * as i18n from "../../i18n";
import { appLangCodeAtom } from "..";
import { defaultLang, useI18n } from "../../i18n";
import { languages } from "../../i18n";
export const LanguageList = ({ style }: { style?: React.CSSProperties }) => {
const [langCode, setLangCode] = useAtom(langCodeAtom);
const { t, langCode } = useI18n();
const setLangCode = useSetAtom(appLangCodeAtom);
return (
<select
className="dropdown-select dropdown-select__language"
onChange={({ target }) => setLangCode(target.value)}
value={langCode}
aria-label={i18n.t("buttons.selectLanguage")}
aria-label={t("buttons.selectLanguage")}
style={style}
>
<option key={i18n.defaultLang.code} value={i18n.defaultLang.code}>
{i18n.defaultLang.label}
<option key={defaultLang.code} value={defaultLang.code}>
{defaultLang.label}
</option>
{languages.map((lang) => (
<option key={lang.code} value={lang.code}>

View File

@ -75,13 +75,14 @@ import { loadFilesFromFirebase } from "./data/firebase";
import { LocalData } from "./data/LocalData";
import { isBrowserStorageStateNewer } from "./data/tabSync";
import clsx from "clsx";
import { atom, Provider, useAtom, useAtomValue } from "jotai";
import { jotaiStore, useAtomWithInitialValue } from "../jotai";
import { reconcileElements } from "./collab/reconciliation";
import { parseLibraryTokensFromUrl, useHandleLibrary } from "../data/library";
import { AppMainMenu } from "./components/AppMainMenu";
import { AppWelcomeScreen } from "./components/AppWelcomeScreen";
import { AppFooter } from "./components/AppFooter";
import { atom, Provider, useAtom, useAtomValue } from "jotai";
import { useAtomWithInitialValue } from "../jotai";
import { appJotaiStore } from "./app-jotai";
import "./index.scss";
@ -226,15 +227,15 @@ const initializeScene = async (opts: {
return { scene: null, isExternalScene: false };
};
const currentLangCode = languageDetector.detect() || defaultLang.code;
export const langCodeAtom = atom(
Array.isArray(currentLangCode) ? currentLangCode[0] : currentLangCode,
const detectedLangCode = languageDetector.detect() || defaultLang.code;
export const appLangCodeAtom = atom(
Array.isArray(detectedLangCode) ? detectedLangCode[0] : detectedLangCode,
);
const ExcalidrawWrapper = () => {
const [errorMessage, setErrorMessage] = useState("");
const [langCode, setLangCode] = useAtom(langCodeAtom);
const [langCode, setLangCode] = useAtom(appLangCodeAtom);
// initial state
// ---------------------------------------------------------------------------
@ -683,7 +684,7 @@ const ExcalidrawWrapper = () => {
const ExcalidrawApp = () => {
return (
<TopErrorBoundary>
<Provider unstable_createStore={() => jotaiStore}>
<Provider unstable_createStore={() => appJotaiStore}>
<ExcalidrawWrapper />
</Provider>
</TopErrorBoundary>