From 8e1901f72cca537cd9dc017ae7748a7cf85b6d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E5=87=A1=E4=B8=BB=E5=84=BF?= <15541157+extraordinary-lord@user.noreply.gitee.com> Date: Mon, 20 Oct 2025 21:47:33 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=E6=8E=88=E6=9D=83=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/video-share/x/auth/callback/page.tsx | 35 ++++++++++++-- components/common/ShareModal.tsx | 50 ++++++++++++++++++-- components/pages/create-to-video2.tsx | 4 +- lib/env.ts | 3 -- 4 files changed, 79 insertions(+), 13 deletions(-) diff --git a/app/api/video-share/x/auth/callback/page.tsx b/app/api/video-share/x/auth/callback/page.tsx index 21bc270..6bee4cb 100644 --- a/app/api/video-share/x/auth/callback/page.tsx +++ b/app/api/video-share/x/auth/callback/page.tsx @@ -7,10 +7,37 @@ export default function TwitterAuthCallbackPage() { const router = useRouter(); useEffect(() => { - // 此页面主要是为了 Next.js 路由捕获回调 URL。 - // 在 ShareModal 检测到参数后会打开 TwitterCallbackModal。 - // 重定向到主页面或相关仪表板页面。 - // 参数将由 ShareModal 接收处理。 + // 解析回调参数 + const searchParams = new URLSearchParams(window.location.search); + const state = searchParams.get('state'); + const code = searchParams.get('code'); + + // 将回调参数写入 localStorage,供原窗口读取 + if (state && code) { + try { + const payload = { + state, + code, + timestamp: Date.now(), + }; + localStorage.setItem('twitterAuthCallbackPayload', JSON.stringify(payload)); + // 通过变化一个随机标记键,确保触发其它窗口的 storage 事件 + localStorage.setItem('twitterAuthCallbackFlag', Math.random().toString(36).slice(2)); + } catch { + // 忽略本地存储异常 + } + + // 通过 postMessage 通知打开者(如果存在且同源策略允许) + try { + if (window.opener) { + window.opener.postMessage({ type: 'TWITTER_AUTH_CALLBACK', state, code }, window.location.origin); + } + } catch { + + } + } + + // 跳回应用页面(原窗口会通过 storage 事件弹出处理弹框) router.push('/movies?twitterCallback=true'); }, [router]); diff --git a/components/common/ShareModal.tsx b/components/common/ShareModal.tsx index d7536db..e13e006 100644 --- a/components/common/ShareModal.tsx +++ b/components/common/ShareModal.tsx @@ -103,16 +103,53 @@ export default function ShareModal({ visible, onClose, project }: ShareModalProp const urlParams = new URLSearchParams(window.location.search); const state = urlParams.get('state'); const code = urlParams.get('code'); - - // 检查是否是 Twitter 授权回调 + + // 当前就在回调路径(同窗口授权) if (state && code && window.location.pathname.includes('/api/video-share/x/auth/callback')) { setTwitterCallbackParams({ state, code }); setTwitterCallbackVisible(true); - - // 清理 URL 参数 const newUrl = window.location.pathname; window.history.replaceState({}, document.title, newUrl); + return; } + + // 新窗口授权,回到非回调路径,比如 /movies?twitterCallback=true + const hasFlag = urlParams.get('twitterCallback') === 'true'; + const cached = localStorage.getItem('twitterAuthCallbackPayload'); + if (hasFlag && cached) { + try { + const parsed = JSON.parse(cached); + if (parsed?.state && parsed?.code) { + setTwitterCallbackParams({ state: parsed.state, code: parsed.code }); + setTwitterCallbackVisible(true); + } + } catch { + + } + // 清理 URL 中的 twitterCallback 标记 + const cleanUrl = window.location.pathname; + window.history.replaceState({}, document.title, cleanUrl); + } + + // 监听 storage 事件(跨窗口通知) + const onStorage = (e: StorageEvent) => { + if (e.key === 'twitterAuthCallbackFlag') { + const cachedPayload = localStorage.getItem('twitterAuthCallbackPayload'); + if (cachedPayload) { + try { + const parsed = JSON.parse(cachedPayload); + if (parsed?.state && parsed?.code) { + setTwitterCallbackParams({ state: parsed.state, code: parsed.code }); + setTwitterCallbackVisible(true); + } + } catch { + + } + } + } + }; + window.addEventListener('storage', onStorage); + return () => window.removeEventListener('storage', onStorage); } }, []); @@ -306,6 +343,11 @@ export default function ShareModal({ visible, onClose, project }: ShareModalProp onClose={() => { setTwitterCallbackVisible(false); setTwitterCallbackParams(null); + // 关闭时清理临时缓存 + try { + localStorage.removeItem('twitterAuthCallbackPayload'); + localStorage.removeItem('twitterAuthCallbackFlag'); + } catch {} }} project={project} urlParams={twitterCallbackParams} diff --git a/components/pages/create-to-video2.tsx b/components/pages/create-to-video2.tsx index d2dc400..b31d347 100644 --- a/components/pages/create-to-video2.tsx +++ b/components/pages/create-to-video2.tsx @@ -2,7 +2,7 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import type { MouseEvent } from 'react'; -import { Loader2, Download, Share } from 'lucide-react'; +import { Loader2, Download, Send } from 'lucide-react'; import { useRouter } from 'next/navigation'; import './style/create-to-video2.css'; @@ -382,7 +382,7 @@ export default function CreateToVideo2() { className="w-[2.5rem] h-[2.5rem] rounded-full items-center justify-center p-0 hidden group-hover:flex transition-all duration-300 hover:bg-white/15" onClick={(e) => handleShareClick(e, project)} > - + {/* 下载按钮 */} diff --git a/lib/env.ts b/lib/env.ts index d0f3170..2064994 100644 --- a/lib/env.ts +++ b/lib/env.ts @@ -155,9 +155,6 @@ export const validateEnvConfig = (): { isValid: boolean; errors: string[] } => { errors.push('NEXT_PUBLIC_JAVA_URL is required'); } - if (!baseUrl) { - errors.push('NEXT_PUBLIC_SHARE_API_URL is required'); - } if (!googleClientId) { errors.push('NEXT_PUBLIC_GOOGLE_CLIENT_ID is required'); From 31bb1b1fe3225ca7bb9ae3104d34ca449dd7f77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E5=87=A1=E4=B8=BB=E5=84=BF?= <15541157+extraordinary-lord@user.noreply.gitee.com> Date: Tue, 21 Oct 2025 10:52:59 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=E5=9B=9E=E8=B0=83=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/common/TwitterCallbackModal.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/components/common/TwitterCallbackModal.tsx b/components/common/TwitterCallbackModal.tsx index ddb5157..57f4121 100644 --- a/components/common/TwitterCallbackModal.tsx +++ b/components/common/TwitterCallbackModal.tsx @@ -56,17 +56,12 @@ export default function TwitterCallbackModal({ } // 调用 Twitter 授权回调接口 - const response = await fetch(`${baseUrl}/api/video-share/x/auth/callback`, { + const response = await fetch(`${baseUrl}/api/video-share/x/auth/callback?state=${encodeURIComponent(urlParams.state)}&code=${encodeURIComponent(urlParams.code)}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token') || ''}` - }, - body: JSON.stringify({ - user_id: userId, - state: urlParams.state, - code: urlParams.code, - }) + } }); const data = await response.json();