forked from XiaoMo/ChatGPT-Next-Web
feat: migrate state from v1 to v2
This commit is contained in:
parent
401c1364be
commit
30040a0366
@ -1,7 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
import GithubIcon from "../icons/github.svg";
|
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 {
|
interface IErrorBoundaryState {
|
||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
@ -20,6 +23,25 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
|
|||||||
this.setState({ hasError: true, error, info });
|
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() {
|
render() {
|
||||||
if (this.state.hasError) {
|
if (this.state.hasError) {
|
||||||
// Render error message
|
// Render error message
|
||||||
@ -31,6 +53,7 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
|
|||||||
<code>{this.state.info?.componentStack}</code>
|
<code>{this.state.info?.componentStack}</code>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
<a href={ISSUE_URL} className="report">
|
<a href={ISSUE_URL} className="report">
|
||||||
<IconButton
|
<IconButton
|
||||||
text="Report This Error"
|
text="Report This Error"
|
||||||
@ -38,6 +61,15 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
|
|||||||
bordered
|
bordered
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
<IconButton
|
||||||
|
icon={<ResetIcon />}
|
||||||
|
text="Clear All Data"
|
||||||
|
onClick={() =>
|
||||||
|
confirm(Locale.Store.ConfirmClearAll) && this.clearAndSaveData()
|
||||||
|
}
|
||||||
|
bordered
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,15 @@ export enum FileName {
|
|||||||
Prompts = "prompts.json",
|
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 MAX_SIDEBAR_WIDTH = 500;
|
||||||
export const MIN_SIDEBAR_WIDTH = 230;
|
export const MIN_SIDEBAR_WIDTH = 230;
|
||||||
export const NARROW_SIDEBAR_WIDTH = 100;
|
export const NARROW_SIDEBAR_WIDTH = 100;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
|
import { StoreKey } from "../constant";
|
||||||
|
|
||||||
export interface AccessControlStore {
|
export interface AccessControlStore {
|
||||||
accessCode: string;
|
accessCode: string;
|
||||||
@ -14,8 +15,6 @@ export interface AccessControlStore {
|
|||||||
fetch: () => void;
|
fetch: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ACCESS_KEY = "access-control";
|
|
||||||
|
|
||||||
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
|
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
|
||||||
|
|
||||||
export const useAccessStore = create<AccessControlStore>()(
|
export const useAccessStore = create<AccessControlStore>()(
|
||||||
@ -62,7 +61,7 @@ export const useAccessStore = create<AccessControlStore>()(
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: ACCESS_KEY,
|
name: StoreKey.Access,
|
||||||
version: 1,
|
version: 1,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -13,6 +13,7 @@ import Locale from "../locales";
|
|||||||
import { showToast } from "../components/ui-lib";
|
import { showToast } from "../components/ui-lib";
|
||||||
import { DEFAULT_CONFIG, ModelConfig, ModelType, useAppConfig } from "./config";
|
import { DEFAULT_CONFIG, ModelConfig, ModelType, useAppConfig } from "./config";
|
||||||
import { createEmptyMask, Mask } from "./mask";
|
import { createEmptyMask, Mask } from "./mask";
|
||||||
|
import { StoreKey } from "../constant";
|
||||||
|
|
||||||
export type Message = ChatCompletionResponseMessage & {
|
export type Message = ChatCompletionResponseMessage & {
|
||||||
date: string;
|
date: string;
|
||||||
@ -109,8 +110,6 @@ function countMessages(msgs: Message[]) {
|
|||||||
return msgs.reduce((pre, cur) => pre + cur.content.length, 0);
|
return msgs.reduce((pre, cur) => pre + cur.content.length, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const LOCAL_KEY = "chat-next-web-store";
|
|
||||||
|
|
||||||
export const useChatStore = create<ChatStore>()(
|
export const useChatStore = create<ChatStore>()(
|
||||||
persist(
|
persist(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
@ -489,16 +488,29 @@ export const useChatStore = create<ChatStore>()(
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: LOCAL_KEY,
|
name: StoreKey.Chat,
|
||||||
version: 2,
|
version: 2,
|
||||||
migrate(persistedState, version) {
|
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) {
|
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;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
|
import { StoreKey } from "../constant";
|
||||||
|
|
||||||
export enum SubmitKey {
|
export enum SubmitKey {
|
||||||
Enter = "Enter",
|
Enter = "Enter",
|
||||||
@ -112,8 +113,6 @@ export const ModalConfigValidator = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const CONFIG_KEY = "app-config";
|
|
||||||
|
|
||||||
export const useAppConfig = create<ChatConfigStore>()(
|
export const useAppConfig = create<ChatConfigStore>()(
|
||||||
persist(
|
persist(
|
||||||
(set, get) => ({
|
(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;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -4,8 +4,7 @@ import { BUILTIN_MASKS } from "../masks";
|
|||||||
import { getLang, Lang } from "../locales";
|
import { getLang, Lang } from "../locales";
|
||||||
import { DEFAULT_TOPIC, Message } from "./chat";
|
import { DEFAULT_TOPIC, Message } from "./chat";
|
||||||
import { ModelConfig, ModelType, useAppConfig } from "./config";
|
import { ModelConfig, ModelType, useAppConfig } from "./config";
|
||||||
|
import { StoreKey } from "../constant";
|
||||||
export const MASK_KEY = "mask-store";
|
|
||||||
|
|
||||||
export type Mask = {
|
export type Mask = {
|
||||||
id: number;
|
id: number;
|
||||||
@ -93,7 +92,7 @@ export const useMaskStore = create<MaskStore>()(
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: MASK_KEY,
|
name: StoreKey.Mask,
|
||||||
version: 2,
|
version: 2,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -2,6 +2,7 @@ import { create } from "zustand";
|
|||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import { getLang } from "../locales";
|
import { getLang } from "../locales";
|
||||||
|
import { StoreKey } from "../constant";
|
||||||
|
|
||||||
export interface Prompt {
|
export interface Prompt {
|
||||||
id?: number;
|
id?: number;
|
||||||
@ -23,8 +24,6 @@ export interface PromptStore {
|
|||||||
updateUserPrompts: (id: number, updater: (prompt: Prompt) => void) => void;
|
updateUserPrompts: (id: number, updater: (prompt: Prompt) => void) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PROMPT_KEY = "prompt-store";
|
|
||||||
|
|
||||||
export const SearchService = {
|
export const SearchService = {
|
||||||
ready: false,
|
ready: false,
|
||||||
builtinEngine: new Fuse<Prompt>([], { keys: ["title"] }),
|
builtinEngine: new Fuse<Prompt>([], { keys: ["title"] }),
|
||||||
@ -123,7 +122,7 @@ export const usePromptStore = create<PromptStore>()(
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: PROMPT_KEY,
|
name: StoreKey.Prompt,
|
||||||
version: 1,
|
version: 1,
|
||||||
onRehydrateStorage(state) {
|
onRehydrateStorage(state) {
|
||||||
const PROMPT_URL = "./prompts.json";
|
const PROMPT_URL = "./prompts.json";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
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";
|
import { requestUsage } from "../requests";
|
||||||
|
|
||||||
export interface UpdateStore {
|
export interface UpdateStore {
|
||||||
@ -16,8 +16,6 @@ export interface UpdateStore {
|
|||||||
updateUsage: (force?: boolean) => Promise<void>;
|
updateUsage: (force?: boolean) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UPDATE_KEY = "chat-update";
|
|
||||||
|
|
||||||
function queryMeta(key: string, defaultValue?: string): string {
|
function queryMeta(key: string, defaultValue?: string): string {
|
||||||
let ret: string;
|
let ret: string;
|
||||||
if (document) {
|
if (document) {
|
||||||
@ -84,7 +82,7 @@ export const useUpdateStore = create<UpdateStore>()(
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: UPDATE_KEY,
|
name: StoreKey.Update,
|
||||||
version: 1,
|
version: 1,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user