146 lines
4.0 KiB
TypeScript
146 lines
4.0 KiB
TypeScript
import { Check, Copy, MessageCircle, Music2 } from 'lucide-react';
|
||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||
|
||
import { copyTextToClipboard } from '../../services/clipboard';
|
||
import {
|
||
buildPublishShareText,
|
||
type PublishShareModalPayload,
|
||
} from './publishShareModalModel';
|
||
import { UnifiedModal } from './UnifiedModal';
|
||
|
||
type PublishShareModalProps = {
|
||
open: boolean;
|
||
payload: PublishShareModalPayload | null;
|
||
onClose: () => void;
|
||
};
|
||
|
||
const SHARE_CHANNELS = [
|
||
{
|
||
id: 'wechat',
|
||
label: '微信',
|
||
icon: MessageCircle,
|
||
className: 'bg-emerald-500 text-white',
|
||
},
|
||
{
|
||
id: 'qq',
|
||
label: 'QQ',
|
||
icon: MessageCircle,
|
||
className: 'bg-sky-500 text-white',
|
||
},
|
||
{
|
||
id: 'douyin',
|
||
label: '抖音',
|
||
icon: Music2,
|
||
className: 'bg-slate-950 text-white',
|
||
},
|
||
] as const;
|
||
|
||
/**
|
||
* 发布完成后的分享弹窗。
|
||
* 目前各渠道先统一复制分享文本,后续如接入微信/QQ/抖音 SDK,可以只替换这里的渠道点击逻辑。
|
||
*/
|
||
export function PublishShareModal({
|
||
open,
|
||
payload,
|
||
onClose,
|
||
}: PublishShareModalProps) {
|
||
const [copyState, setCopyState] = useState<'idle' | 'copied' | 'failed'>(
|
||
'idle',
|
||
);
|
||
const resetTimerRef = useRef<number | null>(null);
|
||
const shareText = useMemo(
|
||
() => (payload ? buildPublishShareText(payload) : ''),
|
||
[payload],
|
||
);
|
||
|
||
useEffect(
|
||
() => () => {
|
||
if (resetTimerRef.current !== null) {
|
||
window.clearTimeout(resetTimerRef.current);
|
||
}
|
||
},
|
||
[],
|
||
);
|
||
|
||
useEffect(() => {
|
||
setCopyState('idle');
|
||
}, [payload?.publicWorkCode]);
|
||
|
||
const copyShareText = () => {
|
||
if (!shareText) {
|
||
return;
|
||
}
|
||
|
||
void copyTextToClipboard(shareText).then((copied) => {
|
||
setCopyState(copied ? 'copied' : 'failed');
|
||
if (resetTimerRef.current !== null) {
|
||
window.clearTimeout(resetTimerRef.current);
|
||
}
|
||
resetTimerRef.current = window.setTimeout(() => {
|
||
resetTimerRef.current = null;
|
||
setCopyState('idle');
|
||
}, 1400);
|
||
});
|
||
};
|
||
|
||
return (
|
||
<UnifiedModal
|
||
open={open && Boolean(payload)}
|
||
title="分享给朋友"
|
||
onClose={onClose}
|
||
size="sm"
|
||
panelClassName="platform-remap-surface"
|
||
bodyClassName="space-y-4 px-4 py-4 sm:px-5 sm:py-5"
|
||
footerClassName="justify-center border-t-0 px-4 pb-5 pt-0 sm:px-5"
|
||
footer={
|
||
<div className="grid w-full grid-cols-3 gap-3">
|
||
{SHARE_CHANNELS.map((channel) => {
|
||
const Icon = channel.icon;
|
||
|
||
return (
|
||
<button
|
||
key={channel.id}
|
||
type="button"
|
||
onClick={copyShareText}
|
||
className="flex min-w-0 flex-col items-center gap-2 rounded-[1rem] px-2 py-2.5 text-xs font-bold text-[var(--platform-text-base)] transition hover:bg-white/62"
|
||
aria-label={`分享到${channel.label}`}
|
||
title={channel.label}
|
||
>
|
||
<span
|
||
className={`inline-flex h-11 w-11 items-center justify-center rounded-full shadow-sm ${channel.className}`}
|
||
>
|
||
<Icon className="h-5 w-5" />
|
||
</span>
|
||
<span>{channel.label}</span>
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
}
|
||
>
|
||
<div className="rounded-[1.25rem] border border-[var(--platform-subpanel-border)] bg-white/72 p-4">
|
||
<div className="whitespace-pre-wrap break-words text-sm leading-6 text-[var(--platform-text-strong)]">
|
||
{shareText}
|
||
</div>
|
||
</div>
|
||
<button
|
||
type="button"
|
||
onClick={copyShareText}
|
||
disabled={!shareText}
|
||
className="platform-button platform-button--primary w-full justify-center gap-2 disabled:cursor-not-allowed disabled:opacity-55"
|
||
>
|
||
{copyState === 'copied' ? (
|
||
<Check className="h-4 w-4" />
|
||
) : (
|
||
<Copy className="h-4 w-4" />
|
||
)}
|
||
{copyState === 'copied'
|
||
? '已复制'
|
||
: copyState === 'failed'
|
||
? '复制失败'
|
||
: '分享'}
|
||
</button>
|
||
</UnifiedModal>
|
||
);
|
||
}
|