62 lines
3.5 KiB
TypeScript
62 lines
3.5 KiB
TypeScript
|
|
"use client";
|
|||
|
|
|
|||
|
|
import { useEffect, useState } from "react";
|
|||
|
|
import { CheckCircle2, Loader2, Puzzle, RefreshCw } from "lucide-react";
|
|||
|
|
import { Button } from "@/components/ui/button";
|
|||
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|||
|
|
|
|||
|
|
type SyncResult = { success?: boolean; valid?: boolean; message?: string; username?: string };
|
|||
|
|
|
|||
|
|
export function ExtensionSyncCard() {
|
|||
|
|
const [ready, setReady] = useState(false);
|
|||
|
|
const [syncing, setSyncing] = useState(false);
|
|||
|
|
const [result, setResult] = useState<SyncResult | null>(null);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
const onMessage = (event: MessageEvent) => {
|
|||
|
|
if (event.source !== window) return;
|
|||
|
|
if (event.data?.type === "HAIXUN_EXTENSION_READY") setReady(true);
|
|||
|
|
if (event.data?.type === "HAIXUN_THREADS_SYNC_RESULT") {
|
|||
|
|
setSyncing(false);
|
|||
|
|
setResult(event.data);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
window.addEventListener("message", onMessage);
|
|||
|
|
window.postMessage({ type: "HAIXUN_PING_EXTENSION" }, "*");
|
|||
|
|
return () => window.removeEventListener("message", onMessage);
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
function sync() {
|
|||
|
|
setSyncing(true);
|
|||
|
|
setResult(null);
|
|||
|
|
window.postMessage({ type: "HAIXUN_REQUEST_THREADS_SYNC", serverUrl: window.location.origin }, "*");
|
|||
|
|
window.setTimeout(() => setSyncing(false), 15000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<Card id="extension" className="overflow-hidden border-primary/20">
|
|||
|
|
<CardHeader>
|
|||
|
|
<div className="flex items-start justify-between gap-4">
|
|||
|
|
<div>
|
|||
|
|
<CardTitle className="flex items-center gap-2 text-base"><Puzzle className="h-4 w-4 text-primary" />Threads Extension</CardTitle>
|
|||
|
|
<CardDescription>所有 Threads 抓取統一使用 Chrome Extension,不需要官方 API。</CardDescription>
|
|||
|
|
</div>
|
|||
|
|
<span className={`rounded-full px-2.5 py-1 text-xs font-medium ${ready ? "bg-success-bg text-success" : "bg-muted text-muted-foreground"}`}>
|
|||
|
|
{ready ? "已偵測" : "未偵測"}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
</CardHeader>
|
|||
|
|
<CardContent className="space-y-4">
|
|||
|
|
<div className="grid gap-3 text-sm sm:grid-cols-3">
|
|||
|
|
<p className="rounded-xl bg-muted/70 p-3"><b className="block text-foreground">1. 登入 Threads</b><span className="text-muted-foreground">在同一個 Chrome 視窗登入</span></p>
|
|||
|
|
<p className="rounded-xl bg-muted/70 p-3"><b className="block text-foreground">2. 同步 Session</b><span className="text-muted-foreground">不會儲存你的密碼</span></p>
|
|||
|
|
<p className="rounded-xl bg-muted/70 p-3"><b className="block text-foreground">3. 開始海巡</b><span className="text-muted-foreground">海巡與找 TA 共用</span></p>
|
|||
|
|
</div>
|
|||
|
|
{result && <p className={`rounded-xl border p-3 text-sm ${result.success ? "border-success-border bg-success-bg text-success" : "border-danger-border bg-danger-bg text-destructive"}`}>{result.success && <CheckCircle2 className="mr-2 inline h-4 w-4" />}{result.username ? `@${result.username} · ` : ""}{result.message ?? (result.success ? "同步成功" : "同步失敗")}</p>}
|
|||
|
|
<Button onClick={sync} disabled={!ready || syncing}>{syncing ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}{syncing ? "同步中…" : "同步 Threads Session"}</Button>
|
|||
|
|
{!ready && <p className="text-xs text-muted-foreground">請先載入 <code className="rounded bg-muted px-1">extension/haixun-threads-sync</code>,並在擴充功能選項填入這個網站網址。</p>}
|
|||
|
|
</CardContent>
|
|||
|
|
</Card>
|
|||
|
|
);
|
|||
|
|
}
|