mirror of
https://github.com/Mail-0/Zero.git
synced 2026-07-01 08:16:28 +00:00
Merge branch 'staging' into fix-back-button
This commit is contained in:
@@ -116,8 +116,9 @@ function SortableNote({
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
className={cn(
|
||||
'bg-background group relative mb-3 overflow-hidden rounded-md border p-3',
|
||||
'group relative mb-3 overflow-hidden rounded-md border border-[#E7E7E7] dark:border-[#252525] p-3',
|
||||
note.isPinned && 'ring-1 ring-amber-200 dark:ring-amber-800',
|
||||
note.color === 'default' ? 'bg-white dark:bg-[#202020]' : '',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@@ -130,16 +131,16 @@ function SortableNote({
|
||||
|
||||
<div className="flex items-start gap-3 pl-1.5">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="whitespace-pre-wrap break-words text-sm">{note.content}</p>
|
||||
<p className="whitespace-pre-wrap break-words text-sm text-black dark:text-white/90">{note.content}</p>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="text-muted-foreground mt-2 flex cursor-default items-center text-xs">
|
||||
<div className="mt-2 flex cursor-default items-center text-xs text-[#8C8C8C]">
|
||||
<Clock className="mr-1 h-3 w-3" />
|
||||
<span>{formatRelativeTime(note.createdAt, format)}</span>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="text-xs">
|
||||
<TooltipContent side="bottom" className="bg-white dark:bg-[#313131]">
|
||||
{note.updatedAt > note.createdAt ? (
|
||||
<>
|
||||
<p>
|
||||
@@ -175,16 +176,16 @@ function SortableNote({
|
||||
<span className="sr-only">More</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={onEdit}>
|
||||
<DropdownMenuContent align="end" className="bg-[#FAFAFA] dark:bg-[#1A1A1A] border-[#E7E7E7] dark:border-[#252525]">
|
||||
<DropdownMenuItem onClick={onEdit} className="text-black dark:text-white/90 focus:bg-white dark:focus:bg-[#202020] focus:text-black dark:focus:text-white">
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
<span>{t('common.notes.actions.edit')}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={onCopy}>
|
||||
<DropdownMenuItem onClick={onCopy} className="text-black dark:text-white/90 focus:bg-white dark:focus:bg-[#202020] focus:text-black dark:focus:text-white">
|
||||
<Copy className="mr-2 h-4 w-4" />
|
||||
<span>{t('common.notes.actions.copy')}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={onTogglePin}>
|
||||
<DropdownMenuItem onClick={onTogglePin} className="text-black dark:text-white/90 focus:bg-white dark:focus:bg-[#202020] focus:text-black dark:focus:text-white">
|
||||
{note.isPinned ? (
|
||||
<>
|
||||
<PinOff className="mr-2 h-4 w-4" />
|
||||
@@ -198,16 +199,20 @@ function SortableNote({
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubTrigger className="text-black dark:text-white/90 focus:bg-white dark:focus:bg-[#202020] focus:text-black dark:focus:text-white">
|
||||
<PaintBucket className="mr-2 h-4 w-4" />
|
||||
<span>{t('common.notes.actions.changeColor')}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent className="w-48">
|
||||
<DropdownMenuSubContent className="w-48 bg-[#FAFAFA] dark:bg-[#1A1A1A] border-[#E7E7E7] dark:border-[#252525]">
|
||||
<DropdownMenuRadioGroup value={note.color} onValueChange={onColorChange}>
|
||||
{NOTE_COLORS.map((color) => {
|
||||
return (
|
||||
<DropdownMenuRadioItem key={color.value} value={color.value}>
|
||||
<DropdownMenuRadioItem
|
||||
key={color.value}
|
||||
value={color.value}
|
||||
className="text-black dark:text-white/90 focus:bg-white dark:focus:bg-[#202020] focus:text-black dark:focus:text-white"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
className={cn(
|
||||
@@ -226,10 +231,10 @@ function SortableNote({
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuSeparator className="bg-[#E7E7E7] dark:bg-[#252525]" />
|
||||
<DropdownMenuItem
|
||||
onClick={onDelete}
|
||||
className="text-destructive focus:text-destructive"
|
||||
className="text-red-600 dark:text-red-400 focus:bg-white dark:focus:bg-[#202020] focus:text-red-600 dark:focus:text-red-400"
|
||||
>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
<span>{t('common.notes.actions.delete')}</span>
|
||||
@@ -348,28 +353,24 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
setEditContent(note.content);
|
||||
};
|
||||
|
||||
const confirmDeleteNote = (noteId: string) => {
|
||||
setNoteToDelete(noteId);
|
||||
setDeleteConfirmOpen(true);
|
||||
const handleDeleteNote = async (noteId: string) => {
|
||||
try {
|
||||
await deleteNote(noteId);
|
||||
await mutate();
|
||||
} catch (error) {
|
||||
console.error('Failed to delete note:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteNote = async () => {
|
||||
if (noteToDelete) {
|
||||
const noteId = noteToDelete;
|
||||
setDeleteConfirmOpen(false);
|
||||
setNoteToDelete(null);
|
||||
|
||||
const promise = async () => {
|
||||
await deleteNote(noteId);
|
||||
await mutate();
|
||||
};
|
||||
|
||||
toast.promise(promise(), {
|
||||
loading: t('common.actions.loading'),
|
||||
success: t('common.notes.noteDeleted'),
|
||||
error: t('common.notes.errors.failedToDeleteNote'),
|
||||
});
|
||||
}
|
||||
const confirmDeleteNote = (noteId: string) => {
|
||||
// TODO: Dialog is bugged? needs to be fixed then implement a confirmation dialog
|
||||
const promise = handleDeleteNote(noteId);
|
||||
toast.promise(promise, {
|
||||
loading: t('common.actions.loading'),
|
||||
success: t('common.notes.noteDeleted'),
|
||||
error: t('common.notes.errors.failedToDeleteNote'),
|
||||
});
|
||||
};
|
||||
|
||||
const handleCopyNote = (content: string) => {
|
||||
@@ -504,14 +505,14 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={cn(
|
||||
'h-8 w-8 rounded-lg p-0 transition-all duration-200',
|
||||
'inline-flex h-7 w-7 items-center justify-center gap-1 overflow-hidden rounded-md bg-white dark:bg-[#313131]',
|
||||
notes.length > 0 && 'text-amber-500',
|
||||
isOpen && 'bg-muted',
|
||||
isOpen && 'bg-white/80 dark:bg-[#313131]/80',
|
||||
)}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<StickyNote
|
||||
className={cn('h-4 w-4', notes.length > 0 && 'fill-amber-200 dark:fill-amber-900')}
|
||||
className={cn('h-4 w-4', notes.length > 0 ? 'fill-amber-200 dark:fill-amber-900' : 'text-[#9A9A9A]')}
|
||||
/>
|
||||
{notes.length > 0 && (
|
||||
<span className="bg-primary text-primary-foreground absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full text-[10px]">
|
||||
@@ -521,18 +522,18 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
<span className="sr-only">{t('common.notes.title')}</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
<TooltipContent side="bottom" className="bg-white dark:bg-[#313131]">
|
||||
<p>{t('common.notes.noteCount', { count: notes.length })}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
{isOpen && (
|
||||
<div
|
||||
className="bg-popover animate-in fade-in-20 zoom-in-95 fixed right-0 top-[5rem] z-50 h-[calc(100vh-5rem)] max-h-[calc(100vh-5rem)] w-full max-w-[100vw] overflow-hidden rounded-t-lg border border-t shadow-md duration-100 sm:absolute sm:right-0 sm:top-full sm:mt-2 sm:h-auto sm:max-h-[80vh] sm:w-[400px] sm:max-w-[90vw] sm:rounded-lg sm:border"
|
||||
className="fixed right-0 top-[5rem] z-50 h-[calc(100vh-5rem)] max-h-[calc(100vh-5rem)] w-full max-w-[100vw] overflow-hidden rounded-t-lg border border-t shadow-lg duration-100 bg-[#FAFAFA] dark:bg-[#1A1A1A] animate-in fade-in-20 zoom-in-95 sm:absolute sm:right-0 sm:top-full sm:mt-2 sm:h-auto sm:max-h-[80vh] sm:w-[400px] sm:max-w-[90vw] sm:rounded-xl sm:border dark:border-[#252525]"
|
||||
onClick={handlePanelClick}
|
||||
>
|
||||
<div className="bg-popover sticky top-0 z-10 flex items-center justify-between border-b p-3">
|
||||
<h3 className="flex items-center text-sm font-medium">
|
||||
<div className="sticky top-0 z-10 flex items-center justify-between border-b border-[#E7E7E7] dark:border-[#252525] p-3">
|
||||
<h3 className="flex items-center text-sm font-medium text-black dark:text-white">
|
||||
<StickyNote className="mr-2 h-4 w-4" />
|
||||
{t('common.notes.title')}{' '}
|
||||
{notes.length > 0 && (
|
||||
@@ -544,23 +545,23 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 w-7 rounded-full p-0"
|
||||
className="h-7 w-7 rounded-md p-0 hover:bg-white/10"
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
<X className="h-4 w-4 fill-[#9A9A9A]" />
|
||||
<span className="sr-only">{t('common.actions.close')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{notes.length > 0 && (
|
||||
<div className="bg-popover sticky top-[49px] z-10 px-3 pb-0 pt-2">
|
||||
<div className="sticky top-[49px] z-10 px-3 pb-0 pt-2">
|
||||
<div className="relative">
|
||||
<Search className="text-muted-foreground absolute left-2 top-2.5 h-4 w-4" />
|
||||
<Search className="absolute left-2 top-2.5 h-4 w-4 text-[#9A9A9A]" />
|
||||
<Input
|
||||
placeholder={t('common.notes.search')}
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-8 text-sm"
|
||||
className="bg-white dark:bg-[#202020] text-sm text-black dark:text-white placeholder:text-[#797979] pl-8 focus:outline-none border-[#E7E7E7] dark:border-[#252525]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -577,15 +578,15 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
<div className="p-3">
|
||||
{notes.length === 0 && !isAddingNewNote ? (
|
||||
<div className="flex flex-col items-center justify-center py-8 text-center">
|
||||
<StickyNote className="text-muted-foreground mb-2 h-12 w-12 opacity-50" />
|
||||
<p className="text-muted-foreground text-sm">{t('common.notes.empty')}</p>
|
||||
<p className="text-muted-foreground mb-4 mt-1 max-w-[80%] text-xs">
|
||||
<StickyNote className="mb-2 h-12 w-12 opacity-50 text-[#8C8C8C]" />
|
||||
<p className="text-sm text-black dark:text-white/90">{t('common.notes.empty')}</p>
|
||||
<p className="mb-4 mt-1 max-w-[80%] text-xs text-[#8C8C8C]">
|
||||
{t('common.notes.emptyDescription')}
|
||||
</p>
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
className="mt-1"
|
||||
className="mt-1 bg-black dark:bg-white text-white dark:text-black hover:bg-black/90 dark:hover:bg-white/90"
|
||||
onClick={() => setIsAddingNewNote(true)}
|
||||
>
|
||||
<PlusCircle className="mr-2 h-4 w-4" />
|
||||
@@ -596,14 +597,14 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
<>
|
||||
{searchQuery && filteredNotes.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-6 text-center">
|
||||
<AlertCircle className="text-muted-foreground mb-2 h-10 w-10 opacity-50" />
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<AlertCircle className="mb-2 h-10 w-10 opacity-50 text-[#8C8C8C]" />
|
||||
<p className="text-sm text-black dark:text-white/90">
|
||||
{t('common.notes.noMatchingNotes', { query: searchQuery })}
|
||||
</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="mt-4"
|
||||
className="mt-4 border-[#E7E7E7] dark:border-[#252525] bg-white dark:bg-[#313131] text-black dark:text-white/90"
|
||||
onClick={() => setSearchQuery('')}
|
||||
>
|
||||
{t('common.notes.clearSearch')}
|
||||
@@ -671,7 +672,7 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
)}
|
||||
|
||||
{isAddingNewNote && (
|
||||
<div className="bg-background relative mb-3 overflow-hidden rounded-md border p-3">
|
||||
<div className="bg-[#FFFFFF] dark:bg-[#202020] relative mb-3 overflow-hidden rounded-md border border-[#E7E7E7] dark:border-[#252525] p-3">
|
||||
<div
|
||||
className={cn(
|
||||
'absolute bottom-0 left-0 top-0 w-1.5 border-l-4',
|
||||
@@ -690,13 +691,13 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
value={newNoteContent}
|
||||
onChange={(e) => setNewNoteContent(e.target.value)}
|
||||
onKeyDown={(e) => handleKeyDown(e, 'add')}
|
||||
className="min-h-[120px] resize-none"
|
||||
className="min-h-[120px] resize-none bg-transparent text-black dark:text-white/90 focus:outline-none border-none"
|
||||
placeholder={t('common.notes.addYourNote')}
|
||||
/>
|
||||
|
||||
<div className="mt-2 flex flex-wrap items-center justify-between gap-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground text-xs">
|
||||
<span className="text-xs text-[#8C8C8C]">
|
||||
{t('common.notes.label')}
|
||||
</span>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
@@ -723,7 +724,7 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
)}
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
<TooltipContent side="bottom" className="bg-white dark:bg-[#313131]">
|
||||
{t(`common.notes.colors.${color.value}` as any)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
@@ -743,6 +744,7 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-[#8C8C8C] hover:bg-white/10 hover:text-[#a0a0a0]"
|
||||
onClick={() => {
|
||||
setIsAddingNewNote(false);
|
||||
setNewNoteContent('');
|
||||
@@ -753,6 +755,7 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
className="bg-black dark:bg-white text-white dark:text-black hover:bg-black/90 dark:hover:bg-white/90"
|
||||
onClick={() => void handleAddNote()}
|
||||
disabled={!newNoteContent.trim()}
|
||||
>
|
||||
@@ -767,7 +770,7 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="mt-1 w-full"
|
||||
className="mt-1 w-full border-[#E7E7E7] dark:border-[#252525] bg-white/5 hover:bg-white/10 dark:text-white/90"
|
||||
onClick={() => setIsAddingNewNote(true)}
|
||||
>
|
||||
<PlusCircle className="mr-2 h-4 w-4" />
|
||||
@@ -781,9 +784,9 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
|
||||
<DragOverlay>
|
||||
{activeId ? (
|
||||
<div className="bg-background rounded-md border p-3 pl-7 shadow-md">
|
||||
<div className="bg-white dark:bg-[#202020] rounded-md border border-[#E7E7E7] dark:border-[#252525] p-3 pl-7 shadow-md">
|
||||
<div className="pl-1.5">
|
||||
<div className="whitespace-pre-wrap break-words text-sm">
|
||||
<div className="whitespace-pre-wrap break-words text-sm text-black dark:text-white/90">
|
||||
{notes.find((n) => n.id === activeId)?.content}
|
||||
</div>
|
||||
</div>
|
||||
@@ -793,9 +796,9 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
</DndContext>
|
||||
|
||||
{editingNoteId && (
|
||||
<div className="bg-muted/20 border-t p-3">
|
||||
<div className="bg-[#FAFAFA] dark:bg-[#1A1A1A] border-t border-[#E7E7E7] dark:border-[#252525] p-3">
|
||||
<div className="space-y-2">
|
||||
<div className="text-muted-foreground mb-1 text-xs font-medium">
|
||||
<div className="mb-1 text-xs font-medium text-[#8C8C8C]">
|
||||
{t('common.notes.editNote')}:
|
||||
</div>
|
||||
<Textarea
|
||||
@@ -803,23 +806,15 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
value={editContent}
|
||||
onChange={(e) => setEditContent(e.target.value)}
|
||||
onKeyDown={(e) => handleKeyDown(e, 'edit')}
|
||||
className="min-h-[100px] resize-none text-sm"
|
||||
className="min-h-[100px] resize-none text-sm bg-[#FFFFFF] dark:bg-[#202020] text-black dark:text-white/90 border-[#E7E7E7] dark:border-[#252525]"
|
||||
placeholder={t('common.notes.addYourNote')}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<div className="text-muted-foreground flex items-center text-xs">
|
||||
<kbd className="bg-muted inline-flex items-center rounded border px-1.5 py-0.5 font-mono text-[10px]">
|
||||
{navigator.platform.includes('Mac') ? '⌘' : 'Ctrl'}+Enter
|
||||
</kbd>
|
||||
<span className="ml-1">{t('common.notes.toSave')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-[#8C8C8C] hover:bg-white/10 hover:text-[#a0a0a0]"
|
||||
onClick={() => {
|
||||
setEditingNoteId(null);
|
||||
setEditContent('');
|
||||
@@ -827,7 +822,12 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
>
|
||||
{t('common.notes.cancel')}
|
||||
</Button>
|
||||
<Button variant="default" size="sm" onClick={() => void handleEditNote()}>
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
className="bg-black dark:bg-white text-white dark:text-black hover:bg-black/90 dark:hover:bg-white/90"
|
||||
onClick={() => void handleEditNote()}
|
||||
>
|
||||
{t('common.actions.saveChanges')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -837,23 +837,6 @@ export function NotesPanel({ threadId }: NotesPanelProps) {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Dialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('common.notes.deleteConfirm')}</DialogTitle>
|
||||
<DialogDescription>{t('common.notes.deleteConfirmDescription')}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setDeleteConfirmOpen(false)}>
|
||||
{t('common.notes.cancel')}
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={() => void handleDeleteNote()}>
|
||||
{t('common.notes.delete')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -483,6 +483,7 @@ export function ThreadDisplay() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<NotesPanel threadId={id} />
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
@@ -546,15 +547,6 @@ export function ThreadDisplay() {
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="bg-white dark:bg-[#313131]">
|
||||
{/* {threadId && (
|
||||
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
|
||||
<StickyNote className="mr-2 h-4 w-4" />
|
||||
<span>{t('common.notes.title')}</span>
|
||||
<div className="absolute right-0 top-0" onClick={(e) => e.stopPropagation()}>
|
||||
<NotesPanel threadId={threadId} />
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
)} */}
|
||||
{/* <DropdownMenuItem onClick={() => setIsFullscreen(!isFullscreen)}>
|
||||
<Expand className="fill-iconLight dark:fill-iconDark mr-2" />
|
||||
<span>
|
||||
|
||||
@@ -44,7 +44,9 @@ export const handleUnsubscribe = async ({ emailData }: { emailData: ParsedMessag
|
||||
name: listUnsubscribeAction.emailAddress,
|
||||
},
|
||||
],
|
||||
subject: listUnsubscribeAction.subject ?? 'Unsubscribe Request',
|
||||
subject: listUnsubscribeAction.subject.trim().length
|
||||
? listUnsubscribeAction.subject
|
||||
: 'Unsubscribe Request',
|
||||
message: 'Zero sent this email to unsubscribe from this mailing list.',
|
||||
});
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user