import { Button } from '@/components/ui/Button'; import { Card } from '@/components/ui/Card'; import { toast } from '@/components/ui/Toast'; import { getApiV1BaseUrl } from '@/lib/api-url'; import { useNavigate } from '@solidjs/router'; import { createSignal, onMount, Show } from 'solid-js'; const API_BASE_URL = getApiV1BaseUrl(); const PENDING_SHARE_KEY = 'trackeep_pending_share_target'; interface PendingSharePayload { title: string; text: string; url: string; } function extractFirstUrl(text: string): string | null { const match = text.match(/https?:\/\/[^\s<>"')\]]+/i); if (!match) { return null; } return match[0].replace(/[),.;!?]+$/, ''); } function normalizeUrl(url: string): string | null { const trimmed = url.trim(); if (!trimmed) { return null; } try { return new URL(trimmed).toString(); } catch { return null; } } function deriveTitle(url: string, providedTitle: string): string { const title = providedTitle.trim(); if (title) { return title; } try { return new URL(url).hostname.replace(/^www\./, ''); } catch { return 'Shared Link'; } } function isGenericTitle(title: string, url: string): boolean { const normalized = title.trim().toLowerCase(); if (!normalized) { return true; } if (normalized === 'shared link' || normalized === 'youtube video') { return true; } try { const host = new URL(url).hostname.toLowerCase().replace(/^www\./, ''); return normalized === host; } catch { return false; } } function deriveDescription(sharedText: string, url: string): string { const cleaned = sharedText.replace(url, '').trim(); return cleaned.slice(0, 1200); } function looksLikeYouTube(url: string): boolean { try { const host = new URL(url).hostname.toLowerCase(); return host.includes('youtube.com') || host.includes('youtu.be'); } catch { return false; } } function writePendingShare(payload: PendingSharePayload): void { sessionStorage.setItem(PENDING_SHARE_KEY, JSON.stringify(payload)); } function readPendingShare(): PendingSharePayload | null { const raw = sessionStorage.getItem(PENDING_SHARE_KEY); if (!raw) { return null; } try { const parsed = JSON.parse(raw) as Partial; return { title: typeof parsed.title === 'string' ? parsed.title : '', text: typeof parsed.text === 'string' ? parsed.text : '', url: typeof parsed.url === 'string' ? parsed.url : '', }; } catch { return null; } } function clearPendingShare(): void { sessionStorage.removeItem(PENDING_SHARE_KEY); } export const ShareTarget = () => { const navigate = useNavigate(); const [status, setStatus] = createSignal<'processing' | 'success' | 'error' | 'auth'>('processing'); const [message, setMessage] = createSignal('Saving shared content to Trackeep...'); onMount(async () => { const params = new URLSearchParams(window.location.search); const incomingPayload: PendingSharePayload = { title: params.get('title') || '', text: params.get('text') || '', url: params.get('url') || '', }; const hasIncomingPayload = Boolean(incomingPayload.title || incomingPayload.text || incomingPayload.url); if (hasIncomingPayload) { writePendingShare(incomingPayload); } const payload = hasIncomingPayload ? incomingPayload : readPendingShare(); if (!payload) { setStatus('error'); setMessage('No shared content detected.'); return; } const token = localStorage.getItem('trackeep_token') || localStorage.getItem('token'); if (!token) { setStatus('auth'); setMessage('Sign in required. Redirecting to login...'); toast.info('Sign in required', 'Please sign in to save shared content.'); navigate('/login?next=%2Fshare-target', { replace: true }); return; } const rawUrl = payload.url || extractFirstUrl(payload.text) || ''; const normalizedUrl = normalizeUrl(rawUrl); if (!normalizedUrl) { setStatus('error'); setMessage('No valid URL found in shared content.'); toast.error('Share failed', 'No valid URL found in shared payload.'); clearPendingShare(); return; } let title = deriveTitle(normalizedUrl, payload.title); let description = deriveDescription(payload.text, normalizedUrl); try { const metadataResponse = await fetch(`${API_BASE_URL}/bookmarks/metadata`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ url: normalizedUrl }), }); if (metadataResponse.ok) { const metadata = await metadataResponse .json() .catch(() => ({})) as { title?: string; description?: string }; if ( typeof metadata.title === 'string' && metadata.title.trim().length > 0 && isGenericTitle(title, normalizedUrl) ) { title = metadata.title.trim(); } if ( typeof metadata.description === 'string' && metadata.description.trim().length > 0 && description.trim().length < 12 ) { description = metadata.description.trim(); } } } catch { // metadata enrichment is best-effort } try { const response = await fetch(`${API_BASE_URL}/bookmarks`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ title, url: normalizedUrl, description, }), }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error || `Failed with status ${response.status}`); } clearPendingShare(); setStatus('success'); const destination = looksLikeYouTube(normalizedUrl) ? '/app/youtube' : '/app/bookmarks'; setMessage( looksLikeYouTube(normalizedUrl) ? 'Saved successfully. Redirecting to YouTube...' : 'Saved successfully. Redirecting to bookmarks...', ); toast.success('Saved to Trackeep', 'Shared link was added to bookmarks.'); setTimeout(() => { navigate(destination, { replace: true }); }, 600); } catch (error) { const text = error instanceof Error ? error.message : 'Failed to save shared content'; setStatus('error'); setMessage(text); toast.error('Share failed', text); } }); return (

Trackeep Share Target

{message()}

); };