mirror of
https://github.com/coaidev/coai.git
synced 2025-05-19 04:50:14 +09:00
update sharing page
This commit is contained in:
parent
8e02dae8d3
commit
0f345fbfa4
133
app/src/assets/sharing.less
Normal file
133
app/src/assets/sharing.less
Normal file
@ -0,0 +1,133 @@
|
||||
.sharing-page {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: calc(100vh - 56px);
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.loading {
|
||||
margin: auto;
|
||||
transform: translateY(-28px);
|
||||
user-select: none;
|
||||
|
||||
.loader {
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
animation: spin 1.25s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.error-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
transform: translateY(-28px);
|
||||
color: hsl(var(--text));
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.message {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.sharing-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
width: 80vw;
|
||||
height: 70vh;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
background: hsl(var(--background-container));
|
||||
color: hsl(var(--text));
|
||||
padding: 0.85rem 1rem 0.75rem;
|
||||
align-items: center;
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
flex-shrink: 0;
|
||||
|
||||
img {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 4px;
|
||||
margin-right: 0.75rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 1rem;
|
||||
transform: translateY(-2px);
|
||||
white-space: nowrap;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.time {
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
touch-action: pan-y;
|
||||
padding: 1rem;
|
||||
scrollbar-width: thin;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
padding: 0.75rem 1.5rem 1rem;
|
||||
|
||||
button {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import axios from "axios";
|
||||
|
||||
export const version = "3.4.1";
|
||||
export const version = "3.4.2";
|
||||
export const deploy: boolean = false;
|
||||
export let rest_api: string = "http://localhost:8094";
|
||||
export let ws_api: string = "ws://localhost:8094";
|
||||
|
@ -4,12 +4,6 @@ import { setHistory } from "../store/chat.ts";
|
||||
import { manager } from "./manager.ts";
|
||||
import { AppDispatch } from "../store";
|
||||
|
||||
type SharingForm = {
|
||||
status: boolean;
|
||||
message: string;
|
||||
data: string;
|
||||
}
|
||||
|
||||
export async function updateConversationList(
|
||||
dispatch: AppDispatch,
|
||||
): Promise<void> {
|
||||
@ -42,17 +36,6 @@ export async function deleteConversation(
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function shareConversation(
|
||||
id: number, refs: number[] = [-1],
|
||||
): Promise<SharingForm> {
|
||||
try {
|
||||
const resp = await axios.post("/conversation/share", { id, refs });
|
||||
return resp.data;
|
||||
} catch (e) {
|
||||
return { status: false, message: (e as Error).message, data: "" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function toggleConversation(
|
||||
dispatch: AppDispatch,
|
||||
id: number,
|
||||
|
47
app/src/conversation/sharing.ts
Normal file
47
app/src/conversation/sharing.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import axios from "axios";
|
||||
import {Message} from "./types.ts";
|
||||
|
||||
export type SharingForm = {
|
||||
status: boolean;
|
||||
message: string;
|
||||
data: string;
|
||||
}
|
||||
|
||||
export type ViewData = {
|
||||
name: string;
|
||||
username: string;
|
||||
time: string;
|
||||
messages: Message[];
|
||||
};
|
||||
|
||||
export type ViewForm = {
|
||||
status: boolean;
|
||||
message: string;
|
||||
data: ViewData | null;
|
||||
}
|
||||
|
||||
export async function shareConversation(
|
||||
id: number, refs: number[] = [-1],
|
||||
): Promise<SharingForm> {
|
||||
try {
|
||||
const resp = await axios.post("/conversation/share", { id, refs });
|
||||
return resp.data;
|
||||
} catch (e) {
|
||||
return { status: false, message: (e as Error).message, data: "" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function viewConversation(
|
||||
hash: string,
|
||||
): Promise<ViewForm> {
|
||||
try {
|
||||
const resp = await axios.get(`/conversation/view?hash=${hash}`);
|
||||
return resp.data as ViewForm;
|
||||
} catch (e) {
|
||||
return {
|
||||
status: false,
|
||||
message: (e as Error).message,
|
||||
data: null,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
import { Conversation } from "./conversation.ts";
|
||||
|
||||
export type Message = {
|
||||
role: string;
|
||||
content: string;
|
||||
keyword?: string;
|
||||
quota?: number;
|
||||
role: string;
|
||||
};
|
||||
|
||||
export type Id = number;
|
||||
|
@ -183,6 +183,8 @@ const resources = {
|
||||
failed: "Share failed",
|
||||
copied: "Copied",
|
||||
"copied-description": "Link has been copied to clipboard",
|
||||
"not-found": "Conversation not found",
|
||||
"not-found-description": "Conversation not found, please check if the link is correct or the conversation has been deleted",
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -353,6 +355,8 @@ const resources = {
|
||||
failed: "分享失败",
|
||||
copied: "复制成功",
|
||||
"copied-description": "链接已复制到剪贴板",
|
||||
"not-found": "对话未找到",
|
||||
"not-found-description": "对话未找到,请检查链接是否正确或对话是否已被删除",
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -534,6 +538,8 @@ const resources = {
|
||||
failed: "Поделиться не удалось",
|
||||
copied: "Скопировано",
|
||||
"copied-description": "Ссылка скопирована в буфер обмена",
|
||||
"not-found": "Разговор не найден",
|
||||
"not-found-description": "Разговор не найден, пожалуйста, проверьте, правильная ли ссылка или разговор был удален",
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -3,6 +3,7 @@ import Home from "./routes/Home.tsx";
|
||||
import NotFound from "./routes/NotFound.tsx";
|
||||
import Auth from "./routes/Auth.tsx";
|
||||
import Generation from "./routes/Generation.tsx";
|
||||
import Sharing from "./routes/Sharing.tsx";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@ -22,6 +23,11 @@ const router = createBrowserRouter([
|
||||
path: "/generate",
|
||||
Component: Generation,
|
||||
},
|
||||
{
|
||||
id: "share",
|
||||
path: "/share/:hash",
|
||||
Component: Sharing,
|
||||
}
|
||||
]);
|
||||
|
||||
export default router;
|
||||
|
@ -23,10 +23,11 @@ import type { RootState } from "../store";
|
||||
import { selectAuthenticated, selectInit } from "../store/auth.ts";
|
||||
import { login, supportModels } from "../conf.ts";
|
||||
import {
|
||||
deleteConversation, shareConversation,
|
||||
deleteConversation,
|
||||
toggleConversation,
|
||||
updateConversationList,
|
||||
} from "../conversation/history.ts";
|
||||
import { shareConversation } from "../conversation/sharing.ts";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
filterMessage,
|
||||
|
108
app/src/routes/Sharing.tsx
Normal file
108
app/src/routes/Sharing.tsx
Normal file
@ -0,0 +1,108 @@
|
||||
import "../assets/sharing.less";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {viewConversation, ViewData, ViewForm} from "../conversation/sharing.ts";
|
||||
import {copyClipboard, saveAsFile, useEffectAsync} from "../utils.ts";
|
||||
import {useState} from "react";
|
||||
import {Copy, File, HelpCircle, Loader2, MessagesSquare} from "lucide-react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import MessageSegment from "../components/Message.tsx";
|
||||
import {Button} from "../components/ui/button.tsx";
|
||||
import router from "../router.ts";
|
||||
import {useToast} from "../components/ui/use-toast.ts";
|
||||
|
||||
type SharingFormProps = {
|
||||
refer?: string;
|
||||
data: ViewData | null;
|
||||
}
|
||||
|
||||
function SharingForm({ refer, data }: SharingFormProps) {
|
||||
if (data === null) return null;
|
||||
const { t } = useTranslation();
|
||||
const date = new Date(data.time);
|
||||
const time = `${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}`;
|
||||
const value = JSON.stringify(data, null, 2);
|
||||
const { toast } = useToast();
|
||||
|
||||
return (
|
||||
<div className={`sharing-container`}>
|
||||
<div className={`header`}>
|
||||
<div className={`user`}>
|
||||
<img src={`https://api.deeptrain.net/avatar/${data.username}`} alt="" />
|
||||
<span>{data.username}</span>
|
||||
</div>
|
||||
<div className={`name`}>{data.name}</div>
|
||||
<div className={`time`}>{time}</div>
|
||||
</div>
|
||||
<div className={`body`}>
|
||||
{
|
||||
data.messages.map((message, i) => (
|
||||
<MessageSegment message={message} key={i} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div className={`action`}>
|
||||
<Button variant={`outline`} onClick={async () => {
|
||||
await copyClipboard(value);
|
||||
toast({
|
||||
title: t('share.copied'),
|
||||
});
|
||||
}}>
|
||||
<Copy className={`h-4 w-4 mr-2`} />
|
||||
{t('message.copy')}
|
||||
</Button>
|
||||
<Button variant={`outline`} onClick={() => saveAsFile("conversation.json", value)}>
|
||||
<File className={`h-4 w-4 mr-2`} />
|
||||
{t('message.save')}
|
||||
</Button>
|
||||
<Button variant={`outline`} onClick={async () => {
|
||||
refer && sessionStorage.setItem('refer', refer);
|
||||
await router.navigate('/');
|
||||
}}>
|
||||
<MessagesSquare className={`h-4 w-4 mr-2`} />
|
||||
{t('message.use')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Sharing() {
|
||||
const { t } = useTranslation();
|
||||
const { hash } = useParams();
|
||||
const [setup, setSetup] = useState(false);
|
||||
const [data, setData] = useState<ViewForm | null>(null);
|
||||
|
||||
useEffectAsync(async () => {
|
||||
if (!hash || setup) return;
|
||||
|
||||
setSetup(true);
|
||||
|
||||
const resp = await viewConversation(hash as string);
|
||||
setData(resp);
|
||||
if (!resp.status) console.debug(`[sharing] error: ${resp.message}`);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`sharing-page`}>
|
||||
{
|
||||
data === null ? (
|
||||
<div className={`loading`}>
|
||||
<Loader2 className={`loader w-12 h-12`} />
|
||||
</div>
|
||||
) : (
|
||||
data.status ? (
|
||||
<SharingForm refer={hash} data={data.data} />
|
||||
) : (
|
||||
<div className={`error-container`}>
|
||||
<HelpCircle className={`w-12 h-12 mb-2.5`} />
|
||||
<p className={`title`}>{t('share.not-found')}</p>
|
||||
<p className={`description`}>{t('share.not-found-description')}</p>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Sharing;
|
3
main.go
3
main.go
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"chat/adapter/chatgpt"
|
||||
"chat/addition"
|
||||
"chat/auth"
|
||||
"chat/manager"
|
||||
@ -21,6 +22,8 @@ func main() {
|
||||
app := gin.Default()
|
||||
middleware.RegisterMiddleware(app)
|
||||
|
||||
fmt.Println(chatgpt.FilterKeys("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|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|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|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|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|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|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|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|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|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|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"))
|
||||
return
|
||||
{
|
||||
auth.Register(app)
|
||||
manager.Register(app)
|
||||
|
Loading…
Reference in New Issue
Block a user