/** * ShareModal — shows a shareable project link and visibility toggle. */ import React, { useState } from 'react'; import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; import { useProjectStore } from '../../store/useProjectStore'; import { updateProject } from '../../services/projectService'; interface ShareModalProps { onClose: () => void; } export const ShareModal: React.FC = ({ onClose }) => { const { t } = useTranslation(); const currentProject = useProjectStore((s) => s.currentProject); const setVisibility = useProjectStore((s) => s.setVisibility); const [copied, setCopied] = useState(false); const [toggling, setToggling] = useState(false); if (!currentProject) return null; const shareUrl = `${window.location.origin}/project/${currentProject.id}`; const isPublic = currentProject.isPublic; const handleCopy = () => { navigator.clipboard.writeText(shareUrl).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); }); }; const handleToggleVisibility = async () => { setToggling(true); try { await updateProject(currentProject.id, { is_public: !isPublic }); setVisibility(!isPublic); } catch { // Silently fail — user can retry } finally { setToggling(false); } }; return createPortal(
e.stopPropagation()}>

{t('editor.share.title')}

{/* Visibility toggle */}
{isPublic ? ( ) : ( )} {isPublic ? t('editor.share.public') : t('editor.share.private')} {isPublic ? t('editor.share.publicHint') : t('editor.share.privateHint')}
{/* Share link */}
(e.target as HTMLInputElement).select()} />
{!isPublic && (
{t('editor.share.privateWarning')}
)}
, document.body, ); }; const styles: Record = { overlay: { position: 'fixed', inset: 0, background: 'rgba(0,0,0,.6)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000, }, modal: { background: '#252526', border: '1px solid #3c3c3c', borderRadius: 8, padding: '1.75rem', width: 440, display: 'flex', flexDirection: 'column', gap: 16, }, title: { color: '#ccc', margin: 0, fontSize: 18, fontWeight: 600 }, visibilityRow: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, padding: '10px 12px', background: '#1e1e1e', border: '1px solid #333', borderRadius: 6, }, visibilityInfo: { display: 'flex', flexDirection: 'column', gap: 4, }, toggleBtn: { background: 'transparent', border: '1px solid #555', borderRadius: 4, color: '#ccc', padding: '6px 12px', fontSize: 12, cursor: 'pointer', whiteSpace: 'nowrap', flexShrink: 0, }, linkRow: { display: 'flex', gap: 6 }, linkInput: { flex: 1, background: '#1e1e1e', border: '1px solid #444', borderRadius: 4, padding: '8px 10px', color: '#4fc3f7', fontSize: 13, fontFamily: 'monospace', outline: 'none', }, copyBtn: { background: '#0e639c', border: 'none', borderRadius: 4, color: '#fff', padding: '8px 16px', fontSize: 13, cursor: 'pointer', fontWeight: 500, display: 'flex', alignItems: 'center', }, warning: { background: '#3d2e00', border: '1px solid #f59e0b44', borderRadius: 4, color: '#f59e0b', padding: '8px 12px', fontSize: 12, }, actions: { display: 'flex', justifyContent: 'flex-end' }, closeBtn: { background: 'transparent', border: '1px solid #555', borderRadius: 4, color: '#ccc', padding: '8px 16px', fontSize: 13, cursor: 'pointer', }, };