Merge pull request #5386 from ConnectAI-E/feature/safeLocalStorage
fix: safaLocalStorage
This commit is contained in:
commit
35f77f45a2
|
@ -67,6 +67,7 @@ import {
|
||||||
isVisionModel,
|
isVisionModel,
|
||||||
isDalle3,
|
isDalle3,
|
||||||
showPlugins,
|
showPlugins,
|
||||||
|
safeLocalStorage,
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
|
|
||||||
import { uploadImage as uploadImageRemote } from "@/app/utils/chat";
|
import { uploadImage as uploadImageRemote } from "@/app/utils/chat";
|
||||||
|
@ -109,6 +110,8 @@ import { getClientConfig } from "../config/client";
|
||||||
import { useAllModels } from "../utils/hooks";
|
import { useAllModels } from "../utils/hooks";
|
||||||
import { MultimodalContent } from "../client/api";
|
import { MultimodalContent } from "../client/api";
|
||||||
|
|
||||||
|
const localStorage = safeLocalStorage();
|
||||||
|
|
||||||
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
||||||
loading: () => <LoadingIcon />,
|
loading: () => <LoadingIcon />,
|
||||||
});
|
});
|
||||||
|
@ -941,7 +944,7 @@ function _Chat() {
|
||||||
.onUserInput(userInput, attachImages)
|
.onUserInput(userInput, attachImages)
|
||||||
.then(() => setIsLoading(false));
|
.then(() => setIsLoading(false));
|
||||||
setAttachImages([]);
|
setAttachImages([]);
|
||||||
localStorage.setItem(LAST_INPUT_KEY, userInput);
|
chatStore.setLastInput(userInput);
|
||||||
setUserInput("");
|
setUserInput("");
|
||||||
setPromptHints([]);
|
setPromptHints([]);
|
||||||
if (!isMobileScreen) inputRef.current?.focus();
|
if (!isMobileScreen) inputRef.current?.focus();
|
||||||
|
@ -1007,7 +1010,7 @@ function _Chat() {
|
||||||
userInput.length <= 0 &&
|
userInput.length <= 0 &&
|
||||||
!(e.metaKey || e.altKey || e.ctrlKey)
|
!(e.metaKey || e.altKey || e.ctrlKey)
|
||||||
) {
|
) {
|
||||||
setUserInput(localStorage.getItem(LAST_INPUT_KEY) ?? "");
|
setUserInput(chatStore.lastInput ?? "");
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { ISSUE_URL } from "../constant";
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
import { showConfirm } from "./ui-lib";
|
import { showConfirm } from "./ui-lib";
|
||||||
import { useSyncStore } from "../store/sync";
|
import { useSyncStore } from "../store/sync";
|
||||||
|
import { useChatStore } from "../store/chat";
|
||||||
|
|
||||||
interface IErrorBoundaryState {
|
interface IErrorBoundaryState {
|
||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
|
@ -30,8 +31,7 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
|
||||||
try {
|
try {
|
||||||
useSyncStore.getState().export();
|
useSyncStore.getState().export();
|
||||||
} finally {
|
} finally {
|
||||||
localStorage.clear();
|
useChatStore.getState().clearAllData();
|
||||||
location.reload();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -426,16 +426,7 @@ export function MaskPage() {
|
||||||
const maskStore = useMaskStore();
|
const maskStore = useMaskStore();
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
|
|
||||||
const [filterLang, setFilterLang] = useState<Lang | undefined>(
|
const filterLang = maskStore.language;
|
||||||
() => localStorage.getItem("Mask-language") as Lang | undefined,
|
|
||||||
);
|
|
||||||
useEffect(() => {
|
|
||||||
if (filterLang) {
|
|
||||||
localStorage.setItem("Mask-language", filterLang);
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem("Mask-language");
|
|
||||||
}
|
|
||||||
}, [filterLang]);
|
|
||||||
|
|
||||||
const allMasks = maskStore
|
const allMasks = maskStore
|
||||||
.getAll()
|
.getAll()
|
||||||
|
@ -542,9 +533,9 @@ export function MaskPage() {
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const value = e.currentTarget.value;
|
const value = e.currentTarget.value;
|
||||||
if (value === Locale.Settings.Lang.All) {
|
if (value === Locale.Settings.Lang.All) {
|
||||||
setFilterLang(undefined);
|
maskStore.setLanguage(undefined);
|
||||||
} else {
|
} else {
|
||||||
setFilterLang(value as Lang);
|
maskStore.setLanguage(value as Lang);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -18,10 +18,13 @@ import ar from "./ar";
|
||||||
import bn from "./bn";
|
import bn from "./bn";
|
||||||
import sk from "./sk";
|
import sk from "./sk";
|
||||||
import { merge } from "../utils/merge";
|
import { merge } from "../utils/merge";
|
||||||
|
import { safeLocalStorage } from "@/app/utils";
|
||||||
|
|
||||||
import type { LocaleType } from "./cn";
|
import type { LocaleType } from "./cn";
|
||||||
export type { LocaleType, PartialLocaleType } from "./cn";
|
export type { LocaleType, PartialLocaleType } from "./cn";
|
||||||
|
|
||||||
|
const localStorage = safeLocalStorage();
|
||||||
|
|
||||||
const ALL_LANGS = {
|
const ALL_LANGS = {
|
||||||
cn,
|
cn,
|
||||||
en,
|
en,
|
||||||
|
@ -82,17 +85,11 @@ merge(fallbackLang, targetLang);
|
||||||
export default fallbackLang as LocaleType;
|
export default fallbackLang as LocaleType;
|
||||||
|
|
||||||
function getItem(key: string) {
|
function getItem(key: string) {
|
||||||
try {
|
return localStorage.getItem(key);
|
||||||
return localStorage.getItem(key);
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setItem(key: string, value: string) {
|
function setItem(key: string, value: string) {
|
||||||
try {
|
localStorage.setItem(key, value);
|
||||||
localStorage.setItem(key, value);
|
|
||||||
} catch {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLanguage() {
|
function getLanguage() {
|
||||||
|
|
|
@ -26,9 +26,11 @@ import { nanoid } from "nanoid";
|
||||||
import { createPersistStore } from "../utils/store";
|
import { createPersistStore } from "../utils/store";
|
||||||
import { collectModelsWithDefaultModel } from "../utils/model";
|
import { collectModelsWithDefaultModel } from "../utils/model";
|
||||||
import { useAccessStore } from "./access";
|
import { useAccessStore } from "./access";
|
||||||
import { isDalle3 } from "../utils";
|
import { isDalle3, safeLocalStorage } from "../utils";
|
||||||
import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
|
import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
|
||||||
|
|
||||||
|
const localStorage = safeLocalStorage();
|
||||||
|
|
||||||
export type ChatMessageTool = {
|
export type ChatMessageTool = {
|
||||||
id: string;
|
id: string;
|
||||||
index?: number;
|
index?: number;
|
||||||
|
@ -179,6 +181,7 @@ function fillTemplateWith(input: string, modelConfig: ModelConfig) {
|
||||||
const DEFAULT_CHAT_STATE = {
|
const DEFAULT_CHAT_STATE = {
|
||||||
sessions: [createEmptySession()],
|
sessions: [createEmptySession()],
|
||||||
currentSessionIndex: 0,
|
currentSessionIndex: 0,
|
||||||
|
lastInput: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useChatStore = createPersistStore(
|
export const useChatStore = createPersistStore(
|
||||||
|
@ -701,6 +704,11 @@ export const useChatStore = createPersistStore(
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
location.reload();
|
location.reload();
|
||||||
},
|
},
|
||||||
|
setLastInput(lastInput: string) {
|
||||||
|
set({
|
||||||
|
lastInput,
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return methods;
|
return methods;
|
||||||
|
|
|
@ -23,9 +23,12 @@ export type Mask = {
|
||||||
|
|
||||||
export const DEFAULT_MASK_STATE = {
|
export const DEFAULT_MASK_STATE = {
|
||||||
masks: {} as Record<string, Mask>,
|
masks: {} as Record<string, Mask>,
|
||||||
|
language: undefined as Lang | undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MaskState = typeof DEFAULT_MASK_STATE;
|
export type MaskState = typeof DEFAULT_MASK_STATE & {
|
||||||
|
language?: Lang | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
export const DEFAULT_MASK_AVATAR = "gpt-bot";
|
export const DEFAULT_MASK_AVATAR = "gpt-bot";
|
||||||
export const createEmptyMask = () =>
|
export const createEmptyMask = () =>
|
||||||
|
@ -102,6 +105,11 @@ export const useMaskStore = createPersistStore(
|
||||||
search(text: string) {
|
search(text: string) {
|
||||||
return Object.values(get().masks);
|
return Object.values(get().masks);
|
||||||
},
|
},
|
||||||
|
setLanguage(language: Lang | undefined) {
|
||||||
|
set({
|
||||||
|
language,
|
||||||
|
});
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: StoreKey.Mask,
|
name: StoreKey.Mask,
|
||||||
|
|
60
app/utils.ts
60
app/utils.ts
|
@ -318,3 +318,63 @@ export function adapter(config: Record<string, unknown>) {
|
||||||
: path;
|
: path;
|
||||||
return fetch(fetchUrl as string, { ...rest, responseType: "text" });
|
return fetch(fetchUrl as string, { ...rest, responseType: "text" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function safeLocalStorage(): {
|
||||||
|
getItem: (key: string) => string | null;
|
||||||
|
setItem: (key: string, value: string) => void;
|
||||||
|
removeItem: (key: string) => void;
|
||||||
|
clear: () => void;
|
||||||
|
} {
|
||||||
|
let storage: Storage | null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof window !== "undefined" && window.localStorage) {
|
||||||
|
storage = window.localStorage;
|
||||||
|
} else {
|
||||||
|
storage = null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("localStorage is not available:", e);
|
||||||
|
storage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getItem(key: string): string | null {
|
||||||
|
if (storage) {
|
||||||
|
return storage.getItem(key);
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Attempted to get item "${key}" from localStorage, but localStorage is not available.`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setItem(key: string, value: string): void {
|
||||||
|
if (storage) {
|
||||||
|
storage.setItem(key, value);
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Attempted to set item "${key}" in localStorage, but localStorage is not available.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeItem(key: string): void {
|
||||||
|
if (storage) {
|
||||||
|
storage.removeItem(key);
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Attempted to remove item "${key}" from localStorage, but localStorage is not available.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clear(): void {
|
||||||
|
if (storage) {
|
||||||
|
storage.clear();
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
"Attempted to clear localStorage, but localStorage is not available.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import { StateStorage } from "zustand/middleware";
|
import { StateStorage } from "zustand/middleware";
|
||||||
import { get, set, del, clear } from "idb-keyval";
|
import { get, set, del, clear } from "idb-keyval";
|
||||||
|
import { safeLocalStorage } from "@/app/utils";
|
||||||
|
|
||||||
|
const localStorage = safeLocalStorage();
|
||||||
|
|
||||||
class IndexedDBStorage implements StateStorage {
|
class IndexedDBStorage implements StateStorage {
|
||||||
public async getItem(name: string): Promise<string | null> {
|
public async getItem(name: string): Promise<string | null> {
|
||||||
|
|
Loading…
Reference in New Issue