Skip to content

Commit 14cdd43

Browse files
committed
fix: improve attachment upload error notification data
1 parent 86cd646 commit 14cdd43

File tree

6 files changed

+75
-52
lines changed

6 files changed

+75
-52
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export * from './insights';
1010
export * from './messageComposer';
1111
export * from './middleware';
1212
export * from './moderation';
13+
export * from './notifications';
1314
export * from './permissions';
1415
export * from './poll';
1516
export * from './poll_manager';

src/messageComposer/attachmentManager.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,17 @@ export class AttachmentManager {
405405
if (localAttachment.localMetadata.uploadState === 'blocked') {
406406
this.upsertAttachments([localAttachment]);
407407
this.client.notifications.addError({
408-
message: 'Error uploading attachment',
409-
origin: { emitter: 'AttachmentManager', context: { attachment } },
408+
message: `The attachment upload was blocked`,
409+
origin: {
410+
emitter: 'AttachmentManager',
411+
context: { attachment, blockedAttachment: localAttachment },
412+
},
413+
options: {
414+
code: 'attachment.upload.blocked',
415+
metadata: {
416+
reason: localAttachment.localMetadata.uploadPermissionCheck?.reason,
417+
},
418+
},
410419
});
411420
return localAttachment;
412421
}
@@ -425,21 +434,7 @@ export class AttachmentManager {
425434
try {
426435
response = await this.doUploadRequest(localAttachment.localMetadata.file);
427436
} catch (error) {
428-
let finalError: Error = {
429-
message: 'Error uploading attachment',
430-
name: 'Error',
431-
};
432-
if (typeof (error as Error).message === 'string') {
433-
finalError = error as Error;
434-
} else if (typeof error === 'object') {
435-
finalError = Object.assign(finalError, error);
436-
}
437-
438-
this.client.notifications.addError({
439-
message: finalError.message,
440-
origin: { emitter: 'AttachmentManager', context: { attachment } },
441-
});
442-
437+
const reason = (error as Error).message ?? 'unknown error';
443438
const failedAttachment: LocalUploadAttachment = {
444439
...attachment,
445440
localMetadata: {
@@ -448,6 +443,15 @@ export class AttachmentManager {
448443
},
449444
};
450445

446+
this.client.notifications.addError({
447+
message: 'Error uploading attachment',
448+
origin: {
449+
emitter: 'AttachmentManager',
450+
context: { attachment, failedAttachment },
451+
},
452+
options: { code: 'attachment.upload.failed', metadata: { reason } },
453+
});
454+
451455
this.upsertAttachments([failedAttachment]);
452456
return failedAttachment;
453457
}

src/notifications/NotificationManager.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,8 @@ import type {
66
NotificationManagerConfig,
77
NotificationState,
88
} from './types';
9-
10-
const DURATIONS: NotificationManagerConfig['durations'] = {
11-
error: 10000,
12-
warning: 5000,
13-
info: 3000,
14-
success: 3000,
15-
} as const;
9+
import { mergeWith } from '../utils/mergeWith';
10+
import { DEFAULT_NOTIFICATION_MANAGER_CONFIG } from './configuration';
1611

1712
export class NotificationManager {
1813
store: StateStore<NotificationState>;
@@ -21,10 +16,7 @@ export class NotificationManager {
2116

2217
constructor(config: Partial<NotificationManagerConfig> = {}) {
2318
this.store = new StateStore<NotificationState>({ notifications: [] });
24-
this.config = {
25-
...config,
26-
durations: config.durations || DURATIONS,
27-
};
19+
this.config = mergeWith(DEFAULT_NOTIFICATION_MANAGER_CONFIG, config);
2820
}
2921

3022
get notifications() {
@@ -50,15 +42,17 @@ export class NotificationManager {
5042
add({ message, origin, options = {} }: AddNotificationPayload): string {
5143
const id = generateUUIDv4();
5244
const now = Date.now();
45+
const severity = options.severity || 'info';
46+
const duration = options.duration ?? this.config.durations[severity];
5347

5448
const notification: Notification = {
5549
id,
5650
message,
5751
origin,
58-
severity: options.severity || 'info',
52+
code: options?.code,
53+
severity,
5954
createdAt: now,
60-
expiresAt: options.duration ? now + options.duration : undefined,
61-
autoClose: options.autoClose ?? true,
55+
expiresAt: now + duration,
6256
actions: options.actions,
6357
metadata: options.metadata,
6458
};
@@ -67,7 +61,7 @@ export class NotificationManager {
6761
notifications: [...this.store.getLatestValue().notifications, notification],
6862
});
6963

70-
if (notification.autoClose && notification.expiresAt) {
64+
if (notification.expiresAt) {
7165
const timeout = setTimeout(() => {
7266
this.remove(id);
7367
}, options.duration || this.config.durations[notification.severity]);

src/notifications/configuration.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { NotificationManagerConfig } from './types';
2+
3+
const DURATION_MS = 3000 as const;
4+
5+
export const DEFAULT_NOTIFICATION_MANAGER_CONFIG: NotificationManagerConfig = {
6+
durations: {
7+
error: DURATION_MS,
8+
info: DURATION_MS,
9+
success: DURATION_MS,
10+
warning: DURATION_MS,
11+
},
12+
};

src/notifications/types.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,42 +33,40 @@ export type Notification = {
3333
* The identifier then can be recognized by notification consumers to act upon specific origin values.
3434
*/
3535
origin: NotificationOrigin;
36+
/** Optional code that can be used to group the notifications of the same type, e.g. attachment-upload-blocked. */
37+
code?: string;
3638
/** Optional timestamp when notification should expire */
3739
expiresAt?: number;
38-
/** Whether notification should automatically close after duration. Defaults to true */
39-
autoClose?: boolean;
4040
/** Array of action buttons for the notification */
4141
actions?: NotificationAction[];
4242
/** Optional metadata to attach to the notification */
4343
metadata?: Record<string, unknown>;
4444
};
4545

4646
/** Configuration options when creating a notification */
47-
export type NotificationOptions = {
48-
/** The severity level. Defaults to 'info' */
49-
severity?: NotificationSeverity;
50-
/** How long notification should display in milliseconds */
47+
export type NotificationOptions = Partial<
48+
Pick<Notification, 'code' | 'severity' | 'actions' | 'metadata'>
49+
> & {
50+
/** How long a notification should be displayed in milliseconds */
5151
duration?: number;
52-
/** Whether notification should auto-close after duration. Defaults to true */
53-
autoClose?: boolean;
54-
/** Array of action buttons for the notification */
55-
actions?: NotificationAction[];
56-
/** Optional metadata to attach to the notification */
57-
metadata?: Record<string, unknown>;
5852
};
5953

60-
/** State shape for the notification store */
54+
/**
55+
* State shape for the notification store
56+
* @deprcated use NotificationManagerState
57+
*/
6158
export type NotificationState = {
6259
/** Array of current notification objects */
6360
notifications: Notification[];
6461
};
6562

63+
/** State shape for the notification store */
64+
export type NotificationManagerState = NotificationState;
65+
6666
export type NotificationManagerConfig = {
6767
durations: Record<NotificationSeverity, number>;
6868
};
6969

70-
export type AddNotificationPayload = {
71-
message: string;
72-
origin: NotificationOrigin;
70+
export type AddNotificationPayload = Pick<Notification, 'message' | 'origin'> & {
7371
options?: NotificationOptions;
7472
};

test/unit/MessageComposer/attachmentManager.test.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -844,10 +844,17 @@ describe('AttachmentManager', () => {
844844

845845
expect(attachmentManager.failedUploadsCount).toBe(1);
846846
expect(mockClient.notifications.addError).toHaveBeenCalledWith({
847-
message: 'Upload failed',
847+
message: 'Error uploading attachment',
848848
origin: {
849849
emitter: 'AttachmentManager',
850-
context: { attachment: expect.any(Object) },
850+
context: {
851+
attachment: expect.any(Object),
852+
failedAttachment: expect.any(Object),
853+
},
854+
},
855+
options: {
856+
code: 'attachment.upload.failed',
857+
metadata: { reason: 'Upload failed' },
851858
},
852859
});
853860
});
@@ -877,10 +884,17 @@ describe('AttachmentManager', () => {
877884

878885
// Verify notification was added
879886
expect(mockClient.notifications.addError).toHaveBeenCalledWith({
880-
message: 'Error uploading attachment',
887+
message: 'The attachment upload was blocked',
881888
origin: {
882889
emitter: 'AttachmentManager',
883-
context: { attachment: blockedAttachment },
890+
context: {
891+
attachment: blockedAttachment,
892+
blockedAttachment: expect.any(Object),
893+
},
894+
},
895+
options: {
896+
code: 'attachment.upload.blocked',
897+
metadata: { reason: 'size_limit' },
884898
},
885899
});
886900
});

0 commit comments

Comments
 (0)