Skip to content

Commit 3cdd259

Browse files
authored
Merge pull request #646 from GetStream/support-custom-attachments
feat: support custom attachments
2 parents 894f8f3 + 2c33b2e commit 3cdd259

64 files changed

Lines changed: 1221 additions & 411 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import AttachmentsScreenshot from "../assets/attachments-screenshot.png";
2+
import VoiceRecordingScreenshot from "../assets/voice-recording-screenshot.png";
3+
4+
- Images (including GIFs) are displayed inline
5+
- Videos are displayed inline
6+
- Voice recordings are displayed inline
7+
- Other files can be downloaded
8+
- Links in a message are enriched with built-in open graph URL scraping
9+
10+
**Example 1** - different type of attachments:
11+
12+
<img src={AttachmentsScreenshot} width="500" />
13+
14+
**Example 2** - voice recording:
15+
16+
<img src={VoiceRecordingScreenshot} width="500" />
33.9 KB
Loading
23.6 KB
Loading
45 KB
Loading
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
---
2+
id: custom-attachments
3+
title: Custom attachments
4+
---
5+
6+
import SupportedAttachments from "../_common/supported-attachments.mdx";
7+
import PaymentLink from "../assets/payment-link.png";
8+
import PaymentPreview from "../assets/payment-preview.png";
9+
import PaymentAttachment from "../assets/payment-attachment.png";
10+
11+
The Stream API allows you to add any attachment to a message. The SDK supports some common types (such as images, videos, etc.) out-of-the-box, but you have to provide your own template to display others.
12+
13+
The Angular SDK has out-of-the-box support for the following types:
14+
15+
<SupportedAttachments />
16+
17+
This guide will show you how to create custom attachments. In the example, we'll allow users to make payment links to send money to each other, but the same logic works for any attachment.
18+
19+
## Creating the attachment
20+
21+
Let's add a button to the message input component to make a payment link. How we create the attachment doesn't matter; the important part is to create an `Attachment` object we can provide to the [`AttachmentService`](../../services/AttachmentService).
22+
23+
```html showLineNumbers
24+
<!-- Each message input component has it's own instance of the AttachmentService -->
25+
<stream-message-input #input>
26+
<button
27+
message-input-start
28+
(click)="createPaymentLink(input.attachmentService)"
29+
>
30+
Payment link
31+
</button>
32+
</stream-message-input>
33+
```
34+
35+
```typescript showLineNumbers {18-22}
36+
// Optionally, you can define the shape of your custom attachments to get proper compile checks
37+
type MyGenerics = DefaultStreamChatGenerics & {
38+
attachmentType: {
39+
type: 'custom';
40+
subtype: 'payment';
41+
value: string;
42+
paymentLink: string;
43+
};
44+
};
45+
46+
createPaymentLink(attachmentService: AttachmentService) {
47+
const attachment: Attachment<MyGenerics> = {
48+
type: 'custom',
49+
subtype: 'payment',
50+
value: `${Math.ceil(Math.random() * 99)}$`,
51+
paymentLink: 'pay/me/or/else',
52+
};
53+
// Insert the attachment to the list of custom attachments
54+
attachmentService.customAttachments$.next([
55+
...attachmentService.customAttachments$.value,
56+
attachment,
57+
]);
58+
}
59+
```
60+
61+
<img src={PaymentLink} width="500" />
62+
63+
Clicking the "Payment link" will add the attachment to the message, but we don't yet have any visual indicator of this.
64+
65+
## Custom attachment preview
66+
67+
Let's add a preview of the payment attachment.
68+
69+
To do this, we define the HTML template code for the preview that uses the `AttachmentService` to display the previews of the custom attachments:
70+
71+
```html showLineNumbers
72+
<ng-template #customAttachmentPreviews let-service="service">
73+
<div
74+
style="padding: 8px; background-color: azure; border-radius: inherit"
75+
class="custom-attachment-container"
76+
*ngFor="let attachment of service.customAttachments$ | async"
77+
>
78+
<ng-container [ngSwitch]="attachment.subtype">
79+
<div *ngSwitchCase="'payment'" class="payment-link">
80+
🤑 {{ attachment.value }}
81+
</div>
82+
</ng-container>
83+
</div>
84+
</ng-template>
85+
```
86+
87+
If you have multiple different types of custom attachments, you can display them all here.
88+
89+
Next, we register the template for the `customAttachmentPreviewListTemplate$` field of the [`CustomTemplatesService`](../../services/CustomTemplatesService):
90+
91+
```typescript showLineNumbers {8-10}
92+
export class AppComponent implements AfterViewInit {
93+
@ViewChild("customAttachmentPreviews")
94+
customAttachmentPreviewsTemplate!: TemplateRef<CustomAttachmentPreviewListContext>;
95+
96+
constructor(private customTemplateService: CustomTemplatesService) {}
97+
98+
ngAfterViewInit(): void {
99+
this.customTemplateService.customAttachmentPreviewListTemplate$.next(
100+
this.customAttachmentPreviewsTemplate
101+
);
102+
}
103+
}
104+
```
105+
106+
If we click the "Payment link" button, the preview is now visible:
107+
108+
<img src={PaymentPreview} width="500" />
109+
110+
## Delete attachment preview
111+
112+
Let's allow deleting a payment link by extending the template of the preview:
113+
114+
```html showLineNumbers {10-11}
115+
<ng-template #customAttachmentPreviews let-service="service">
116+
<div
117+
style="padding: 8px; background-color: azure; border-radius: inherit"
118+
class="custom-attachment-container"
119+
*ngFor="let attachment of service.customAttachments$ | async"
120+
>
121+
<ng-container [ngSwitch]="attachment.subtype">
122+
<div *ngSwitchCase="'payment'" class="payment-link">
123+
🤑 {{ attachment.value }}
124+
<!-- Add a delete button -->
125+
<button (click)="deletePaymentLink(attachment, service)">X</button>
126+
</div>
127+
</ng-container>
128+
</div>
129+
</ng-template>
130+
```
131+
132+
This is the implementation of the delete payment attachment method:
133+
134+
```typescript showLineNumbers {5-8}
135+
deletePaymentLink(
136+
attachment: Attachment<MyGenerics>,
137+
attachmentService: AttachmentService<MyGenerics>
138+
) {
139+
attachmentService.customAttachments$.next(
140+
attachmentService.customAttachments$.value.filter(
141+
(a) => a.paymentLink !== attachment.paymentLink
142+
)
143+
);
144+
}
145+
```
146+
147+
## Loading state
148+
149+
Sometimes, creating attachments happens asynchronously. If that's the case, you should disable message sending while the attachment is processing.
150+
151+
Here is how you can do that by extending the `createPaymentLink` method:
152+
153+
```typescript showLineNumbers {2-5,23-26}
154+
async createPaymentLink(attachmentService: AttachmentService) {
155+
// Increment the upload counter to disable message send
156+
attachmentService.attachmentUploadInProgressCounter$.next(
157+
attachmentService.attachmentUploadInProgressCounter$.value + 1
158+
);
159+
const attachment: Attachment<MyGenerics> = {
160+
type: 'custom',
161+
subtype: 'payment',
162+
value: `${Math.ceil(Math.random() * 99)}$`,
163+
paymentLink: '',
164+
};
165+
// simulate network call
166+
await new Promise<void>((resolve) => {
167+
setTimeout(() => {
168+
attachment.paymentLink = 'pay/me/or/else';
169+
resolve();
170+
}, 2000);
171+
});
172+
attachmentService.customAttachments$.next([
173+
...attachmentService.customAttachments$.value,
174+
attachment,
175+
]);
176+
// Attachment is ready, decrease the upload counter
177+
attachmentService.attachmentUploadInProgressCounter$.next(
178+
attachmentService.attachmentUploadInProgressCounter$.value - 1
179+
);
180+
}
181+
```
182+
183+
## Custom attachment inside the message list
184+
185+
The last missing step is to display the payment link inside the message list. This will be very similar to how we created the attachment preview.
186+
187+
First, let's define an HTML template:
188+
189+
```html showLineNumbers
190+
<ng-template #customAttachments let-attachments="attachments">
191+
<div
192+
class="custom-attachment-container"
193+
*ngFor="let attachment of attachments"
194+
>
195+
<ng-container [ngSwitch]="attachment.subtype">
196+
<div
197+
style="margin-inline: 16px; margin-top: 8px"
198+
*ngSwitchCase="'payment'"
199+
class="payment-link"
200+
>
201+
💵
202+
<a [href]="attachment.link" target="_blank">{{ attachment.value }}</a>
203+
</div>
204+
</ng-container>
205+
</div>
206+
</ng-template>
207+
```
208+
209+
The `attachments` template variable will contain the list of custom attachments.
210+
211+
:::note
212+
By default the SDK will treat all `image`, `file`, `giphy`, `video` and `voiceRecording` attachments as built-in. All other type of attachments are treated as custom attachments.
213+
214+
If you want to change the filtering logic, provide your own implementation using the `filterCustomAttachment` method of the [`MessageService`](../../services/MessageService/#filtercustomattachment).
215+
:::
216+
217+
Next, we register the template for the `customAttachmentListTemplate$` field of the [`CustomTemplatesService`](../../services/CustomTemplatesService):
218+
219+
```typescript showLineNumbers {8-10}
220+
export class AppComponent implements AfterViewInit {
221+
@ViewChild("customAttachments")
222+
customAttachmentsTemplate!: TemplateRef<CustomAttachmentListContext>;
223+
224+
constructor(private customTemplateService: CustomTemplatesService) {}
225+
226+
ngAfterViewInit(): void {
227+
this.customTemplateService.customAttachmentListTemplate$.next(
228+
this.customAttachmentsTemplate
229+
);
230+
}
231+
}
232+
```
233+
234+
This is how the attachment looks like inside the message list:
235+
236+
<img src={PaymentAttachment} width="500" />
237+
238+
If you need reference to the message ID (and parent message ID for thread replies) the attachments belong to, this is how you can access them:
239+
240+
```html showLineNumbers {4-5}
241+
<ng-template
242+
#customAttachments
243+
let-attachments="attachments"
244+
let-messageId="messageId"
245+
let-parentMessageId="parentMessageId"
246+
>
247+
<div *ngFor="let attachment of attachments">
248+
{{ messageId }} {{ parentMessageId }} {{ attachment | json }}
249+
</div>
250+
</ng-template>
251+
```

docusaurus/docs/Angular/components/AttachmentListComponent.mdx

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,12 @@
1-
import AttachmentsScreenshot from "../assets/attachments-screenshot.png";
2-
import VoiceRecordingScreenshot from "../assets/voice-recording-screenshot.png";
31
import ImageSizingScreenshot1 from "../assets/image-sizing-screenshot-1.png";
42
import ImageSizingScreenshot2 from "../assets/image-sizing-screenshot-2.png";
53
import ImageSizingScreenshot3 from "../assets/image-sizing-screenshot-3.png";
64
import AttachmentSizeWarning from "../assets/attachment-size-warning.png";
5+
import SupportedAttachments from "../_common/supported-attachments.mdx";
76

87
The `AttachmentList` component displays the attachments of a message. The following attachments are supported:
98

10-
- Images (including GIFs) are displayed inline
11-
- Videos are displayed inline
12-
- Voice recordings are displayed inline (the Angular SDK doesn't support recording, only playback)
13-
- Other files can be downloaded
14-
- Links in a message are enriched with built-in open graph URL scraping
15-
16-
**Example 1** - different type of attachments:
17-
18-
<img src={AttachmentsScreenshot} width="500" />
19-
20-
**Example 2** - voice recording:
21-
22-
<img src={VoiceRecordingScreenshot} width="500" />
9+
<SupportedAttachments />
2310

2411
## Basic Usage
2512

@@ -122,7 +109,7 @@ The id of the message the attachments belong to
122109

123110
#### Defined in
124111

125-
[projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts:39](https://github.com/GetStream/stream-chat-angular/blob/c4925a571484c046f73b9e63286a2e64380af0c6/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts#L39)
112+
[projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts:44](https://github.com/GetStream/stream-chat-angular/blob/233af9a28d1b6ecdfe793362d5f20b0e8b749c16/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts#L44)
126113

127114
---
128115

@@ -134,7 +121,7 @@ The parent id of the message the attachments belong to
134121

135122
#### Defined in
136123

137-
[projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts:43](https://github.com/GetStream/stream-chat-angular/blob/c4925a571484c046f73b9e63286a2e64380af0c6/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts#L43)
124+
[projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts:48](https://github.com/GetStream/stream-chat-angular/blob/233af9a28d1b6ecdfe793362d5f20b0e8b749c16/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts#L48)
138125

139126
---
140127

@@ -146,7 +133,7 @@ The attachments to display
146133

147134
#### Defined in
148135

149-
[projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts:47](https://github.com/GetStream/stream-chat-angular/blob/c4925a571484c046f73b9e63286a2e64380af0c6/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts#L47)
136+
[projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts:52](https://github.com/GetStream/stream-chat-angular/blob/233af9a28d1b6ecdfe793362d5f20b0e8b749c16/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts#L52)
150137

151138
---
152139

@@ -158,6 +145,6 @@ Emits the state of the image carousel window
158145

159146
#### Defined in
160147

161-
[projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts:51](https://github.com/GetStream/stream-chat-angular/blob/c4925a571484c046f73b9e63286a2e64380af0c6/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts#L51)
148+
[projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts:56](https://github.com/GetStream/stream-chat-angular/blob/233af9a28d1b6ecdfe793362d5f20b0e8b749c16/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts#L56)
162149

163150
[//]: # "End of generated content"

docusaurus/docs/Angular/components/AttachmentPreviewListComponent.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ A stream that emits the current file uploads and their states
4747

4848
#### Defined in
4949

50-
[projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts:17](https://github.com/GetStream/stream-chat-angular/blob/c4925a571484c046f73b9e63286a2e64380af0c6/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts#L17)
50+
[projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts:28](https://github.com/GetStream/stream-chat-angular/blob/233af9a28d1b6ecdfe793362d5f20b0e8b749c16/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts#L28)
5151

5252
---
5353

@@ -59,7 +59,7 @@ An output to notify the parent component if the user tries to retry a failed upl
5959

6060
#### Defined in
6161

62-
[projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts:21](https://github.com/GetStream/stream-chat-angular/blob/c4925a571484c046f73b9e63286a2e64380af0c6/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts#L21)
62+
[projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts:32](https://github.com/GetStream/stream-chat-angular/blob/233af9a28d1b6ecdfe793362d5f20b0e8b749c16/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts#L32)
6363

6464
---
6565

@@ -71,6 +71,6 @@ An output to notify the parent component if the user wants to delete a file
7171

7272
#### Defined in
7373

74-
[projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts:25](https://github.com/GetStream/stream-chat-angular/blob/c4925a571484c046f73b9e63286a2e64380af0c6/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts#L25)
74+
[projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts:36](https://github.com/GetStream/stream-chat-angular/blob/233af9a28d1b6ecdfe793362d5f20b0e8b749c16/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts#L36)
7575

7676
[//]: # "End of generated content"

0 commit comments

Comments
 (0)