Skip to content

Commit 4d22b67

Browse files
committed
feat(file-viewer): enable viewing .eml files
1 parent e7755a8 commit 4d22b67

10 files changed

Lines changed: 528 additions & 17 deletions

File tree

etc/lime-elements.api.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ export namespace Components {
406406
"value": LabelValue;
407407
}
408408
export interface LimelEmailViewer {
409+
"attachments"?: EmailAttachment[];
409410
"bodyHtml"?: string;
410411
"bodyText"?: string;
411412
"cc"?: string;
@@ -983,6 +984,13 @@ export type EditorTextLink = {
983984
// @beta (undocumented)
984985
export type EditorUiType = 'standard' | 'minimal' | 'no-toolbar';
985986

987+
// @public
988+
export interface EmailAttachment {
989+
filename?: string;
990+
mimeType?: string;
991+
size?: number;
992+
}
993+
986994
// Warning: (ae-missing-release-tag) "EventEmitter" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
987995
//
988996
// @public (undocumented)
@@ -1010,7 +1018,7 @@ export interface FileInfo {
10101018
}
10111019

10121020
// @public (undocumented)
1013-
export type FileType = 'pdf' | 'image' | 'video' | 'audio' | 'text' | 'office' | 'unknown';
1021+
export type FileType = 'pdf' | 'image' | 'video' | 'audio' | 'text' | 'email' | 'office' | 'unknown';
10141022

10151023
// @public (undocumented)
10161024
export type FlexContainerAlign = 'start' | 'end' | 'center' | 'stretch';
@@ -1648,6 +1656,7 @@ export namespace JSX {
16481656
"value"?: LabelValue;
16491657
}
16501658
export interface LimelEmailViewer {
1659+
"attachments"?: EmailAttachment[];
16511660
"bodyHtml"?: string;
16521661
"bodyText"?: string;
16531662
"cc"?: string;

src/components/email-viewer/email-viewer.scss

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,58 @@
6767
}
6868
}
6969

70+
.attachments {
71+
padding: 0.5rem 0.75rem;
72+
73+
label {
74+
font-size: var(--limel-theme-small-font-size);
75+
opacity: 0.6;
76+
}
77+
78+
ul {
79+
all: unset;
80+
display: grid;
81+
grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
82+
gap: 0.5rem;
83+
84+
padding: 0.5rem 0;
85+
}
86+
87+
li {
88+
all: unset;
89+
position: relative;
90+
display: flex;
91+
flex-direction: column;
92+
gap: 0.25rem;
93+
94+
font-size: 0.6875rem;
95+
line-height: normal;
96+
97+
padding: 0.75rem 0.5rem 0.5rem 0.5rem;
98+
border-radius: 0.5rem;
99+
border: 1px solid rgba(var(--contrast-600));
100+
background-color: rgba(var(--contrast-400));
101+
}
102+
103+
.attachment-filename {
104+
font-weight: 500;
105+
}
106+
107+
.attachment-mime-type {
108+
opacity: 0.7;
109+
}
110+
111+
limel-badge {
112+
--badge-max-width: auto;
113+
--badge-background-color: rgb(var(--contrast-1000), 0.7);
114+
--badge-text-color: rgb(var(--color-white));
115+
position: absolute;
116+
top: 0.125rem;
117+
right: 0.125rem;
118+
box-shadow: var(--shadow-brighten-edges-outside);
119+
}
120+
}
121+
70122
.body {
71123
flex-grow: 1;
72124
max-width: 100%;

src/components/email-viewer/email-viewer.tsx

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Component, Prop, h } from '@stencil/core';
22
import translate from '../../global/translations';
33
import { Languages } from '../date-picker/date.types';
4+
import { EmailAttachment } from '../../util/email';
5+
import { formatBytes } from '../../util/format-bytes';
46

57
/**
68
* This is a private component, used to render `.eml` files inside
@@ -63,6 +65,12 @@ export class EmailViewer {
6365
@Prop()
6466
public bodyText?: string;
6567

68+
/**
69+
* List of non-inline attachments.
70+
*/
71+
@Prop()
72+
public attachments?: EmailAttachment[];
73+
6674
/**
6775
* Optional URL to render as a final fallback using an `<object type="text/plain">`.
6876
*/
@@ -112,6 +120,7 @@ export class EmailViewer {
112120
this.getTranslation('file-viewer.email.date'),
113121
this.date
114122
)}
123+
{this.renderAttachments()}
115124
</div>
116125
);
117126
}
@@ -156,8 +165,8 @@ export class EmailViewer {
156165
return (
157166
<dl class={`headers ${type}`}>
158167
<dt>{label}</dt>
159-
{values.map((v) => (
160-
<dd>{v}</dd>
168+
{values.map((headerValue, index) => (
169+
<dd key={`${type}-${index}`}>{headerValue}</dd>
161170
))}
162171
</dl>
163172
);
@@ -170,13 +179,52 @@ export class EmailViewer {
170179
if (type === 'to' || type === 'cc') {
171180
return value
172181
.split(',')
173-
.map((v) => v.trim())
182+
.map((valuePart) => valuePart.trim())
174183
.filter(Boolean);
175184
}
176185

177186
return [value];
178187
}
179188

189+
private renderAttachments() {
190+
if (!this.attachments || this.attachments.length === 0) {
191+
return;
192+
}
193+
194+
const label = this.getTranslation('file-viewer.email.attachments');
195+
196+
return (
197+
<div class="attachments">
198+
<span>{label}</span>
199+
<ul>
200+
{this.attachments.map((attachment, index) => (
201+
<li key={`attachment-${index}`}>
202+
<span class="attachment-filename">
203+
{attachment.filename?.trim() ||
204+
this.getTranslation(
205+
'file-viewer.email.attachment.unnamed'
206+
)}
207+
</span>
208+
<span class="attachment-mime-type">
209+
{attachment.mimeType?.trim()}
210+
</span>
211+
{this.renderSizeBadge(attachment.size)}
212+
</li>
213+
))}
214+
</ul>
215+
</div>
216+
);
217+
}
218+
219+
private renderSizeBadge(size: number) {
220+
if (typeof size !== 'number') {
221+
return;
222+
}
223+
return (
224+
<limel-badge class="attachment-size" label={formatBytes(size)} />
225+
);
226+
}
227+
180228
private getTranslation(key: string) {
181229
return translate.get(key, this.language);
182230
}

0 commit comments

Comments
 (0)