refactor: Business logic and dependencies updates

- 核心服务代码更新 (db, server, auth, proxy)
- Agent 相关模块更新 (mindspace, experience)
- 前端组件和 hooks 更新
- 数据库 schema 更新
- 依赖版本更新
This commit is contained in:
john
2026-06-27 08:25:02 +08:00
parent f1220a7905
commit 25f8223253
20 changed files with 1458 additions and 116 deletions
+77 -1
View File
@@ -13,6 +13,7 @@ import {
publishPageToPlaza,
redactMindSpacePage,
updateMindSpacePage,
updatePublicationStatus,
} from '../api/client';
import type {
MindSpacePage,
@@ -288,6 +289,8 @@ export function MindSpacePageDetail({
pageTitle: string;
pushedToPlaza: boolean;
} | null>(null);
const [confirmPublicationStatusOpen, setConfirmPublicationStatusOpen] = useState(false);
const [statusConfirming, setStatusConfirming] = useState(false);
const applyPageRecord = useCallback(
(next: MindSpacePage, options?: { recordHistory?: boolean }) => {
@@ -334,6 +337,13 @@ export function MindSpacePageDetail({
? await getMindSpacePublicationStats(next.publication.id).catch(() => null)
: null,
);
if (
next.publication &&
next.publication.accessMode === 'public' &&
next.publication.userConfirmedAt === null
) {
setConfirmPublicationStatusOpen(true);
}
} catch (err) {
setError(err instanceof Error ? err.message : '页面加载失败');
} finally {
@@ -434,6 +444,34 @@ export function MindSpacePageDetail({
}
};
const confirmPublicationStatus = async (nextAccessMode: AccessMode) => {
if (!page || !page.publication) return;
setStatusConfirming(true);
try {
const nextExpiresAt =
nextAccessMode === 'public'
? null
: nextAccessMode === 'time_limited'
? Date.now() + 30 * 60 * 1000
: null;
const updated = await updatePublicationStatus(page.publication.id, {
accessMode: nextAccessMode,
expiresAt: nextExpiresAt,
});
setPage({ ...page, publication: updated });
setAccessMode(updated.accessMode);
setExpiresAt(
updated.expiresAt ? localDateTimeValue(updated.expiresAt) : '',
);
setConfirmPublicationStatusOpen(false);
setError(null);
} catch (err) {
setError(err instanceof Error ? err.message : '更新发布状态失败');
} finally {
setStatusConfirming(false);
}
};
const offlineFromSuccess = async () => {
if (!publishSuccess) return;
setPublishing(true);
@@ -1395,7 +1433,13 @@ export function MindSpacePageDetail({
))}
</section>
</section>
{sharePayload ? <ShareSheet payload={sharePayload} onClose={() => setSharePayload(null)} /> : null}
{sharePayload ? (
<ShareSheet
payload={sharePayload}
publication={page?.publication}
onClose={() => setSharePayload(null)}
/>
) : null}
{fullscreenPreviewOpen && page && page.contentFormat === 'html' ? (
<MindSpacePageFullscreenPreview
@@ -1414,6 +1458,38 @@ export function MindSpacePageDetail({
onContentChange={handlePreviewContentChange}
/>
) : null}
{confirmPublicationStatusOpen && page?.publication ? (
<MindSpaceModal className="mindspace-confirm-publication-status-modal">
<div className="mindspace-modal-content">
<h3></h3>
<p> 30 </p>
<div className="mindspace-modal-actions">
<button
className="mindspace-secondary"
disabled={statusConfirming}
onClick={() => confirmPublicationStatus('private')}
>
{statusConfirming ? '处理中...' : '改为私有'}
</button>
<button
className="mindspace-secondary"
disabled={statusConfirming}
onClick={() => confirmPublicationStatus('time_limited')}
>
{statusConfirming ? '处理中...' : '再预览 30 分钟'}
</button>
<button
className="mindspace-primary"
disabled={statusConfirming}
onClick={() => confirmPublicationStatus('public')}
>
{statusConfirming ? '处理中...' : '发布为永久公开'}
</button>
</div>
</div>
</MindSpaceModal>
) : null}
</>
);
}
+35 -1
View File
@@ -1,4 +1,5 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import type { MindSpacePublication } from '../types';
import {
SHARE_CHANNELS,
canUseNativeShare,
@@ -10,13 +11,37 @@ import {
export function ShareSheet({
payload,
publication,
onClose,
}: {
payload: SharePayload;
publication?: MindSpacePublication | null;
onClose: () => void;
}) {
const [message, setMessage] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
const [expiresInMinutes, setExpiresInMinutes] = useState<number | null>(null);
useEffect(() => {
if (!publication || publication.accessMode !== 'public' || publication.userConfirmedAt !== null) {
setExpiresInMinutes(null);
return;
}
if (!publication.expiresAt) {
setExpiresInMinutes(null);
return;
}
const update = () => {
const now = Date.now();
const remaining = Math.ceil((publication.expiresAt! - now) / (1000 * 60));
setExpiresInMinutes(Math.max(0, remaining));
};
update();
const interval = setInterval(update, 10000);
return () => clearInterval(interval);
}, [publication]);
const handleChannel = async (channel: ShareChannel) => {
setBusy(true);
@@ -60,6 +85,15 @@ export function ShareSheet({
<p className="share-sheet-eyebrow">SHARE</p>
<h2 id="share-sheet-title"></h2>
<p className="share-sheet-subtitle">{payload.title}</p>
{expiresInMinutes !== null && (
<p className="share-sheet-preview-notice">
{' '}
<strong>
{expiresInMinutes > 0 ? `${expiresInMinutes} 分钟` : '不到 1 分钟'}
</strong>
</p>
)}
</div>
<button type="button" className="share-sheet-close" onClick={onClose} aria-label="关闭">
×