mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-02 00:06:23 +00:00
feat: show typed, coloured file icons on the reader's attachment chips
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
// List chips showed a typed, coloured icon per file; the reader's chips a
|
||||
// grey paperclip for everything (webmail a78d03a18 uses the list's icons in
|
||||
// the reader too).
|
||||
|
||||
const src = (p: string) => fs.readFileSync(path.resolve(__dirname, '..', p), 'utf8');
|
||||
|
||||
describe('reader attachment chip icons', () => {
|
||||
it('draws the same typed icon and colour as the list chips', () => {
|
||||
const reader = src('email/AttachmentChips.tsx');
|
||||
expect(reader).not.toMatch(/<Paperclip /);
|
||||
expect(reader).toMatch(/const kind = attachmentKind\(item\.type, item\.name\);/);
|
||||
expect(reader).toMatch(/<Icon size=\{14\} color=\{attachmentIconColor\(kind, c\)\} \/>/);
|
||||
const list = src('email/ListAttachmentChips.tsx');
|
||||
expect(list).toMatch(/ATTACHMENT_ICONS\[kind\]/);
|
||||
expect(list).toMatch(/attachmentIconColor\(kind, c\)/);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet, Pressable, ActivityIndicator, Alert } from 'react-native';
|
||||
import { Paperclip, Download, Eye, ExternalLink, Save, Share2, FolderArchive, FolderOpen } from '../icons';
|
||||
import { Download, Eye, ExternalLink, Save, Share2, FolderArchive, FolderOpen } from '../icons';
|
||||
import type { Attachment, Email } from '../../api/types';
|
||||
import { spacing, radius, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
getAttachmentDisplayName, visibleAttachments, formatSize,
|
||||
} from '../../lib/attachment-display';
|
||||
import { emailExportFilename } from '../../lib/download-filename';
|
||||
import { attachmentKind } from '../../lib/list-attachments';
|
||||
import { ATTACHMENT_ICONS, attachmentIconColor } from './attachment-icons';
|
||||
import { ActionSheet, type ActionSheetItem } from './ActionSheet';
|
||||
import { AttachmentPreviewModal } from './AttachmentPreviewModal';
|
||||
import type { ExtractedAttachment } from './use-body-override';
|
||||
@@ -232,6 +234,9 @@ export function AttachmentChips({ email, jmapAccountId, calendarBannerShown, tne
|
||||
<View style={styles.row}>
|
||||
{visible.map((item) => {
|
||||
const busy = busyKey === item.key;
|
||||
// Typed and coloured like the list's chips (webmail a78d03a18).
|
||||
const kind = attachmentKind(item.type, item.name);
|
||||
const Icon = ATTACHMENT_ICONS[kind];
|
||||
return (
|
||||
<Pressable
|
||||
key={item.key}
|
||||
@@ -241,7 +246,7 @@ export function AttachmentChips({ email, jmapAccountId, calendarBannerShown, tne
|
||||
disabled={!!busyKey}
|
||||
accessibilityLabel={item.name}
|
||||
>
|
||||
{busy ? <ActivityIndicator size="small" color={c.textMuted} /> : <Paperclip size={14} color={c.textMuted} />}
|
||||
{busy ? <ActivityIndicator size="small" color={c.textMuted} /> : <Icon size={14} color={attachmentIconColor(kind, c)} />}
|
||||
<Text style={styles.name} numberOfLines={1}>{item.name}</Text>
|
||||
<Text style={styles.size}>{formatSize(item.size)}</Text>
|
||||
<Download size={14} color={c.textMuted} />
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Alert, Pressable, StyleSheet, Text, View } from 'react-native';
|
||||
import { File as FileIcon, FileArchive, FileImage, FileSpreadsheet, FileText } from '../icons';
|
||||
import type { Attachment, Email, Mailbox } from '../../api/types';
|
||||
import { getEmailAttachments } from '../../api/email';
|
||||
import { radius, spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
@@ -13,8 +12,9 @@ import { accountIdOfRow } from '../../stores/email-store';
|
||||
import { accountScopedId } from '../../lib/thread-utils';
|
||||
import {
|
||||
attachmentKind, realAttachments, requestListAttachments, shortAttachmentName,
|
||||
type AttachmentKind, type LoadListAttachments,
|
||||
type LoadListAttachments,
|
||||
} from '../../lib/list-attachments';
|
||||
import { ATTACHMENT_ICONS, attachmentIconColor } from './attachment-icons';
|
||||
import { getAttachmentDisplayName } from '../../lib/attachment-display';
|
||||
import { attachmentSaveName, downloadAttachment } from '../../lib/email-export';
|
||||
import { AttachmentPreviewModal } from './AttachmentPreviewModal';
|
||||
@@ -23,26 +23,6 @@ import { useFilePreview } from './use-file-preview';
|
||||
/** How many chips a row shows before collapsing the rest into a count. */
|
||||
const MAX_CHIPS = 2;
|
||||
|
||||
const ICONS: Record<AttachmentKind, typeof FileIcon> = {
|
||||
image: FileImage,
|
||||
pdf: FileText,
|
||||
sheet: FileSpreadsheet,
|
||||
archive: FileArchive,
|
||||
document: FileText,
|
||||
other: FileIcon,
|
||||
};
|
||||
|
||||
function iconColor(kind: AttachmentKind, c: ThemePalette): string {
|
||||
switch (kind) {
|
||||
case 'image': return c.tags.purple.dot;
|
||||
case 'pdf': return c.tags.red.dot;
|
||||
case 'sheet': return c.tags.green.dot;
|
||||
case 'archive': return c.tags.amber.dot;
|
||||
case 'document': return c.tags.blue.dot;
|
||||
default: return c.textMuted;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The row's attachment parts: the ones the email already carries (a message
|
||||
* opened before), else loaded lazily when it has a paperclip.
|
||||
@@ -94,7 +74,7 @@ export const ListAttachmentChips = React.memo(function ListAttachmentChips({
|
||||
<View style={styles.row}>
|
||||
{shown.map((a, i) => {
|
||||
const kind = attachmentKind(a.type, a.name);
|
||||
const Icon = ICONS[kind];
|
||||
const Icon = ATTACHMENT_ICONS[kind];
|
||||
return (
|
||||
<Pressable
|
||||
key={`${a.blobId}-${i}`}
|
||||
@@ -106,7 +86,7 @@ export const ListAttachmentChips = React.memo(function ListAttachmentChips({
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={a.name}
|
||||
>
|
||||
<Icon size={12} color={iconColor(kind, c)} />
|
||||
<Icon size={12} color={attachmentIconColor(kind, c)} />
|
||||
<Text style={[styles.name, dyn.caption]} numberOfLines={1}>{shortAttachmentName(a.name ?? '')}</Text>
|
||||
</Pressable>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { File as FileIcon, FileArchive, FileImage, FileSpreadsheet, FileText } from '../icons';
|
||||
import type { ThemePalette } from '../../theme/tokens';
|
||||
import type { AttachmentKind } from '../../lib/list-attachments';
|
||||
|
||||
/**
|
||||
* The icon of an attachment chip by file type, the same in the list and the
|
||||
* reader (webmail components/email/attachment-chips.tsx, a78d03a18).
|
||||
*/
|
||||
export const ATTACHMENT_ICONS: Record<AttachmentKind, typeof FileIcon> = {
|
||||
image: FileImage,
|
||||
pdf: FileText,
|
||||
sheet: FileSpreadsheet,
|
||||
archive: FileArchive,
|
||||
document: FileText,
|
||||
other: FileIcon,
|
||||
};
|
||||
|
||||
/** The icon's colour by file type. */
|
||||
export function attachmentIconColor(kind: AttachmentKind, c: ThemePalette): string {
|
||||
switch (kind) {
|
||||
case 'image': return c.tags.purple.dot;
|
||||
case 'pdf': return c.tags.red.dot;
|
||||
case 'sheet': return c.tags.green.dot;
|
||||
case 'archive': return c.tags.amber.dot;
|
||||
case 'document': return c.tags.blue.dot;
|
||||
default: return c.textMuted;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user