Skip to content

Commit

Permalink
feat: close ChatGPTNextWeb#2754 add import/export to file
Browse files Browse the repository at this point in the history
  • Loading branch information
Yidadaa authored and chenzeyu committed Nov 8, 2023
1 parent 3833824 commit 84a6be4
Show file tree
Hide file tree
Showing 14 changed files with 862 additions and 655 deletions.
7 changes: 2 additions & 5 deletions app/components/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import GithubIcon from "../icons/github.svg";
import ResetIcon from "../icons/reload.svg";
import { ISSUE_URL } from "../constant";
import Locale from "../locales";
import { downloadAs } from "../utils";
import { showConfirm } from "./ui-lib";
import { useSyncStore } from "../store/sync";

interface IErrorBoundaryState {
hasError: boolean;
Expand All @@ -26,10 +26,7 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {

clearAndSaveData() {
try {
downloadAs(
JSON.stringify(localStorage),
"chatgpt-next-web-snapshot.json",
);
useSyncStore.getState().export();
} finally {
localStorage.clear();
location.reload();
Expand Down
6 changes: 4 additions & 2 deletions app/components/mask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ export function MaskPage() {
const closeMaskModal = () => setEditingMaskId(undefined);

const downloadAll = () => {
downloadAs(JSON.stringify(masks), FileName.Masks);
downloadAs(JSON.stringify(masks.filter((v) => !v.builtin)), FileName.Masks);
};

const importFromFile = () => {
Expand Down Expand Up @@ -452,11 +452,13 @@ export function MaskPage() {
icon={<DownloadIcon />}
bordered
onClick={downloadAll}
text={Locale.UI.Export}
/>
</div>
<div className="window-action-button">
<IconButton
icon={<UploadIcon />}
text={Locale.UI.Import}
bordered
onClick={() => importFromFile()}
/>
Expand Down Expand Up @@ -604,7 +606,7 @@ export function MaskPage() {
<MaskConfig
mask={editingMask}
updateMask={(updater) =>
maskStore.update(editingMaskId!, updater)
maskStore.updateMask(editingMaskId!, updater)
}
readonly={editingMask.builtin}
/>
Expand Down
104 changes: 48 additions & 56 deletions app/components/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import ClearIcon from "../icons/clear.svg";
import LoadingIcon from "../icons/three-dots.svg";
import EditIcon from "../icons/edit.svg";
import EyeIcon from "../icons/eye.svg";
import DownloadIcon from "../icons/download.svg";
import UploadIcon from "../icons/upload.svg";

import {
Input,
List,
Expand Down Expand Up @@ -49,6 +52,7 @@ import { Avatar, AvatarPicker } from "./emoji";
import { getClientConfig } from "../config/client";
import { useSyncStore } from "../store/sync";
import { nanoid } from "nanoid";
import { useMaskStore } from "../store/mask";

function EditPromptModal(props: { id: string; onClose: () => void }) {
const promptStore = usePromptStore();
Expand All @@ -75,7 +79,7 @@ function EditPromptModal(props: { id: string; onClose: () => void }) {
readOnly={!prompt.isUser}
className={styles["edit-prompt-title"]}
onInput={(e) =>
promptStore.update(
promptStore.updatePrompt(
props.id,
(prompt) => (prompt.title = e.currentTarget.value),
)
Expand All @@ -87,7 +91,7 @@ function EditPromptModal(props: { id: string; onClose: () => void }) {
className={styles["edit-prompt-content"]}
rows={10}
onInput={(e) =>
promptStore.update(
promptStore.updatePrompt(
props.id,
(prompt) => (prompt.content = e.currentTarget.value),
)
Expand Down Expand Up @@ -127,14 +131,15 @@ function UserPromptModal(props: { onClose?: () => void }) {
actions={[
<IconButton
key="add"
onClick={() =>
promptStore.add({
onClick={() => {
const promptId = promptStore.add({
id: nanoid(),
createdAt: Date.now(),
title: "Empty Prompt",
content: "Empty Prompt Content",
})
}
});
setEditingPromptId(promptId);
}}
icon={<AddIcon />}
bordered
text={Locale.Settings.Prompt.Modal.Add}
Expand Down Expand Up @@ -244,70 +249,57 @@ function DangerItems() {
function SyncItems() {
const syncStore = useSyncStore();
const webdav = syncStore.webDavConfig;
const chatStore = useChatStore();
const promptStore = usePromptStore();
const maskStore = useMaskStore();

const stateOverview = useMemo(() => {
const sessions = chatStore.sessions;
const messageCount = sessions.reduce((p, c) => p + c.messages.length, 0);

// not ready: https://github.com/Yidadaa/ChatGPT-Next-Web/issues/920#issuecomment-1609866332
return null;
return {
chat: sessions.length,
message: messageCount,
prompt: Object.keys(promptStore.prompts).length,
mask: Object.keys(maskStore.masks).length,
};
}, [chatStore.sessions, maskStore.masks, promptStore.prompts]);

return (
<List>
<ListItem
title={"上次同步:" + new Date().toLocaleString()}
subTitle={"20 次对话,100 条消息,200 提示词,20 面具"}
title={Locale.Settings.Sync.LastUpdate}
subTitle={new Date().toLocaleString()}
>
<IconButton
icon={<ResetIcon />}
text="同步"
text={Locale.UI.Sync}
onClick={() => {
syncStore.check().then(console.log);
}}
/>
</ListItem>

<ListItem
title={"本地备份"}
subTitle={"20 次对话,100 条消息,200 提示词,20 面具"}
></ListItem>

<ListItem
title={"Web Dav Server"}
subTitle={Locale.Settings.AccessCode.SubTitle}
title={Locale.Settings.Sync.LocalState}
subTitle={Locale.Settings.Sync.Overview(stateOverview)}
>
<input
value={webdav.server}
type="text"
placeholder={"https://example.com"}
onChange={(e) => {
syncStore.update(
(config) => (config.server = e.currentTarget.value),
);
}}
/>
</ListItem>

<ListItem title="Web Dav User Name" subTitle="user name here">
<input
value={webdav.username}
type="text"
placeholder={"username"}
onChange={(e) => {
syncStore.update(
(config) => (config.username = e.currentTarget.value),
);
}}
/>
</ListItem>

<ListItem title="Web Dav Password" subTitle="password here">
<input
value={webdav.password}
type="text"
placeholder={"password"}
onChange={(e) => {
syncStore.update(
(config) => (config.password = e.currentTarget.value),
);
}}
/>
<div style={{ display: "flex" }}>
<IconButton
icon={<UploadIcon />}
text={Locale.UI.Export}
onClick={() => {
syncStore.export();
}}
/>
<IconButton
icon={<DownloadIcon />}
text={Locale.UI.Import}
onClick={() => {
syncStore.import();
}}
/>
</div>
</ListItem>
</List>
);
Expand Down Expand Up @@ -562,6 +554,8 @@ export function Settings() {
</ListItem>
</List>

<SyncItems />

<List>
<ListItem
title={Locale.Settings.Mask.Splash.Title}
Expand Down Expand Up @@ -722,8 +716,6 @@ export function Settings() {
</ListItem>
</List>

<SyncItems />

<List>
<ModelConfigList
modelConfig={config.modelConfig}
Expand Down
11 changes: 11 additions & 0 deletions app/locales/cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,14 @@ const cn = {
Title: "自动生成标题",
SubTitle: "根据对话内容生成合适的标题",
},
Sync: {
LastUpdate: "上次同步",
LocalState: "本地数据",
Overview: (overview: any) => {
return `${overview.chat} 次对话,${overview.message} 条消息,${overview.prompt} 条提示词,${overview.mask} 个面具`;
},
ImportFailed: "导入失败",
},
Mask: {
Splash: {
Title: "面具启动页",
Expand Down Expand Up @@ -368,6 +376,9 @@ const cn = {
Close: "关闭",
Create: "新建",
Edit: "编辑",
Export: "导出",
Import: "导入",
Sync: "同步",
},
Exporter: {
Model: "模型",
Expand Down
Loading

0 comments on commit 84a6be4

Please sign in to comment.