feat: migrate state from v1 to v2

This commit is contained in:
Yidadaa 2023-04-27 02:00:22 +08:00
parent 401c1364be
commit 30040a0366
8 changed files with 85 additions and 27 deletions

View File

@ -1,7 +1,10 @@
import React from "react";
import { IconButton } from "./button";
import GithubIcon from "../icons/github.svg";
import { ISSUE_URL } from "../constant";
import ResetIcon from "../icons/reload.svg";
import { ISSUE_URL, StoreKey } from "../constant";
import Locale from "../locales";
import { downloadAs } from "../utils";
interface IErrorBoundaryState {
hasError: boolean;
@ -20,6 +23,25 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
this.setState({ hasError: true, error, info });
}
clearAndSaveData() {
const snapshot: Record<string, any> = {};
Object.values(StoreKey).forEach((key) => {
snapshot[key] = localStorage.getItem(key);
if (snapshot[key]) {
try {
snapshot[key] = JSON.parse(snapshot[key]);
} catch {}
}
});
try {
downloadAs(JSON.stringify(snapshot), "chatgpt-next-web-snapshot.json");
} catch {}
localStorage.clear();
}
render() {
if (this.state.hasError) {
// Render error message
@ -31,13 +53,23 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
<code>{this.state.info?.componentStack}</code>
</pre>
<a href={ISSUE_URL} className="report">
<div style={{ display: "flex", justifyContent: "space-between" }}>
<a href={ISSUE_URL} className="report">
<IconButton
text="Report This Error"
icon={<GithubIcon />}
bordered
/>
</a>
<IconButton
text="Report This Error"
icon={<GithubIcon />}
icon={<ResetIcon />}
text="Clear All Data"
onClick={() =>
confirm(Locale.Store.ConfirmClearAll) && this.clearAndSaveData()
}
bordered
/>
</a>
</div>
</div>
);
}

View File

@ -24,6 +24,15 @@ export enum FileName {
Prompts = "prompts.json",
}
export enum StoreKey {
Chat = "chat-next-web-store",
Access = "access-control",
Config = "app-config",
Mask = "mask-store",
Prompt = "prompt-store",
Update = "chat-update",
}
export const MAX_SIDEBAR_WIDTH = 500;
export const MIN_SIDEBAR_WIDTH = 230;
export const NARROW_SIDEBAR_WIDTH = 100;

View File

@ -1,5 +1,6 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { StoreKey } from "../constant";
export interface AccessControlStore {
accessCode: string;
@ -14,8 +15,6 @@ export interface AccessControlStore {
fetch: () => void;
}
export const ACCESS_KEY = "access-control";
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
export const useAccessStore = create<AccessControlStore>()(
@ -62,7 +61,7 @@ export const useAccessStore = create<AccessControlStore>()(
},
}),
{
name: ACCESS_KEY,
name: StoreKey.Access,
version: 1,
},
),

View File

@ -13,6 +13,7 @@ import Locale from "../locales";
import { showToast } from "../components/ui-lib";
import { DEFAULT_CONFIG, ModelConfig, ModelType, useAppConfig } from "./config";
import { createEmptyMask, Mask } from "./mask";
import { StoreKey } from "../constant";
export type Message = ChatCompletionResponseMessage & {
date: string;
@ -109,8 +110,6 @@ function countMessages(msgs: Message[]) {
return msgs.reduce((pre, cur) => pre + cur.content.length, 0);
}
const LOCAL_KEY = "chat-next-web-store";
export const useChatStore = create<ChatStore>()(
persist(
(set, get) => ({
@ -489,16 +488,29 @@ export const useChatStore = create<ChatStore>()(
},
}),
{
name: LOCAL_KEY,
name: StoreKey.Chat,
version: 2,
migrate(persistedState, version) {
const state = persistedState as ChatStore;
const state = persistedState as any;
const newState = JSON.parse(JSON.stringify(state)) as ChatStore;
if (version < 2) {
state.sessions.forEach((s) => (s.mask = createEmptyMask()));
newState.globalId = 0;
newState.sessions = [];
const oldSessions = state.sessions;
for (const oldSession of oldSessions) {
const newSession = createEmptySession();
newSession.topic = oldSession.topic;
newSession.messages = [...oldSession.messages];
newSession.mask.modelConfig.sendMemory = true;
newSession.mask.modelConfig.historyMessageCount = 4;
newSession.mask.modelConfig.compressMessageLengthThreshold = 1000;
newState.sessions.push(newSession);
}
}
return state;
return newState;
},
},
),

View File

@ -1,5 +1,6 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { StoreKey } from "../constant";
export enum SubmitKey {
Enter = "Enter",
@ -112,8 +113,6 @@ export const ModalConfigValidator = {
},
};
const CONFIG_KEY = "app-config";
export const useAppConfig = create<ChatConfigStore>()(
persist(
(set, get) => ({
@ -130,7 +129,18 @@ export const useAppConfig = create<ChatConfigStore>()(
},
}),
{
name: CONFIG_KEY,
name: StoreKey.Config,
version: 2,
migrate(persistedState, version) {
if (version === 2) return persistedState as any;
const state = persistedState as ChatConfig;
state.modelConfig.sendMemory = true;
state.modelConfig.historyMessageCount = 4;
state.modelConfig.compressMessageLengthThreshold = 1000;
return state;
},
},
),
);

View File

@ -4,8 +4,7 @@ import { BUILTIN_MASKS } from "../masks";
import { getLang, Lang } from "../locales";
import { DEFAULT_TOPIC, Message } from "./chat";
import { ModelConfig, ModelType, useAppConfig } from "./config";
export const MASK_KEY = "mask-store";
import { StoreKey } from "../constant";
export type Mask = {
id: number;
@ -93,7 +92,7 @@ export const useMaskStore = create<MaskStore>()(
},
}),
{
name: MASK_KEY,
name: StoreKey.Mask,
version: 2,
},
),

View File

@ -2,6 +2,7 @@ import { create } from "zustand";
import { persist } from "zustand/middleware";
import Fuse from "fuse.js";
import { getLang } from "../locales";
import { StoreKey } from "../constant";
export interface Prompt {
id?: number;
@ -23,8 +24,6 @@ export interface PromptStore {
updateUserPrompts: (id: number, updater: (prompt: Prompt) => void) => void;
}
export const PROMPT_KEY = "prompt-store";
export const SearchService = {
ready: false,
builtinEngine: new Fuse<Prompt>([], { keys: ["title"] }),
@ -123,7 +122,7 @@ export const usePromptStore = create<PromptStore>()(
},
}),
{
name: PROMPT_KEY,
name: StoreKey.Prompt,
version: 1,
onRehydrateStorage(state) {
const PROMPT_URL = "./prompts.json";

View File

@ -1,6 +1,6 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { FETCH_COMMIT_URL, FETCH_TAG_URL } from "../constant";
import { FETCH_COMMIT_URL, FETCH_TAG_URL, StoreKey } from "../constant";
import { requestUsage } from "../requests";
export interface UpdateStore {
@ -16,8 +16,6 @@ export interface UpdateStore {
updateUsage: (force?: boolean) => Promise<void>;
}
export const UPDATE_KEY = "chat-update";
function queryMeta(key: string, defaultValue?: string): string {
let ret: string;
if (document) {
@ -84,7 +82,7 @@ export const useUpdateStore = create<UpdateStore>()(
},
}),
{
name: UPDATE_KEY,
name: StoreKey.Update,
version: 1,
},
),