Skip to content

Commit 71da77a

Browse files
authored
Merge pull request #1773 from GetStream/cdn-integration-review-proposals
feat: new CDN integration
2 parents 4c2c0a1 + 03166a9 commit 71da77a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1107
-326
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ E2E_TEST_USER_1_TOKEN='test-user-1-jwt'
55
E2E_TEST_USER_2='test-user-2'
66
E2E_TEST_USER_2_TOKEN='test-user-2-jwt'
77
E2E_JUMP_TO_MESSAGE_CHANNEL='jump-to-message'
8+
E2E_ATTACHMENT_SIZING_CHANNEL='attachment-sizing'
89
E2E_ADD_MESSAGE_CHANNEL='add-message'
910
E2E_ADDITIONAL_CHANNELS="mr-channel-1,mr-channel-2,edit-message-channel,pin-message-channel"
1011
E2E_LONG_MESSAGE_LISTS_CHANNEL='navigate-long-message-lists'

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ jobs:
5656
E2E_TEST_USER_2_TOKEN: ${{ secrets.E2E_TEST_USER_2_TOKEN }}
5757
E2E_ADDITIONAL_CHANNELS: mr-channel-1, mr-channel-2, edit-message-channel, pin-message-channel
5858
E2E_LONG_MESSAGE_LISTS_CHANNEL: navigate-long-message-lists
59+
E2E_ATTACHMENT_SIZING_CHANNEL: attachment-sizing
5960

6061
- name: 🎥 Upload Artifacts
6162
uses: actions/upload-artifact@v3
52 KB
Loading
229 KB
Loading
336 KB
Loading
245 KB
Loading

docusaurus/docs/React/contexts/channel-state-context.mdx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,17 @@ Error object (if any) in loading the `channel`, otherwise null.
6969

7070
The giphy version to use when displaying giphies.
7171

72-
| Type |
73-
| --------------------------------------------------------------------------------------------------------------------------------------------------- |
74-
| `'original' | 'fixed_height' | 'fixed_height_still' | 'fixed_height_downsampled' | 'fixed_width' | 'fixed_width_still' | 'fixed_width_downsampled'` |
72+
| Type |
73+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
74+
| `'original'` \| `'fixed_height'` \| `'fixed_height_still'` \| `'fixed_height_downsampled'` \| `'fixed_width'` \| `'fixed_width_still'` \| `'fixed_width_downsampled'` |
75+
76+
### imageAttachmentSizeHandler
77+
78+
A custom function to provide size configuration for image attachments
79+
80+
| Type |
81+
| ---------------------------------------------------------------- |
82+
| `(a: Attachment, e: HTMLElement) => ImageAttachmentConfigration` |
7583

7684
### hasMore
7785

@@ -202,6 +210,13 @@ Flag signalling whether the scroll to the bottom is prevented. Used internally b
202210
| Type |
203211
|---------|
204212
| boolean |
213+
### shouldGenerateVideoThumbnail
214+
215+
You can turn on/off thumbnail generation for video attachments
216+
217+
| Type |
218+
| --------- |
219+
| `boolean` |
205220

206221
### thread
207222

@@ -242,6 +257,13 @@ Flag signalling whether the scroll to the bottom is prevented in thread. Used in
242257
| Type |
243258
|---------|
244259
| boolean |
260+
### videoAttachmentSizeHandler
261+
262+
A custom function to provide size configuration for video attachments
263+
264+
| Type |
265+
| ----------------------------------------------------------------- |
266+
| `(a: Attachment, e: HTMLElement) => VideoAttachmentConfiguration` |
245267

246268
### watcherCount
247269

