Write on the margins of the internet. Powered by the AT Protocol. margin.at
extension web atproto comments
at main 116 lines 3.9 kB view raw
1import React, { useState } from "react"; 2import { Button } from "../ui"; 3import { ExternalLink, Shield } from "lucide-react"; 4import { addSkippedHostname } from "../../store/preferences"; 5 6interface ExternalLinkModalProps { 7 isOpen: boolean; 8 onClose: () => void; 9 url: string | null; 10} 11 12export default function ExternalLinkModal({ 13 isOpen, 14 onClose, 15 url, 16}: ExternalLinkModalProps) { 17 const [dontAskAgain, setDontAskAgain] = useState(false); 18 19 if (!isOpen || !url) return null; 20 21 const displayUrl = url.split("#:~:text=")[0]; 22 23 const handleContinue = () => { 24 if (dontAskAgain && url) { 25 try { 26 const hostname = new URL(url).hostname; 27 addSkippedHostname(hostname); 28 } catch (e) { 29 console.error("Invalid URL", e); 30 } 31 } 32 window.open(url, "_blank", "noopener,noreferrer"); 33 onClose(); 34 }; 35 36 const hostname = (() => { 37 try { 38 return new URL(url).hostname; 39 } catch { 40 return "this site"; 41 } 42 })(); 43 44 return ( 45 <div 46 className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm animate-fade-in" 47 onClick={onClose} 48 > 49 <div 50 className="bg-white dark:bg-surface-900 rounded-xl shadow-2xl max-w-md w-full animate-scale-in ring-1 ring-surface-200 dark:ring-surface-700 overflow-hidden" 51 onClick={(e) => e.stopPropagation()} 52 > 53 <div className="px-6 pt-6 pb-4"> 54 <div className="flex items-start gap-3"> 55 <div className="w-9 h-9 bg-primary-100 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400 rounded-lg flex items-center justify-center flex-shrink-0 mt-0.5"> 56 <Shield size={18} /> 57 </div> 58 <div className="min-w-0"> 59 <h2 className="text-base font-semibold text-surface-900 dark:text-white"> 60 Leaving Margin 61 </h2> 62 <p className="text-sm text-surface-500 dark:text-surface-400 mt-1"> 63 You're about to visit an external site. 64 </p> 65 </div> 66 </div> 67 68 <div className="mt-4 flex items-center gap-2 bg-surface-50 dark:bg-surface-800/60 border border-surface-200 dark:border-surface-700 rounded-lg px-3 py-2.5"> 69 <ExternalLink 70 size={14} 71 className="text-surface-400 dark:text-surface-500 flex-shrink-0" 72 /> 73 <span className="text-sm text-surface-700 dark:text-surface-300 break-all line-clamp-2"> 74 {displayUrl} 75 </span> 76 </div> 77 </div> 78 79 <div className="px-6 pb-5 pt-2 flex flex-col gap-3"> 80 <label className="flex items-center gap-2 cursor-pointer select-none group"> 81 <input 82 type="checkbox" 83 checked={dontAskAgain} 84 onChange={(e) => setDontAskAgain(e.target.checked)} 85 className="rounded border-surface-300 dark:border-surface-600 text-primary-600 focus:ring-primary-500 w-3.5 h-3.5 cursor-pointer" 86 /> 87 <span className="text-xs text-surface-500 dark:text-surface-400 group-hover:text-surface-600 dark:group-hover:text-surface-300 transition-colors"> 88 Always allow links to{" "} 89 <span className="font-medium text-surface-700 dark:text-surface-200"> 90 {hostname} 91 </span> 92 </span> 93 </label> 94 95 <div className="flex gap-2"> 96 <Button 97 onClick={onClose} 98 variant="ghost" 99 className="flex-1 justify-center" 100 > 101 Cancel 102 </Button> 103 <Button 104 onClick={handleContinue} 105 variant="primary" 106 className="flex-1 justify-center" 107 icon={<ExternalLink size={14} />} 108 > 109 Open Link 110 </Button> 111 </div> 112 </div> 113 </div> 114 </div> 115 ); 116}