Check component props for untranslated literals, and fail on a finding
i18n-literals checked title, aria-label, placeholder and alt on elements, but not the same English passed to a component, so a MenuItem label or a Popover ariaLabel written as a literal went through. It also excused a literal that happened to be a catalogue key. That exemption is meant for English held in a constant and translated where it renders, and a literal written straight into a JSX attribute has no such render site. No component passes its props through t(). And neither half of i18n:check was run with --check, so a finding printed and the script still exited 0. Component props are checked now, a key no longer excuses a literal in an attribute, and both scripts run with --check. That found 28 strings rendering English in every language: 19 already had keys and are wrapped, and 9 are new keys in all nine catalogues. The contact editor's Save and Saving… buttons are wrapped as well, on the same line.
This commit is contained in:
@@ -1214,6 +1214,15 @@ export const catalog: Catalog = {
|
||||
"Resize panes": "Bereichsgröße ändern",
|
||||
"Resize message list": "Größe der Nachrichtenliste ändern",
|
||||
"Resize sidebar": "Größe der Seitenleiste ändern",
|
||||
"Hide from list": "Aus der Liste ausblenden",
|
||||
"Show in list": "In der Liste einblenden",
|
||||
"Stop trusting sender images": "Bildern des Absenders nicht mehr vertrauen",
|
||||
"Always show images from sender": "Bilder dieses Absenders immer anzeigen",
|
||||
"Collapse all": "Alle einklappen",
|
||||
"Expand all": "Alle ausklappen",
|
||||
"Send now instead": "Stattdessen jetzt senden",
|
||||
"Switch to plain text": "Zu Nur-Text wechseln",
|
||||
"Switch to rich text": "Zu formatiertem Text wechseln",
|
||||
"QR code": "QR-Code",
|
||||
"Draft discarded": "Entwurf verworfen",
|
||||
"Draft saved": "Entwurf gespeichert",
|
||||
|
||||
@@ -1187,6 +1187,15 @@ export const catalog: Catalog = {
|
||||
"Resize panes": "Cambiar el tamaño de los paneles",
|
||||
"Resize message list": "Cambiar el tamaño de la lista de mensajes",
|
||||
"Resize sidebar": "Cambiar el tamaño de la barra lateral",
|
||||
"Hide from list": "Ocultar de la lista",
|
||||
"Show in list": "Mostrar en la lista",
|
||||
"Stop trusting sender images": "Dejar de confiar en las imágenes del remitente",
|
||||
"Always show images from sender": "Mostrar siempre las imágenes del remitente",
|
||||
"Collapse all": "Contraer todo",
|
||||
"Expand all": "Expandir todo",
|
||||
"Send now instead": "Enviar ahora, sin programar",
|
||||
"Switch to plain text": "Cambiar a texto sin formato",
|
||||
"Switch to rich text": "Cambiar a texto enriquecido",
|
||||
"QR code": "Código QR",
|
||||
"Draft discarded": "Borrador descartado",
|
||||
"Draft saved": "Borrador guardado",
|
||||
|
||||
@@ -1192,6 +1192,15 @@ export const catalog: Catalog = {
|
||||
"Resize panes": "Redimensionner les volets",
|
||||
"Resize message list": "Redimensionner la liste des messages",
|
||||
"Resize sidebar": "Redimensionner la barre latérale",
|
||||
"Hide from list": "Masquer de la liste",
|
||||
"Show in list": "Afficher dans la liste",
|
||||
"Stop trusting sender images": "Ne plus faire confiance aux images de l'expéditeur",
|
||||
"Always show images from sender": "Toujours afficher les images de l'expéditeur",
|
||||
"Collapse all": "Tout réduire",
|
||||
"Expand all": "Tout développer",
|
||||
"Send now instead": "Envoyer tout de suite",
|
||||
"Switch to plain text": "Passer en texte brut",
|
||||
"Switch to rich text": "Passer en texte enrichi",
|
||||
"QR code": "QR code",
|
||||
"Draft discarded": "Brouillon abandonné",
|
||||
"Draft saved": "Brouillon enregistré",
|
||||
|
||||
@@ -1195,6 +1195,15 @@ export const catalog: Catalog = {
|
||||
"Resize panes": "ペインの大きさを変更",
|
||||
"Resize message list": "メール一覧の大きさを変更",
|
||||
"Resize sidebar": "サイドバーの大きさを変更",
|
||||
"Hide from list": "一覧で非表示にする",
|
||||
"Show in list": "一覧に表示する",
|
||||
"Stop trusting sender images": "差出人の画像を信頼しない",
|
||||
"Always show images from sender": "差出人の画像を常に表示",
|
||||
"Collapse all": "すべて折りたたむ",
|
||||
"Expand all": "すべて展開",
|
||||
"Send now instead": "予約をやめて今すぐ送信",
|
||||
"Switch to plain text": "プレーンテキストに切り替え",
|
||||
"Switch to rich text": "リッチテキストに切り替え",
|
||||
"QR code": "QR コード",
|
||||
"Draft discarded": "下書きを破棄しました",
|
||||
"Draft saved": "下書きを保存しました",
|
||||
|
||||
@@ -1183,6 +1183,15 @@ export const catalog: Catalog = {
|
||||
"Resize panes": "Vensterdelen verslepen",
|
||||
"Resize message list": "Grootte van de berichtenlijst wijzigen",
|
||||
"Resize sidebar": "Grootte van de zijbalk wijzigen",
|
||||
"Hide from list": "Verbergen in de lijst",
|
||||
"Show in list": "Tonen in de lijst",
|
||||
"Stop trusting sender images": "Afbeeldingen van afzender niet meer vertrouwen",
|
||||
"Always show images from sender": "Afbeeldingen van afzender altijd tonen",
|
||||
"Collapse all": "Alles samenvouwen",
|
||||
"Expand all": "Alles uitvouwen",
|
||||
"Send now instead": "Toch nu verzenden",
|
||||
"Switch to plain text": "Overschakelen naar platte tekst",
|
||||
"Switch to rich text": "Overschakelen naar opgemaakte tekst",
|
||||
"QR code": "QR-code",
|
||||
"Draft discarded": "Concept weggegooid",
|
||||
"Draft saved": "Concept opgeslagen",
|
||||
|
||||
@@ -1190,6 +1190,15 @@ export const catalog: Catalog = {
|
||||
"Resize panes": "Redimensionar os painéis",
|
||||
"Resize message list": "Redimensionar a lista de mensagens",
|
||||
"Resize sidebar": "Redimensionar a barra lateral",
|
||||
"Hide from list": "Ocultar da lista",
|
||||
"Show in list": "Mostrar na lista",
|
||||
"Stop trusting sender images": "Deixar de confiar nas imagens do remetente",
|
||||
"Always show images from sender": "Sempre mostrar as imagens do remetente",
|
||||
"Collapse all": "Recolher tudo",
|
||||
"Expand all": "Expandir tudo",
|
||||
"Send now instead": "Enviar agora mesmo",
|
||||
"Switch to plain text": "Mudar para texto simples",
|
||||
"Switch to rich text": "Mudar para texto formatado",
|
||||
"QR code": "Código QR",
|
||||
"Draft discarded": "Rascunho descartado",
|
||||
"Draft saved": "Rascunho salvo",
|
||||
|
||||
@@ -1189,6 +1189,15 @@ export const catalog: Catalog = {
|
||||
"Resize panes": "Изменить размер панелей",
|
||||
"Resize message list": "Изменить размер списка писем",
|
||||
"Resize sidebar": "Изменить размер боковой панели",
|
||||
"Hide from list": "Скрыть из списка",
|
||||
"Show in list": "Показать в списке",
|
||||
"Stop trusting sender images": "Больше не доверять изображениям отправителя",
|
||||
"Always show images from sender": "Всегда показывать изображения отправителя",
|
||||
"Collapse all": "Свернуть все",
|
||||
"Expand all": "Развернуть все",
|
||||
"Send now instead": "Отправить сейчас",
|
||||
"Switch to plain text": "Переключиться на обычный текст",
|
||||
"Switch to rich text": "Переключиться на форматированный текст",
|
||||
"QR code": "QR-код",
|
||||
"Draft discarded": "Черновик удалён",
|
||||
"Draft saved": "Черновик сохранён",
|
||||
|
||||
@@ -1183,6 +1183,15 @@ export const catalog: Catalog = {
|
||||
"Resize panes": "Змінити розмір панелей",
|
||||
"Resize message list": "Змінити розмір списку листів",
|
||||
"Resize sidebar": "Змінити розмір бічної панелі",
|
||||
"Hide from list": "Сховати зі списку",
|
||||
"Show in list": "Показати в списку",
|
||||
"Stop trusting sender images": "Більше не довіряти зображенням відправника",
|
||||
"Always show images from sender": "Завжди показувати зображення відправника",
|
||||
"Collapse all": "Згорнути все",
|
||||
"Expand all": "Розгорнути все",
|
||||
"Send now instead": "Надіслати зараз",
|
||||
"Switch to plain text": "Перейти на звичайний текст",
|
||||
"Switch to rich text": "Перейти на форматований текст",
|
||||
"QR code": "QR-код",
|
||||
"Draft discarded": "Чернетку видалено",
|
||||
"Draft saved": "Чернетку збережено",
|
||||
|
||||
@@ -1194,6 +1194,15 @@ export const catalog: Catalog = {
|
||||
"Resize panes": "调整窗格大小",
|
||||
"Resize message list": "调整邮件列表大小",
|
||||
"Resize sidebar": "调整侧边栏大小",
|
||||
"Hide from list": "从列表中隐藏",
|
||||
"Show in list": "在列表中显示",
|
||||
"Stop trusting sender images": "不再信任发件人的图片",
|
||||
"Always show images from sender": "始终显示该发件人的图片",
|
||||
"Collapse all": "全部折叠",
|
||||
"Expand all": "全部展开",
|
||||
"Send now instead": "改为立即发送",
|
||||
"Switch to plain text": "切换为纯文本",
|
||||
"Switch to rich text": "切换为富文本",
|
||||
"QR code": "二维码",
|
||||
"Draft discarded": "草稿已放弃",
|
||||
"Draft saved": "草稿已保存",
|
||||
|
||||
@@ -239,7 +239,7 @@ export function DateField({ value, onChange, className, disabled, required, id,
|
||||
<CalIcon size={15} />
|
||||
</button>
|
||||
{anchor && (
|
||||
<Popover anchor={anchor} onClose={() => { setAnchor(null); inputRef.current?.focus(); }} role="dialog" className="dp-pop" closeOnClick={false} ariaLabel="Choose a date">
|
||||
<Popover anchor={anchor} onClose={() => { setAnchor(null); inputRef.current?.focus(); }} role="dialog" className="dp-pop" closeOnClick={false} ariaLabel={translate("Choose a date")}>
|
||||
<CalendarGrid
|
||||
selected={selected}
|
||||
onClose={() => { setAnchor(null); inputRef.current?.focus(); }}
|
||||
@@ -335,7 +335,7 @@ export function DateTimeField({ value, onChange, className, disabled, required,
|
||||
<span className="dp-open" aria-hidden="true"><Clock size={15} /></span>
|
||||
</span>
|
||||
{anchor && (
|
||||
<Popover anchor={anchor} onClose={close} role="dialog" className="dp-pop dp-pop-wide" closeOnClick={false} ariaLabel="Choose a date and time">
|
||||
<Popover anchor={anchor} onClose={close} role="dialog" className="dp-pop dp-pop-wide" closeOnClick={false} ariaLabel={translate("Choose a date and time")}>
|
||||
<div className="dp-split">
|
||||
<CalendarGrid
|
||||
selected={valid}
|
||||
|
||||
@@ -289,7 +289,7 @@ export function Composer({ draft }: { draft: Draft }) {
|
||||
<button className="btn btn-primary" onClick={sendMenu.open} aria-label={translate("Send options")}><ChevronDown size={16} /></button>
|
||||
</span>
|
||||
<Popover anchor={sendMenu.anchor} onClose={sendMenu.close} side="top" width={280}>
|
||||
<MenuItem icon={<Send size={16} />} label={d.sendAt !== null ? "Send now instead" : "Send"} kbd={d.sendAt !== null ? undefined : "Ctrl+↵"} onClick={() => { if (d.sendAt !== null) patch({ sendAt: null }); sendMenu.close(); void doSend(); }} />
|
||||
<MenuItem icon={<Send size={16} />} label={d.sendAt !== null ? translate("Send now instead") : translate("Send")} kbd={d.sendAt !== null ? undefined : "Ctrl+↵"} onClick={() => { if (d.sendAt !== null) patch({ sendAt: null }); sendMenu.close(); void doSend(); }} />
|
||||
<MenuItem icon={<Clock size={16} />} label={translate("Undo window: {seconds}s", { seconds: settings.undoSendSeconds })} onClick={() => updateSettings({ undoSendSeconds: settings.undoSendSeconds >= 30 ? 0 : settings.undoSendSeconds + 5 })} />
|
||||
{canSchedule && <ScheduleMenuItems maxMs={scheduleMax} onPick={scheduleFor} onCustom={() => { sendMenu.close(); setScheduleOpen(true); }} />}
|
||||
</Popover>
|
||||
@@ -323,7 +323,7 @@ export function Composer({ draft }: { draft: Draft }) {
|
||||
</Popover>
|
||||
<button className="icon-btn" onClick={moreMenu.open} aria-label={translate("More options")}><MoreVertical size={18} /></button>
|
||||
<Popover anchor={moreMenu.anchor} onClose={moreMenu.close} side="top" width={260}>
|
||||
<MenuItem icon={<Type size={16} />} label={d.format === "html" ? "Switch to plain text" : "Switch to rich text"} onClick={toggleFormat} />
|
||||
<MenuItem icon={<Type size={16} />} label={d.format === "html" ? translate("Switch to plain text") : translate("Switch to rich text")} onClick={toggleFormat} />
|
||||
<MenuItem icon={<CheckCheck size={16} />} label={translate("Request read receipt")} checked={d.requestReceipt} onClick={() => patch({ requestReceipt: !d.requestReceipt })} />
|
||||
<MenuSep />
|
||||
<MenuTitle>{translate("Priority")}</MenuTitle>
|
||||
|
||||
@@ -156,7 +156,7 @@ export function ContactEditor({ card, defaultBookId, onClose, onSaved }: Props)
|
||||
const photoSrc = photo?.dataUrl ?? (!removePhoto && existingPhoto ? (existingPhoto.uri?.startsWith("data:") ? existingPhoto.uri : existingPhoto.blobId ? client.downloadUrl(contacts.accountId!, existingPhoto.blobId, "photo", existingPhoto.mediaType ?? "image/jpeg", true) : null) : null);
|
||||
|
||||
return (
|
||||
<Dialog open onClose={onClose} title={isNew ? "New contact" : `Edit ${contactDisplayName(card as ContactCard)}`} size="lg" footer={<><button className="btn" onClick={onClose}>{t("Cancel")}</button><button className="btn btn-primary" disabled={busy} onClick={() => void save()}>{busy ? "Saving…" : "Save"}</button></>}>
|
||||
<Dialog open onClose={onClose} title={isNew ? t("New contact") : `Edit ${contactDisplayName(card as ContactCard)}`} size="lg" footer={<><button className="btn" onClick={onClose}>{t("Cancel")}</button><button className="btn btn-primary" disabled={busy} onClick={() => void save()}>{busy ? t("Saving…") : t("Save")}</button></>}>
|
||||
<div className="contact-form">
|
||||
<div className="row" style={{ gap: 16, marginBottom: 12 }}>
|
||||
<label className="avatar xl" style={{ background: "var(--bg-sunken)", color: "var(--fg-muted)", cursor: "pointer", position: "relative" }} title={t("Change photo")}>
|
||||
|
||||
@@ -471,7 +471,7 @@ function MailboxMenu({ mailbox: m, onClose, onCreateChild, onShare }: { mailbox:
|
||||
)}
|
||||
<MenuItem icon={<FolderPlus size={16} />} label={t("New subfolder")} onClick={onCreateChild} disabled={!m.myRights.mayCreateChild} />
|
||||
<MenuItem icon={<Pencil size={16} />} label={t("Rename")} onClick={() => void rename()} disabled={isSpecial || !m.myRights.mayRename} />
|
||||
<MenuItem icon={m.isSubscribed ? <EyeOff size={16} /> : <Eye size={16} />} label={m.isSubscribed ? "Hide from list" : "Show in list"} onClick={() => void useMail.getState().updateMailbox(m.id, { isSubscribed: !m.isSubscribed })} disabled={m.role === "inbox"} />
|
||||
<MenuItem icon={m.isSubscribed ? <EyeOff size={16} /> : <Eye size={16} />} label={m.isSubscribed ? t("Hide from list") : t("Show in list")} onClick={() => void useMail.getState().updateMailbox(m.id, { isSubscribed: !m.isSubscribed })} disabled={m.role === "inbox"} />
|
||||
{/* Sharing a mail folder is withdrawn, not removed: Stalwart accepts and
|
||||
stores the share, and it never reaches the other account -- its own
|
||||
docs list calendars, address books and files as shareable and not mail
|
||||
|
||||
@@ -299,7 +299,7 @@ export function MessageList({ title, list, openThreadId, openMessageId, focusId,
|
||||
<Popover anchor={selMenu.anchor} onClose={selMenu.close} align="end" width={240}>
|
||||
<MenuItem
|
||||
icon={mailbox?.role === "junk" ? <ShieldCheck size={16} /> : <AlertOctagon size={16} />}
|
||||
label={mailbox?.role === "junk" ? "Not spam" : "Report spam"}
|
||||
label={mailbox?.role === "junk" ? t("Not spam") : t("Report spam")}
|
||||
onClick={() => void actions.spam()}
|
||||
/>
|
||||
<MenuItem icon={<Mail size={16} />} label={t("Mark as unread")} onClick={() => void actions.read(false)} />
|
||||
@@ -532,10 +532,10 @@ export function MessageList({ title, list, openThreadId, openMessageId, focusId,
|
||||
<MenuItem icon={<CalendarRange size={16} />} label={archiveDateLabel("year")} onClick={() => void useMail.getState().archiveByDate(ctxTargets, "year")} />
|
||||
<MenuItem icon={<CalendarDays size={16} />} label={archiveDateLabel("month")} onClick={() => void useMail.getState().archiveByDate(ctxTargets, "month")} />
|
||||
<MenuItem icon={<Trash2 size={16} />} label={t("Delete")} kbd="#" onClick={() => void actions.trash(ctxTargets)} />
|
||||
<MenuItem icon={<AlertOctagon size={16} />} label={mailbox?.role === "junk" ? "Not spam" : "Report spam"} kbd="!" onClick={() => void actions.spam(ctxTargets)} />
|
||||
<MenuItem icon={<AlertOctagon size={16} />} label={mailbox?.role === "junk" ? t("Not spam") : t("Report spam")} kbd="!" onClick={() => void actions.spam(ctxTargets)} />
|
||||
<MenuSep />
|
||||
<MenuItem icon={someUnread ? <MailOpen size={16} /> : <Mail size={16} />} label={someUnread ? "Mark as read" : "Mark as unread"} onClick={() => void actions.read(someUnread, ctxTargets)} />
|
||||
<MenuItem icon={<Star size={16} />} label={someUnstarred ? "Add star" : "Remove star"} kbd="s" onClick={() => void actions.star(someUnstarred, ctxTargets)} />
|
||||
<MenuItem icon={someUnread ? <MailOpen size={16} /> : <Mail size={16} />} label={someUnread ? t("Mark as read") : t("Mark as unread")} onClick={() => void actions.read(someUnread, ctxTargets)} />
|
||||
<MenuItem icon={<Star size={16} />} label={someUnstarred ? t("Add star") : t("Remove star")} kbd="s" onClick={() => void actions.star(someUnstarred, ctxTargets)} />
|
||||
<MenuItem icon={<FolderInput size={16} />} label={t("Move to…")} kbd="v" onClick={() => actions.move(ctxTargets)} />
|
||||
<MenuItem icon={<Tag size={16} />} label={t("Label…")} kbd="l" onClick={() => actions.label(ctxTargets, ctxMenu.anchor ?? { x: 0, y: 0 })} />
|
||||
<MenuSep />
|
||||
|
||||
@@ -338,7 +338,7 @@ export const MessageView = memo(function MessageView({ email: e, expanded, wasUn
|
||||
the other three rather than down among the read-only actions. */}
|
||||
<MenuItem icon={<MailPlus size={16} />} label={translate("Compose as new")} onClick={() => void useCompose.getState().composeAsNew(e)} />
|
||||
<MenuSep />
|
||||
<MenuItem icon={<Mail size={16} />} label={e.keywords.$seen ? "Mark as unread" : "Mark as read"} onClick={() => void useMail.getState().markRead([e.id], !e.keywords.$seen)} />
|
||||
<MenuItem icon={<Mail size={16} />} label={e.keywords.$seen ? translate("Mark as unread") : translate("Mark as read")} onClick={() => void useMail.getState().markRead([e.id], !e.keywords.$seen)} />
|
||||
<MenuItem icon={<Trash2 size={16} />} label={translate("Delete this message")} onClick={() => void useMail.getState().trash([e.id])} />
|
||||
<MenuSep />
|
||||
<MenuItem icon={<Eye size={16} />} label={translate("Show original")} onClick={() => void openSource()} />
|
||||
@@ -351,7 +351,7 @@ export const MessageView = memo(function MessageView({ email: e, expanded, wasUn
|
||||
{from && (
|
||||
<>
|
||||
<MenuSep />
|
||||
<MenuItem icon={<Ban size={16} />} label={senderTrusted ? "Stop trusting sender images" : "Always show images from sender"} onClick={() => updateSettings({ trustedImageSenders: senderTrusted ? settings.trustedImageSenders.filter((x) => x !== from.email.toLowerCase()) : [...settings.trustedImageSenders, from.email.toLowerCase()] })} />
|
||||
<MenuItem icon={<Ban size={16} />} label={senderTrusted ? translate("Stop trusting sender images") : translate("Always show images from sender")} onClick={() => updateSettings({ trustedImageSenders: senderTrusted ? settings.trustedImageSenders.filter((x) => x !== from.email.toLowerCase()) : [...settings.trustedImageSenders, from.email.toLowerCase()] })} />
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
|
||||
@@ -245,17 +245,17 @@ export function ThreadView({ threadId, mailboxId, onBack, actions, onNavigate, h
|
||||
<ArrowLeft size={20} />
|
||||
</button>
|
||||
<button className="icon-btn" title={t("Archive (e)")} onClick={() => void actions.archive(rowIds)}><Archive size={19} /></button>
|
||||
<button className="icon-btn" title={inJunk ? "Not spam" : "Report spam (!)"} onClick={() => void actions.spam(rowIds)}>{inJunk ? <ShieldCheck size={19} /> : <AlertOctagon size={19} />}</button>
|
||||
<button className="icon-btn" title={inJunk ? t("Not spam") : t("Report spam (!)")} onClick={() => void actions.spam(rowIds)}>{inJunk ? <ShieldCheck size={19} /> : <AlertOctagon size={19} />}</button>
|
||||
<button className="icon-btn" title={t("Delete (#)")} onClick={() => void actions.trash(rowIds)}><Trash2 size={19} /></button>
|
||||
<span className="tb-sep hide-mobile" />
|
||||
<button className="icon-btn hide-mobile" title={anyUnread ? "Mark as read" : "Mark as unread"} onClick={() => void actions.read(anyUnread, rowIds)}>{anyUnread ? <MailOpen size={19} /> : <Mail size={19} />}</button>
|
||||
<button className="icon-btn hide-mobile" title={anyUnread ? t("Mark as read") : t("Mark as unread")} onClick={() => void actions.read(anyUnread, rowIds)}>{anyUnread ? <MailOpen size={19} /> : <Mail size={19} />}</button>
|
||||
<button className="icon-btn hide-mobile" title={t("Move to (v)")} onClick={() => actions.move(rowIds)}><FolderInput size={19} /></button>
|
||||
<button className="icon-btn hide-mobile" title={t("Labels (l)")} onClick={(e) => setLabelAnchor({ x: e.clientX, y: e.clientY })}><Tag size={19} /></button>
|
||||
<button className="icon-btn" onClick={moreMenu.open} aria-label={t("More")}><MoreVertical size={19} /></button>
|
||||
<Popover anchor={moreMenu.anchor} onClose={moreMenu.close} align="start" width={240}>
|
||||
<MenuItem icon={<Star size={16} />} label={anyStarred ? "Remove star" : "Add star"} onClick={() => void actions.star(!anyStarred, rowIds)} />
|
||||
<MenuItem icon={<Star size={16} />} label={anyStarred ? t("Remove star") : t("Add star")} onClick={() => void actions.star(!anyStarred, rowIds)} />
|
||||
<MenuItem icon={<Tag size={16} />} label={t("Label…")} onClick={() => setLabelAnchor({ x: window.innerWidth / 2, y: 100 })} />
|
||||
<MenuItem icon={allExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />} label={allExpanded ? "Collapse all" : "Expand all"} onClick={() => { setAllExpanded((v) => !v); setExpanded({}); }} />
|
||||
<MenuItem icon={allExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />} label={allExpanded ? t("Collapse all") : t("Expand all")} onClick={() => { setAllExpanded((v) => !v); setExpanded({}); }} />
|
||||
<MenuSep />
|
||||
<MenuItem icon={<Printer size={16} />} label={t("Print conversation")} onClick={() => window.print()} />
|
||||
{last && accountId && (
|
||||
|
||||
Reference in New Issue
Block a user