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');