Add 'Add to contacts' feature

Toolbar More menu now saves sender to Contact Manager with green
success toast or grey 'Already in contacts' toast for duplicates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Devon
2026-05-25 10:19:48 -04:00
parent 22aca3cfd1
commit f7873d14c7
5 changed files with 51 additions and 96 deletions
+33 -4
View File
@@ -2896,11 +2896,19 @@ function OnboardingWizard({accountId,onComplete,C}){
// Email Toolbar
function EmailToolbar({email,folder,C,onReply,onReplyAll,onForward,onArchive,onDelete,onJunk,onStar,onUnread,onPrint}){
function EmailToolbar({email,folder,C,onReply,onReplyAll,onForward,onArchive,onDelete,onJunk,onStar,onUnread,onPrint,onAddToContacts}){
const[moreOpen,setMore]=useState(false);
const[addedToast,setAddedToast]=useState(null); // null | "added" | "exists"
const isSent=folder==="sent"||folder==="drafts"||folder==="out";
function handleAddToContacts(){
setMore(false);
const result=onAddToContacts?.({name:email.from,email:email.from_email,av:email.av,avColor:email.avColor});
setAddedToast(result||"added");
setTimeout(()=>setAddedToast(null),3000);
}
return <div style={{display:"flex",alignItems:"center",gap:2,padding:"6px 14px",
borderBottom:`1px solid ${C.border}`,background:C.surface,flexShrink:0,flexWrap:"wrap"}}>
borderBottom:`1px solid ${C.border}`,background:C.surface,flexShrink:0,flexWrap:"wrap",position:"relative"}}>
{!isSent&&<>
<IBtn icon="ti-arrow-back-up" title="Reply" C={C} onClick={onReply} label="Reply"/>
<IBtn icon="ti-arrows-left" title="Reply All" C={C} onClick={onReplyAll} label="Reply All"/>
@@ -2922,7 +2930,7 @@ function EmailToolbar({email,folder,C,onReply,onReplyAll,onForward,onArchive,onD
{[["ti-alert-triangle","Mark as junk",()=>{onJunk();setMore(false);}],
["ti-copy","Copy to folder",()=>setMore(false)],
["ti-code","View source",()=>setMore(false)],
["ti-user-plus","Add to contacts",()=>setMore(false)]].map(([ic,lbl,fn])=>(
["ti-user-plus","Add to contacts",handleAddToContacts]].map(([ic,lbl,fn])=>(
<button key={lbl} onClick={fn} style={{display:"flex",alignItems:"center",gap:9,
width:"100%",padding:"9px 14px",background:"transparent",border:"none",
cursor:"pointer",fontFamily:FONT,fontSize:13,color:C.text,textAlign:"left"}}
@@ -2933,6 +2941,16 @@ function EmailToolbar({email,folder,C,onReply,onReplyAll,onForward,onArchive,onD
))}
</div>}
</div>
{addedToast&&<div style={{position:"absolute",top:44,right:14,zIndex:60,
display:"flex",alignItems:"center",gap:8,padding:"8px 14px",borderRadius:8,
background:addedToast==="added"?"#166534":"#374151",
boxShadow:"0 4px 16px rgba(0,0,0,0.25)",animation:"loginIn 0.2s ease both"}}>
<i className={`ti ${addedToast==="added"?"ti-user-check":"ti-user-x"}`}
style={{fontSize:15,color:addedToast==="added"?"#86efac":"#9ca3af"}} aria-hidden="true"/>
<span style={{fontSize:13,color:"#fff",fontFamily:FONT,whiteSpace:"nowrap"}}>
{addedToast==="added"?"Contact added successfully":"Already in contacts"}
</span>
</div>}
</div>;
}
@@ -3694,6 +3712,16 @@ function EmailApp({activeProfile,profiles,setActiveProfile,saveProfile,loginBg,s
function doDelete(){if(selEmail){setEmails(p=>p.map(e=>e.id===selEmail.id?{...e,folder:"trash"}:e));setSelEmail(null);}}
function doJunk(){if(selEmail){setEmails(p=>p.map(e=>e.id===selEmail.id?{...e,folder:"trash"}:e));setSelEmail(null);}}
function doStar(){if(selEmail)setEmails(p=>p.map(e=>e.id===selEmail.id?{...e,starred:!e.starred}:e));}
function doAddToContacts(data){
if(!data?.email)return"added";
const exists=contacts.some(c=>c.email?.toLowerCase()===data.email.toLowerCase());
if(!exists){
setContacts(p=>[...p,{id:"ct"+uid(),name:data.name||data.email,email:data.email,
av:data.av||data.email.slice(0,2).toUpperCase(),avColor:data.avColor||"#0078d4",phone:"",address:""}]);
return"added";
}
return"exists";
}
function doUnread(){if(selEmail){setEmails(p=>p.map(e=>e.id===selEmail.id?{...e,read:false}:e));setSelEmail(null);}}
function openReply(mode){
if(!selEmail)return;
@@ -4029,7 +4057,8 @@ function EmailApp({activeProfile,profiles,setActiveProfile,saveProfile,loginBg,s
<EmailToolbar email={selEmail} folder={selFolder} C={C}
onReply={()=>openReply("reply")} onReplyAll={()=>openReply("replyAll")}
onForward={()=>openReply("forward")} onArchive={doArchive} onDelete={doDelete}
onJunk={doJunk} onStar={doStar} onUnread={doUnread} onPrint={()=>printEmail(selEmail)}/>
onJunk={doJunk} onStar={doStar} onUnread={doUnread} onPrint={()=>printEmail(selEmail)}
onAddToContacts={doAddToContacts}/>
<div style={{padding:"16px 22px 14px",borderBottom:`1px solid ${bgDark?"rgba(255,255,255,0.12)":C.border}`,background:bgDark?"rgba(0,0,0,0.25)":"rgba(255,255,255,0.7)",flexShrink:0,backdropFilter:bgIsImage(bg)?"blur(12px)":"none"}}>
<h2 style={{margin:"0 0 12px",fontSize:17,fontWeight:600,color:readingTextColor,lineHeight:1.3}}>
{selEmail.subject}