Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Support data sync #494

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
import installExtension, { VUEJS3_DEVTOOLS } from "electron-devtools-installer";
import fs from "fs";
import path from "path";
const MongoClient = require("mongodb").MongoClient;
qcgm1978 marked this conversation as resolved.
Show resolved Hide resolved
import updateApp from "./update";
const isDevelopment = process.env.NODE_ENV !== "production";

Expand Down Expand Up @@ -310,7 +311,7 @@ ipcMain.handle("save-proxy-setting", async (event, args) => {
});
});

ipcMain.handle("save-proxy-and-restart", async () => {
ipcMain.handle("restart-app", async () => {
app.relaunch();
app.exit();
return "";
Expand Down
168 changes: 126 additions & 42 deletions src/components/ChatSetting.vue
qcgm1978 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<template>
<v-list-item>
<v-list-item-title>{{ $t("chat.name") }}</v-list-item-title>
<v-btn
color="primary"
variant="outlined"
Expand All @@ -11,69 +12,150 @@
variant="outlined"
:text="$t('chat.downloadAllChatHistory')"
@click="downloadJson"
style="margin-left: 10px"
style="margin: 10px"
></v-btn>
</v-list-item>
<v-list-item>
<v-list-item-title>{{ $t("proxy.fullSet") }}</v-list-item-title>
<v-btn
color="primary"
variant="outlined"
:text="$t('chat.backupToLocal')"
@click="downloadDataJson"
style="margin: 10px; float: left"
></v-btn>
<!-- <pre v-if="jsonData">{{ jsonData }}</pre> -->
<v-file-input
color="primary"
variant="outlined"
:label="$t('chat.restoreFromLocal')"
@change="readJson"
style="width: 400px"
></v-file-input>
</v-list-item>
<ConfirmModal ref="confirmModal" />
<v-snackbar
v-model="snackbar.show"
:timeout="snackbar.timeout"
:color="snackbar.color"
>
{{ snackbar.text }}
</v-snackbar>
</template>

<script setup>
import { ref } from "vue";
import { ref, reactive } from "vue";
import { useStore } from "vuex";
import i18n from "@/i18n";
const electron = window.require("electron");
const ipcRenderer = electron.ipcRenderer;
import ConfirmModal from "@/components/ConfirmModal.vue";
import bots from "@/bots";

const emit = defineEmits(["close-dialog"]);
const confirmModal = ref();
const store = useStore();
const jsonData = ref(null);
const snackbar = reactive({
show: false,
text: "",
timeout: 1500,
color: "success",
});
const readJson = async (event) => {
const reader = new FileReader();
reader.onload = (evt) => {
const value = JSON.parse(evt.target.result);
jsonData.value = value;
reload(value);
};
reader.readAsText(event.target.files[0]);
};
async function reload(value) {
const load = i18n.global.t("proxy.saveAndApply");
const result = await confirmModal.value.showModal("", `${load}?`);
if (result) {
// todo merge data if supporting merge local and new data
// let value_messages = JSON.parse(value["chatall-messages"]);
// const local_chats = JSON.parse(localStorage["chatall-messages"]).chats;
// value_messages.chats.forEach((element) => {
// element.index += local_chats.length;
// });
// value_messages.chats = [...local_chats, ...value_chats.chats];
// value["chatall-messages"] = JSON.stringify(value_messages.chats);
qcgm1978 marked this conversation as resolved.
Show resolved Hide resolved
Object.keys(value).map((d) => (localStorage[d] = value[d]));
await ipcRenderer.invoke("restart-app");
}
}

// This function downloads the chat history as a JSON file.

const downloadJson = () => {
const messages = get_messages();
if (!messages) {
console.error("chatall-messages not found in localStorage");
return;
}

// Create an array of messages from the chat history.
const content = "history";
download_by_link(messages, content);
};
function get_messages() {
// Get the chat history from localStorage.
const chatallMessages = localStorage.getItem("chatall-messages");
if (!chatallMessages) {
console.error("chatall-messages not found in localStorage");
return;
}

const chats = JSON.parse(chatallMessages)?.chats ?? [];

// Create an array of messages from the chat history.
const messages = chats
.filter((d) => !d.hide)
.map((chat) => ({
// The title of the chat.
title: chat.title,
// The messages in the chat.
messages: chat.messages
.filter((d) => !d.hide)
.reduce((arr, message) => {
const t = message.type;
const content = message.content;
if (t == "prompt") {
arr.push({
prompt: content,
responses: [],
});
} else {
const botClassname = message.className;
const bot = bots.getBotByClassName(botClassname);
const botName = bot.getFullname();
arr.at(-1).responses.push({
content,
botName,
botClassname,
botModel: message.model,
highlight: message.highlight,
});
}
return arr;
}, []),
}));

// Create a blob that contains the JSON data.
// The space parameter specifies the indentation of nested objects in the string representation.
const blob = new Blob([JSON.stringify({ chats: messages }, null, 2)], {
try {
// Create an array of messages from the chat history.
const messages = chats
.filter((d) => !d.hide)
.map((chat) => ({
// The title of the chat.
title: chat.title,
// The messages in the chat.
messages: chat.messages
.filter((d) => !d.hide)
.reduce((arr, message) => {
const t = message.type;
const content = message.content;
if (t == "prompt") {
arr.push({
prompt: content,
responses: [],
});
} else {
const botClassname = message.className;
const bot = bots.getBotByClassName(botClassname);
const botName = bot.getFullname();
arr.at(-1).responses.push({
content,
botName,
botClassname,
botModel: message.model,
highlight: message.highlight,
});
}
return arr;
}, []),
}));
return messages;
} catch (e) {
// debugger;
}
}
const downloadDataJson = () => {
const content = "data";
const messages = localStorage;
download_by_link(messages, content);
};
// Create a blob that contains the JSON data.
// The space parameter specifies the indentation of nested objects in the string representation.
function download_by_link(messages, name) {
const blob = new Blob([JSON.stringify(messages, null, 2)], {
// The type of the blob.
type: "application/json",
});
Expand All @@ -88,7 +170,7 @@ const downloadJson = () => {
const hour = String(date.getHours()).padStart(2, "0");
const minute = String(date.getMinutes()).padStart(2, "0");
const second = String(date.getSeconds()).padStart(2, "0");
const fileName = `chatall-history-${year}${month}${day}-${hour}${minute}${second}`;
const fileName = `chatall-${name}-${year}${month}${day}-${hour}${minute}${second}`;

const a = document.createElement("a");
a.href = url;
Expand All @@ -103,7 +185,8 @@ const downloadJson = () => {

// Revoke the URL for the blob.
URL.revokeObjectURL(url);
};
}

async function deleteChats() {
const confirm = await confirmModal.value.showModal(
"",
Expand All @@ -115,3 +198,4 @@ async function deleteChats() {
}
}
</script>
@/utils/storage
2 changes: 1 addition & 1 deletion src/components/ProxySetting.vue
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ async function saveAndActive() {
);
if (result) {
await onlySave();
await ipcRenderer.invoke("save-proxy-and-restart");
await ipcRenderer.invoke("restart-app");
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,14 @@
"newChat": "New Chat",
"deleteAllChatHistory": "Delete All Chat History",
"downloadAllChatHistory": "Save All Chat History",
"confirmDeleteAllChatHistory": "Are you sure you want to delete all chat history? This action cannot be undone."
"confirmDeleteAllChatHistory": "Are you sure you want to delete all chat history? This action cannot be undone.",
"backupToLocal": "Backup all data to local",
"restoreFromLocal": "Restore from backup",
"MongoDBAtlas":"MongoDB Atlas",
"upload": "Upload",
"download": "Download",
"confirmUpload": "Are you sure you want to upload all chat history?",
"confirmDownload": "Are you sure you want to download all chat history?"
qcgm1978 marked this conversation as resolved.
Show resolved Hide resolved
},
"bot": {
"creatingConversation": "Creating conversation...",
Expand Down
12 changes: 10 additions & 2 deletions src/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,14 @@
"newChat": "新对话",
"deleteAllChatHistory": "删除所有对话记录",
"downloadAllChatHistory": "保存所有对话记录",
"confirmDeleteAllChatHistory": "你确定要删除所有对话记录吗?此操作无法撤消。"
"confirmDeleteAllChatHistory": "你确定要删除所有对话记录吗?此操作无法撤消。",
"backupToLocal": "备份所有数据到本地",
"restoreFromLocal": "从备份恢复",
"MongoDBAtlas":"MongoDB 云数据库",
"upload": "上传",
"download": "下载",
"confirmUpload": "你确定要上传所有对话记录吗?",
"confirmDownload": "你确定要下载所有对话记录吗?"
qcgm1978 marked this conversation as resolved.
Show resolved Hide resolved
},
"bot": {
"creatingConversation": "创建新对话...",
Expand Down Expand Up @@ -253,7 +260,8 @@
"itemsPerPageAll": "全部"
},
"input": {
"clear": "清除"
"clear": "清除",
"prependAction": "预设操作"
qcgm1978 marked this conversation as resolved.
Show resolved Hide resolved
}
},
"10": "10",
Expand Down
1 change: 1 addition & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { get_messages } from "./storage";
47 changes: 47 additions & 0 deletions src/utils/storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import bots from "@/bots";
export function get_messages() {
qcgm1978 marked this conversation as resolved.
Show resolved Hide resolved
// Get the chat history from localStorage.
const chatallMessages = localStorage.getItem("chatall-messages");
if (!chatallMessages) {
return;
}

const chats = JSON.parse(chatallMessages)?.chats ?? [];
try {
// Create an array of messages from the chat history.
const messages = chats
.filter((d) => !d.hide)
.map((chat) => ({
// The title of the chat.
title: chat.title,
// The messages in the chat.
messages: chat.messages
.filter((d) => !d.hide)
.reduce((arr, message) => {
const t = message.type;
const content = message.content;
if (t == "prompt") {
arr.push({
prompt: content,
responses: [],
});
} else {
const botClassname = message.className;
const bot = bots.getBotByClassName(botClassname);
const botName = bot.getFullname();
arr.at(-1).responses.push({
content,
botName,
botClassname,
botModel: message.model,
highlight: message.highlight,
});
}
return arr;
}, []),
}));
return messages;
} catch (e) {
// debugger;
}
}