From 3f054f0ae9148c57f5e5c651630645b25d3d46bf 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] =?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');