102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
|
|
"use client";
|
|||
|
|
|
|||
|
|
import { useCallback, useEffect, useState } from "react";
|
|||
|
|
import { Chrome, Loader2 } from "lucide-react";
|
|||
|
|
import { Button } from "@/components/ui/button";
|
|||
|
|
import { notify } from "@/lib/notifications/store";
|
|||
|
|
|
|||
|
|
interface ChromeSessionSyncProps {
|
|||
|
|
onSynced?: () => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function ChromeSessionSync({ onSynced }: ChromeSessionSyncProps) {
|
|||
|
|
const [extensionReady, setExtensionReady] = useState(false);
|
|||
|
|
const [syncing, setSyncing] = useState(false);
|
|||
|
|
const [activeAccountId, setActiveAccountId] = useState<string | null>(null);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
fetch("/api/accounts")
|
|||
|
|
.then((res) => res.json())
|
|||
|
|
.then((data) => setActiveAccountId(data.activeAccountId ?? null))
|
|||
|
|
.catch(() => undefined);
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
const handleMessage = useCallback(
|
|||
|
|
(event: MessageEvent) => {
|
|||
|
|
if (event.source !== window) return;
|
|||
|
|
|
|||
|
|
if (event.data?.type === "HAIXUN_EXTENSION_READY") {
|
|||
|
|
setExtensionReady(true);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (event.data?.type !== "HAIXUN_THREADS_SYNC_RESULT") return;
|
|||
|
|
|
|||
|
|
setSyncing(false);
|
|||
|
|
const data = event.data as {
|
|||
|
|
valid?: boolean;
|
|||
|
|
message?: string;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
notify({
|
|||
|
|
type: data.valid ? "success" : "error",
|
|||
|
|
title: data.valid ? "已同步到 server" : "同步失敗",
|
|||
|
|
message: data.message,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (data.valid) {
|
|||
|
|
window.dispatchEvent(new CustomEvent("haixun:accounts-updated"));
|
|||
|
|
onSynced?.();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
[onSynced]
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
window.addEventListener("message", handleMessage);
|
|||
|
|
window.postMessage({ type: "HAIXUN_PING_EXTENSION" }, "*");
|
|||
|
|
return () => window.removeEventListener("message", handleMessage);
|
|||
|
|
}, [handleMessage]);
|
|||
|
|
|
|||
|
|
function handleSync() {
|
|||
|
|
setSyncing(true);
|
|||
|
|
window.postMessage(
|
|||
|
|
{
|
|||
|
|
type: "HAIXUN_REQUEST_THREADS_SYNC",
|
|||
|
|
serverUrl: window.location.origin,
|
|||
|
|
accountId: activeAccountId ?? undefined,
|
|||
|
|
},
|
|||
|
|
"*"
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
window.setTimeout(() => {
|
|||
|
|
setSyncing((current) => {
|
|||
|
|
if (current) {
|
|||
|
|
notify({
|
|||
|
|
type: "warning",
|
|||
|
|
title: "尚未收到擴充功能回應",
|
|||
|
|
message: "請在 Chrome 安裝並啟用 extension/haixun-threads-sync 擴充功能。",
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
});
|
|||
|
|
}, 12000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="space-y-3">
|
|||
|
|
<p className="text-xs text-muted-foreground">
|
|||
|
|
擴充狀態:
|
|||
|
|
{extensionReady ? (
|
|||
|
|
<span className="text-success">已連線</span>
|
|||
|
|
) : (
|
|||
|
|
<span className="text-warning">未偵測到(請載入 extension/haixun-threads-sync)</span>
|
|||
|
|
)}
|
|||
|
|
</p>
|
|||
|
|
<Button onClick={handleSync} disabled={syncing} className="w-full sm:w-auto">
|
|||
|
|
{syncing ? <Loader2 className="h-4 w-4 animate-spin" /> : <Chrome className="h-4 w-4" />}
|
|||
|
|
{syncing ? "同步中…" : "從 Chrome 同步到目前帳號"}
|
|||
|
|
</Button>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|