diff --git a/front/src/App.tsx b/front/src/App.tsx index 031d624..2b31d25 100644 --- a/front/src/App.tsx +++ b/front/src/App.tsx @@ -1,14 +1,15 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; -import { tokens, useToastController, Toast, ToastTitle } from '@fluentui/react-components'; +import { tokens, useToastController, Toast, ToastTitle, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, Button, Input, Text } from '@fluentui/react-components'; import { MainLayout } from './layouts/MainLayout'; import About from './components/About'; import CreatePost from './components/CreatePost'; import PostCard from './components/PostCard'; import ImageViewer from './components/ImageViewer'; -import { fetchArticles, type Article } from './api'; +import { fetchArticles, reset_identity_token, type Article } from './api'; import { useLayout } from './context/LayoutContext'; import './App.css'; +import { Eye24Regular, EyeOff24Regular, Copy24Regular } from '@fluentui/react-icons'; const Home: React.FC<{ onPreviewImage: (src: string, alt?: string) => void }> = ({ onPreviewImage }) => { const { refreshTrigger } = useLayout(); @@ -154,6 +155,19 @@ function App() { setImageViewer({ open: true, src, alt }); }; const closeImageViewer = () => setImageViewer({ open: false }); + const [identityDialogOpen, setIdentityDialogOpen] = useState(false); + const [identityToken, setIdentityToken] = useState(''); + const [identityVisible, setIdentityVisible] = useState(false); + + useEffect(() => { + const handler = () => { + setIdentityToken(localStorage.getItem('identity_token') || ''); + setIdentityVisible(false); + setIdentityDialogOpen(true); + }; + window.addEventListener('identity_invalid', handler as EventListener); + return () => window.removeEventListener('identity_invalid', handler as EventListener); + }, []); return ( @@ -162,10 +176,70 @@ function App() { path="/" element={ - ) : null + overlays={ + <> + {imageViewer.open && imageViewer.src ? ( + + ) : null} + setIdentityDialogOpen(!!data.open)}> + + + Identity_token无效,是否重置? + + + 注意:重置Identity_token后,你将以一个全新的匿名身份继续使用本站,你将无法再修改或删除使用旧 identity 发布的内容! + +
+ 当前Identity_token: +
+ +
+
+
+ + + + +
+
+
+ } /> } diff --git a/front/src/api.ts b/front/src/api.ts index d1d73a7..25373d6 100644 --- a/front/src/api.ts +++ b/front/src/api.ts @@ -117,6 +117,26 @@ export const get_id_token = async (): Promise => { return token; }; +const notifyInvalidIdentity = () => { + if (typeof window !== 'undefined') { + window.dispatchEvent(new CustomEvent('identity_invalid')); + } +}; + +const handlePostApiCode = (json: any) => { + if (json && json.code === 2004) { + notifyInvalidIdentity(); + throw new Error(json.data || 'Identity token invalid'); + } +}; + +export const reset_identity_token = async (): Promise => { + const token = await fetch_id_token(); + localStorage.removeItem('identity_token'); + localStorage.setItem('identity_token', token); + return token; +}; + export interface CreatePostResponse { code: number; data: any; @@ -156,7 +176,9 @@ export const createPost = async (content: string): Promise = throw new Error(`HTTP error! status: ${response.status}`); } - return await response.json(); + const json = await response.json(); + handlePostApiCode(json); + return json; } catch (error) { console.error('Failed to create post:', error); throw error; @@ -286,6 +308,7 @@ export const postComment = async (commentData: PostCommentRequest): Promise => { throw new Error(`HTTP error! status: ${response.status}`); } const json = await response.json(); + handlePostApiCode(json); if (json.code === 1001 && typeof json.data === 'string') { return json.data; } diff --git a/front/src/layouts/MainLayout.tsx b/front/src/layouts/MainLayout.tsx index 5dd7aa8..94ac58e 100644 --- a/front/src/layouts/MainLayout.tsx +++ b/front/src/layouts/MainLayout.tsx @@ -52,7 +52,7 @@ const useStyles = makeStyles({ } }); -const LayoutContent = ({ toasterId, imageViewer }: { toasterId: string; imageViewer?: ReactNode }) => { +const LayoutContent = ({ toasterId, overlays }: { toasterId: string; overlays?: ReactNode }) => { const styles = useStyles(); const { isDarkMode } = useLayout(); @@ -71,17 +71,17 @@ const LayoutContent = ({ toasterId, imageViewer }: { toasterId: string; imageVie