docusaurus/docs/React/core-components/channel.mdx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,17 @@ Custom UI component to render a Giphy preview in the `VirtualizedMessageList`.
300300
The Giphy version to render - check the keys of the [Image Object](https://developers.giphy.com/docs/api/schema#image-object) for possible values.
301301

302302
| Type | Default |
303-
|--------|----------------|
303+
| ------ | -------------- |
304304
| string | 'fixed_height' |
305305

306+
### imageAttachmentSizeHandler
307+
308+
A custom function to provide size configuration for image attachments
309+
310+
| Type |
311+
| ---------------------------------------------------------------- |
312+
| `(a: Attachment, e: HTMLElement) => ImageAttachmentConfigration` |
313+
306314
### HeaderComponent
307315

308316
Custom UI component to render at the top of the `MessageList`.
@@ -503,6 +511,14 @@ Custom UI component for send button.
503511
|-----------|---------------------------------------------------------------------|
504512
| component | <GHComponentLink text='SendButton' path='/MessageInput/icons.tsx'/> |
505513

514+
### shouldGenerateVideoThumbnail
515+
516+
You can turn on/off thumbnail generation for video attachments
517+
518+
| Type |
519+
| -------- |
520+
| `boolean` |
521+
506522
### skipMessageDataMemoization
507523

508524
If true, skips the message data string comparison used to memoize the current channel messages (helpful for channels with 1000s of messages).
@@ -560,6 +576,14 @@ Custom UI component for the typing indicator.
560576
|-----------|---------------------------------------------------------------------------------------|
561577
| component | <GHComponentLink text='TypingIndicator' path='/TypingIndicator/TypingIndicator.tsx'/> |
562578

579+
### videoAttachmentSizeHandler
580+
581+
A custom function to provide size configuration for video attachments
582+
583+
| Type |
584+
| ----------------------------------------------------------------- |
585+
| `(a: Attachment, e: HTMLElement) => VideoAttachmentConfiguration` |
586+
563587
### VirtualMessage
564588

565589
Custom UI component to display a message in the `VirtualizedMessageList`.

docusaurus/docs/React/message-components/attachment.mdx

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ title: Attachments
66

77
import GHComponentLink from '../_docusaurus-components/GHComponentLink';
88
import ReviewNote from '../_docusaurus-components/ReviewNote';
9-
9+
import ImageSizingScreenshot1 from '../assets/ImageSizing1.png';
10+
import ImageSizingScreenshot2 from '../assets/ImageSizing2.png';
11+
import ImageSizingScreenshot3 from '../assets/ImageSizing3.png';
12+
import AttachmentSizeWarning from '../assets/AttachmentSizeWarning.png';
1013

1114
The `Attachment` component takes a list of message attachments and conditionally renders the UI of each individual attachment based
1215
upon its type. The following table shows the attachment UI components that will be rendered for various attachment types:
@@ -36,7 +39,7 @@ const CustomMessage = () => {
3639

3740
return (
3841
<div>
39-
{ hasAttachments && <Attachment attachments={message.attachments} />}
42+
{hasAttachments && <Attachment attachments={message.attachments} />}
4043
// render remaining custom Message UI
4144
</div>
4245
);
@@ -103,6 +106,58 @@ const CustomAttachment = (props) => {
103106
</Chat>;
104107
```
105108
109+
### Image and video sizing
110+
111+
The following section details how the width and height of images and videos uploaded from files are computed.
112+
113+
#### Maximum size
114+
115+
You can control the maximum width and height of images and videos with the [`--str-chat__attachment-max-width`](../theming/component-variables.mdx) CSS variable (available only in [theme-v2](../theming/introduction.mdx)). The value of this variable must be a value that can be computed to a valid pixel value using the [`getComputedStyle`](https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle) method (for example: `300px`, `10rem`, `calc(300px - var(--margin))`, but not `100%`). If you provide an invalid value, the image and video sizing can break, which can lead to scrolling issues inside the message list (for example the message list isn't scrolled to the bottom when you open a channel).
116+
117+
If you set an invalid value to the variable, you'll see a warning on the browser's console:
118+
119+
<img src={AttachmentSizeWarning} width='800' />
120+
121+
#### File size optimization
122+
123+
Based on the CSS settings provided for images and videos (see [Maximum size](#maximum-size) section), the SDK will load an image (or thumbnail image in case of a video file) with a reduced file size while still providing sufficient image quality for the given dimensions. This will result in less network traffic and faster image load for users.
124+
125+
For example if an image has `max-width` and `max-height` set to `300px` and the original image has the following dimensions: `965 × 1280` it's enough to load an image with a reduced file size, file size reduction is done by Stream's CDN.
126+
127+
#### Aspect ratio
128+
129+
The following description is applicable for [theme-v2](../theming/introduction.mdx).
130+
131+
The SDK will try to display images and videos with their original aspect ratio, however this isn't always guaranteed (in those cases a cropped image will be displayed). Three notable exceptions are:
132+
133+
1. if a message contains multiple lines of texts and/or multiple attachments, the image/video might be stretched to its `max-width`
134+
135+
**Example 1** - message with one line of text - image is displayed with original aspect ratio
136+
137+
<img src={ImageSizingScreenshot1} width='200' />
138+
139+
**Example 2** - message with multiple lines of text - image is cropped
140+
141+
<img src={ImageSizingScreenshot2} width='200' />
142+
143+
2. in Safari, images/videos with portrait orientation are stretched to `max-width`
144+
145+
**Example 3** - portrait images in Safari - image is cropped
146+
147+
<img src={ImageSizingScreenshot3} width='200' />
148+
149+
3. if the image/video can't be rendered with the original aspect ratio given the `max-width` and `max-height` constraints of the host HTML element
150+
151+
:::important
152+
**File size optimization** and maintaining **aspect ratio** uses features provided by Stream's CDN. If you're using your **own CDN** you'll likely have to provide your own implementation for this. You can do this using the [`imageAttachmentSizeHandler`](../core-components/channel.mdx/#imageattachmentsizehandler) and [`videoAttachmentSizeHandler`](../core-components/channel.mdx/#videoattachmentsizehandler) props.
153+
:::
154+
155+
:::danger
156+
**If you're planning to rewrite attachment sizing with your own CSS code** it's important to note that:
157+
158+
The sizing logic for images and videos (the [`imageAttachmentSizeHandler`](../core-components/channel.mdx/#imageattachmentsizehandler) and [`videoAttachmentSizeHandler`](../core-components/channel.mdx/#videoattachmentsizehandler)) requires that host elements of images and videos (ususally `img` and `video` elements) have `max-height`/`height` and `max-width` properties defined and that these values can be computed to a valid pixel value using the [`getComputedStyle`](https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle) (for more information refer to the [Maximum size](#maximum-size) section).
159+
:::
160+
106161
## Props
107162
108163
### <div class="label required basic">Required</div> attachments
@@ -122,61 +177,57 @@ The handler function to call when an action is performed on an attachment, examp
122177
| ----------------------------------------------------------------------------------------------------- |
123178
| (dataOrName?: string \| FormData, value?: string, event?: React.BaseSyntheticEvent) => Promise<void\> |
124179
125-
126-
127-
<ReviewNote
128-
author='Martin Cupela'
129-
id='04803878-3362-11ed-8912-a4bb6d26ac2f'
130-
>
131-
What are attachment actions, what is their use case / recipes? Missing concept explanation. It would be nice to have a link to a doc explaining these.
180+
<ReviewNote author='Martin Cupela' id='04803878-3362-11ed-8912-a4bb6d26ac2f'>
181+
What are attachment actions, what is their use case / recipes? Missing concept explanation. It
182+
would be nice to have a link to a doc explaining these.
132183
</ReviewNote>
133184
134185
### AttachmentActions
135186
136187
Custom UI component for displaying attachment actions.
137188
138189
| Type | Default |
139-
|-----------|--------------------------------------------------------------------------------------|
190+
| --------- | ------------------------------------------------------------------------------------ |
140191
| component | <GHComponentLink text='AttachmentActions' path='/Attachment/AttachmentActions.tsx'/> |
141192
142193
### Audio
143194
144195
Custom UI component for displaying an audio type attachment.
145196
146197
| Type | Default |
147-
|-----------|--------------------------------------------------------------|
198+
| --------- | ------------------------------------------------------------ |
148199
| component | <GHComponentLink text='Audio' path='/Attachment/Audio.tsx'/> |
149200
150201
### Card
151202
152203
Custom UI component for displaying a card type attachment.
153204
154205
| Type | Default |
155-
|-----------|------------------------------------------------------------|
206+
| --------- | ---------------------------------------------------------- |
156207
| component | <GHComponentLink text='Card' path='/Attachment/Card.tsx'/> |
157208
158209
### File
159210
160211
Custom UI component for displaying a file type attachment.
161212
162213
| Type | Default |
163-
|-----------|----------------------------------------------------------------------|
214+
| --------- | -------------------------------------------------------------------- |
164215
| component | <GHComponentLink text='File' path='/Attachment/FileAttachment.tsx'/> |
165216
166217
### Gallery
167218
168219
Custom UI component for displaying a gallery of image type attachments.
169220
170221
| Type | Default |
171-
|-----------|---------------------------------------------------------------|
222+
| --------- | ------------------------------------------------------------- |
172223
| component | <GHComponentLink text='Gallery' path='/Gallery/Gallery.tsx'/> |
173224
174225
### Image
175226
176227
Custom UI component for displaying an image type attachment.
177228
178229
| Type | Default |
179-
|-----------|-----------------------------------------------------------|
230+
| --------- | --------------------------------------------------------- |
180231
| component | <GHComponentLink text='Image' path='/Gallery/Image.tsx'/> |
181232
182233
### Media

e2e/attachment-sizing.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import * as dotenv from 'dotenv';
2+
import { expect } from '@playwright/test';
3+
4+
import selectors from './user/selectors';
5+
import { test } from './user/test';
6+
7+
import ChannelPreview from './user/components/ChannelPreview';
8+
import MessageList from './user/components/MessageList/MessageList';
9+
10+
dotenv.config();
11+
dotenv.config({ path: `.env.local` });
12+
13+
const CHANNEL_NAME = 'attachment-sizing' as const;
14+
const user1Id = process.env.E2E_TEST_USER_1;
15+
const USER1_CHAT_VIEW_CLASSNAME = `.${user1Id}`;
16+
17+
test.describe('add height to video and image attachments', () => {
18+
test.beforeEach(async ({ controller, user }) => {
19+
await controller.openStory('attachment-sizing--user1', selectors.channelPreviewButton);
20+
await user.clicks(ChannelPreview).text(CHANNEL_NAME);
21+
});
22+
23+
test('should add height for video attachments', async ({ page, user }) => {
24+
const videoElementsLocator = page.locator('[data-testid="video-wrapper"]');
25+
const result = await videoElementsLocator.evaluateAll<boolean>(
26+
(videoElements) =>
27+
videoElements.length > 0 &&
28+
videoElements.every((element) => getComputedStyle(element).height.includes('px')),
29+
);
30+
31+
expect(result).toBeTruthy();
32+
await user
33+
.sees(MessageList)
34+
.isScrolledToBottom(`${USER1_CHAT_VIEW_CLASSNAME} ${selectors.messageList}`);
35+
});
36+
37+
test('should add height for single image attachments', async ({ page, user }) => {
38+
const imageElementsLocator = page.locator('[data-testid="image-test"]');
39+
const result = await imageElementsLocator.evaluateAll(
40+
(imageElements) =>
41+
imageElements.length > 0 &&
42+
imageElements.every((element) => getComputedStyle(element).height.includes('px')),
43+
);
44+
45+
expect(result).toBe(true);
46+
await user
47+
.sees(MessageList)
48+
.isScrolledToBottom(`${USER1_CHAT_VIEW_CLASSNAME} ${selectors.messageList}`);
49+
});
50+
51+
test('should add height for gallery image attachments', async ({ page, user }) => {
52+
const imageElementsLocator = page.locator(
53+
'[data-testid="gallery-image-last"],[data-testid="gallery-image"]',
54+
);
55+
const result = await imageElementsLocator.evaluateAll(
56+
(imageElements) =>
57+
imageElements.length > 0 &&
58+
imageElements.every((element) => getComputedStyle(element).height.includes('px')),
59+
);
60+
61+
expect(result).toBe(true);
62+
await user
63+
.sees(MessageList)
64+
.isScrolledToBottom(`${USER1_CHAT_VIEW_CLASSNAME} ${selectors.messageList}`);
65+
});
66+
});

0 commit comments

Comments
 (0)