mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-06-07 13:20:23 +09:00
move code to utils/file
This commit is contained in:
parent
e2f0206d88
commit
33450ce429
@ -6,9 +6,10 @@ import locales from "@/app/locales";
|
|||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { useIndexedDB } from "react-indexed-db-hook";
|
import { useIndexedDB } from "react-indexed-db-hook";
|
||||||
import { StoreKey } from "@/app/constant";
|
import { StoreKey } from "@/app/constant";
|
||||||
import { SdDbInit, useSdStore } from "@/app/store/sd";
|
import { useSdStore } from "@/app/store/sd";
|
||||||
|
import { FileDbInit } from "@/app/utils/file";
|
||||||
|
|
||||||
SdDbInit();
|
FileDbInit();
|
||||||
|
|
||||||
const sdCommonParams = (model: string, data: any) => {
|
const sdCommonParams = (model: string, data: any) => {
|
||||||
return [
|
return [
|
||||||
|
@ -3,7 +3,7 @@ import styles from "@/app/components/sd.module.scss";
|
|||||||
import { IconButton } from "@/app/components/button";
|
import { IconButton } from "@/app/components/button";
|
||||||
import ReturnIcon from "@/app/icons/return.svg";
|
import ReturnIcon from "@/app/icons/return.svg";
|
||||||
import Locale from "@/app/locales";
|
import Locale from "@/app/locales";
|
||||||
import { Path, StoreKey } from "@/app/constant";
|
import { Path } from "@/app/constant";
|
||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
copyToClipboard,
|
copyToClipboard,
|
||||||
@ -20,7 +20,6 @@ import DeleteIcon from "@/app/icons/clear.svg";
|
|||||||
import CopyIcon from "@/app/icons/copy.svg";
|
import CopyIcon from "@/app/icons/copy.svg";
|
||||||
import PromptIcon from "@/app/icons/prompt.svg";
|
import PromptIcon from "@/app/icons/prompt.svg";
|
||||||
import ResetIcon from "@/app/icons/reload.svg";
|
import ResetIcon from "@/app/icons/reload.svg";
|
||||||
import { useIndexedDB } from "react-indexed-db-hook";
|
|
||||||
import { useSdStore } from "@/app/store/sd";
|
import { useSdStore } from "@/app/store/sd";
|
||||||
import locales from "@/app/locales";
|
import locales from "@/app/locales";
|
||||||
import LoadingIcon from "../icons/three-dots.svg";
|
import LoadingIcon from "../icons/three-dots.svg";
|
||||||
@ -30,19 +29,10 @@ import {
|
|||||||
showConfirm,
|
showConfirm,
|
||||||
showImageModal,
|
showImageModal,
|
||||||
showModal,
|
showModal,
|
||||||
|
IndexDBImage,
|
||||||
} from "@/app/components/ui-lib";
|
} from "@/app/components/ui-lib";
|
||||||
import { func } from "prop-types";
|
import { func } from "prop-types";
|
||||||
|
import { useFileDB } from "@/app/utils/file";
|
||||||
function getBase64ImgUrl(base64Data: string, contentType: string) {
|
|
||||||
const byteCharacters = atob(base64Data);
|
|
||||||
const byteNumbers = new Array(byteCharacters.length);
|
|
||||||
for (let i = 0; i < byteCharacters.length; i++) {
|
|
||||||
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
||||||
}
|
|
||||||
const byteArray = new Uint8Array(byteNumbers);
|
|
||||||
const blob = new Blob([byteArray], { type: contentType });
|
|
||||||
return URL.createObjectURL(blob);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSdTaskStatus(item: any) {
|
function getSdTaskStatus(item: any) {
|
||||||
let s: string;
|
let s: string;
|
||||||
@ -94,45 +84,6 @@ function getSdTaskStatus(item: any) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function IndexDBImage({ img_data, title, isMobileScreen }) {
|
|
||||||
const [src, setSrc] = useState(img_data);
|
|
||||||
const sdListDb = useIndexedDB(StoreKey.SdList);
|
|
||||||
const img_id = useMemo(
|
|
||||||
() => img_data.replace("indexeddb://", "").split("@").pop(),
|
|
||||||
[img_data],
|
|
||||||
);
|
|
||||||
useEffect(() => {
|
|
||||||
sdListDb
|
|
||||||
.getByID(img_id)
|
|
||||||
.then(({ data }) => {
|
|
||||||
setSrc(data);
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
setSrc(img_data);
|
|
||||||
});
|
|
||||||
}, [img_data, img_id]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<img
|
|
||||||
className={styles["img"]}
|
|
||||||
src={`data:image/png;base64,${src}`}
|
|
||||||
alt={title}
|
|
||||||
onClick={(e) => {
|
|
||||||
showImageModal(
|
|
||||||
getBase64ImgUrl(src, "image/png"),
|
|
||||||
true,
|
|
||||||
isMobileScreen
|
|
||||||
? { width: "100%", height: "fit-content" }
|
|
||||||
: { maxWidth: "100%", maxHeight: "100%" },
|
|
||||||
isMobileScreen
|
|
||||||
? { width: "100%", height: "fit-content" }
|
|
||||||
: { width: "100%", height: "100%" },
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Sd() {
|
export function Sd() {
|
||||||
const isMobileScreen = useMobileScreen();
|
const isMobileScreen = useMobileScreen();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -140,7 +91,7 @@ export function Sd() {
|
|||||||
const showMaxIcon = !isMobileScreen && !clientConfig?.isApp;
|
const showMaxIcon = !isMobileScreen && !clientConfig?.isApp;
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
const sdListDb = useIndexedDB(StoreKey.SdList);
|
const fileDb = useFileDB();
|
||||||
const sdStore = useSdStore();
|
const sdStore = useSdStore();
|
||||||
const [sdImages, setSdImages] = useState(sdStore.draw);
|
const [sdImages, setSdImages] = useState(sdStore.draw);
|
||||||
|
|
||||||
@ -197,13 +148,25 @@ export function Sd() {
|
|||||||
className={styles["sd-img-item"]}
|
className={styles["sd-img-item"]}
|
||||||
>
|
>
|
||||||
{item.status === "success" ? (
|
{item.status === "success" ? (
|
||||||
<>
|
|
||||||
<IndexDBImage
|
<IndexDBImage
|
||||||
img_data={item.img_data}
|
className={styles["img"]}
|
||||||
title={item.id}
|
db={fileDb}
|
||||||
|
src={item.img_data}
|
||||||
|
alt={item.id}
|
||||||
|
onClick={(data, e) => {
|
||||||
|
showImageModal(
|
||||||
|
data,
|
||||||
|
true,
|
||||||
|
isMobileScreen
|
||||||
|
? { width: "100%", height: "fit-content" }
|
||||||
|
: { maxWidth: "100%", maxHeight: "100%" },
|
||||||
|
isMobileScreen
|
||||||
|
? { width: "100%", height: "fit-content" }
|
||||||
|
: { width: "100%", height: "100%" },
|
||||||
|
);
|
||||||
|
}}
|
||||||
isMobileScreen={isMobileScreen}
|
isMobileScreen={isMobileScreen}
|
||||||
/>
|
/>
|
||||||
</>
|
|
||||||
) : item.status === "error" ? (
|
) : item.status === "error" ? (
|
||||||
<div className={styles["pre-img"]}>
|
<div className={styles["pre-img"]}>
|
||||||
<ErrorIcon />
|
<ErrorIcon />
|
||||||
@ -286,7 +249,7 @@ export function Sd() {
|
|||||||
created_at: new Date().toLocaleString(),
|
created_at: new Date().toLocaleString(),
|
||||||
img_data: "",
|
img_data: "",
|
||||||
};
|
};
|
||||||
sdStore.sendTask(reqData, sdListDb);
|
sdStore.sendTask(reqData, fileDb);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ChatAction
|
<ChatAction
|
||||||
@ -295,7 +258,7 @@ export function Sd() {
|
|||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (await showConfirm(Locale.Sd.Danger.Delete)) {
|
if (await showConfirm(Locale.Sd.Danger.Delete)) {
|
||||||
// remove img_data + remove item in list
|
// remove img_data + remove item in list
|
||||||
sdListDb.deleteRecord(item.id).then(
|
fileDb.deleteRecord(item.id).then(
|
||||||
() => {
|
() => {
|
||||||
sdStore.draw = sdImages.filter(
|
sdStore.draw = sdImages.filter(
|
||||||
(i: any) => i.id !== item.id,
|
(i: any) => i.id !== item.id,
|
||||||
|
@ -19,6 +19,7 @@ import React, {
|
|||||||
MouseEvent,
|
MouseEvent,
|
||||||
useEffect,
|
useEffect,
|
||||||
useState,
|
useState,
|
||||||
|
useMemo,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
|
|
||||||
@ -510,3 +511,29 @@ export function Selector<T>(props: {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function IndexDBImage({ src, alt, onClick, db, className }) {
|
||||||
|
const [data, setData] = useState(src);
|
||||||
|
const imgId = useMemo(
|
||||||
|
() => src.replace("indexeddb://", "").split("@").pop(),
|
||||||
|
[src],
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
db.getByID(imgId)
|
||||||
|
.then(({ data }) => {
|
||||||
|
setData(`data:image/png;base64,${data}`);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
setData(src);
|
||||||
|
});
|
||||||
|
}, [src, imgId]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
className={className}
|
||||||
|
src={data}
|
||||||
|
alt={alt}
|
||||||
|
onClick={(e) => onClick(data, e)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -55,6 +55,7 @@ export enum FileName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum StoreKey {
|
export enum StoreKey {
|
||||||
|
File = "chat-next-web-file",
|
||||||
Chat = "chat-next-web-store",
|
Chat = "chat-next-web-store",
|
||||||
Access = "access-control",
|
Access = "access-control",
|
||||||
Config = "app-config",
|
Config = "app-config",
|
||||||
|
@ -1,33 +1,9 @@
|
|||||||
import { initDB } from "react-indexed-db-hook";
|
|
||||||
import { StabilityPath, StoreKey } from "@/app/constant";
|
import { StabilityPath, StoreKey } from "@/app/constant";
|
||||||
import { showToast } from "@/app/components/ui-lib";
|
import { showToast } from "@/app/components/ui-lib";
|
||||||
import { getHeaders } from "@/app/client/api";
|
import { getHeaders } from "@/app/client/api";
|
||||||
import { createPersistStore } from "@/app/utils/store";
|
import { createPersistStore } from "@/app/utils/store";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
|
|
||||||
export const SdDbConfig = {
|
|
||||||
name: "@chatgpt-next-web/sd",
|
|
||||||
version: 1,
|
|
||||||
objectStoresMeta: [
|
|
||||||
{
|
|
||||||
store: StoreKey.SdList,
|
|
||||||
storeConfig: { keyPath: "id", autoIncrement: true },
|
|
||||||
storeSchema: [
|
|
||||||
{ name: "data", keypath: "data", options: { unique: false } },
|
|
||||||
{
|
|
||||||
name: "created_at",
|
|
||||||
keypath: "created_at",
|
|
||||||
options: { unique: false },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export function SdDbInit() {
|
|
||||||
initDB(SdDbConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useSdStore = createPersistStore<
|
export const useSdStore = createPersistStore<
|
||||||
{
|
{
|
||||||
currentId: number;
|
currentId: number;
|
||||||
@ -96,7 +72,7 @@ export const useSdStore = createPersistStore<
|
|||||||
this.updateDraw({
|
this.updateDraw({
|
||||||
...data,
|
...data,
|
||||||
status: "success",
|
status: "success",
|
||||||
img_data: `indexeddb://${StoreKey.SdList}@${data.id}`,
|
img_data: `indexeddb://${StoreKey.File}@${data.id}`,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.updateDraw({
|
this.updateDraw({
|
||||||
|
31
app/utils/file.tsx
Normal file
31
app/utils/file.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"use client";
|
||||||
|
import { initDB } from "react-indexed-db-hook";
|
||||||
|
import { StoreKey } from "@/app/constant";
|
||||||
|
import { useIndexedDB } from "react-indexed-db-hook";
|
||||||
|
|
||||||
|
export const FileDbConfig = {
|
||||||
|
name: "@chatgpt-next-web/file",
|
||||||
|
version: 1,
|
||||||
|
objectStoresMeta: [
|
||||||
|
{
|
||||||
|
store: StoreKey.File,
|
||||||
|
storeConfig: { keyPath: "id", autoIncrement: true },
|
||||||
|
storeSchema: [
|
||||||
|
{ name: "data", keypath: "data", options: { unique: false } },
|
||||||
|
{
|
||||||
|
name: "created_at",
|
||||||
|
keypath: "created_at",
|
||||||
|
options: { unique: false },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export function FileDbInit() {
|
||||||
|
initDB(FileDbConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useFileDB() {
|
||||||
|
return useIndexedDB(StoreKey.File);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user