@linktr.ee/messaging-react 4.2.1 → 4.3.0-rc-1787410254
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/Card-DA-EJpQI.cjs +2 -0
- package/dist/Card-DA-EJpQI.cjs.map +1 -0
- package/dist/Card-DyoGzhhM.js +1033 -0
- package/dist/Card-DyoGzhhM.js.map +1 -0
- package/dist/index-C04QXCa0.cjs +3 -0
- package/dist/index-C04QXCa0.cjs.map +1 -0
- package/dist/index-Cc8BzIhS.js +5253 -0
- package/dist/index-Cc8BzIhS.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +5 -26
- package/dist/index.js +23 -26
- package/package.json +2 -2
- package/src/components/MediaMessage/index.tsx +6 -423
- package/src/components/MediaMessage/isLinkAttachment.test.ts +33 -0
- package/src/index.ts +1 -10
- package/dist/Card-3VtiPkQH.js +0 -701
- package/dist/Card-3VtiPkQH.js.map +0 -1
- package/dist/Card-BxYYijOd.cjs +0 -2
- package/dist/Card-BxYYijOd.cjs.map +0 -1
- package/dist/index-Cd-uS2hf.cjs +0 -3
- package/dist/index-Cd-uS2hf.cjs.map +0 -1
- package/dist/index-wG3iH9e6.js +0 -5916
- package/dist/index-wG3iH9e6.js.map +0 -1
- package/src/components/MediaMessage/MediaMessage.stories.tsx +0 -270
- package/src/components/MediaMessage/MediaMessage.test.tsx +0 -494
|
@@ -1,494 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import type { LocalMessage } from 'stream-chat'
|
|
3
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
4
|
-
|
|
5
|
-
import { renderWithProviders, screen, fireEvent } from '../../test/utils'
|
|
6
|
-
|
|
7
|
-
import { MediaMessage, resolveLinkAttachment } from '.'
|
|
8
|
-
|
|
9
|
-
vi.mock('../Avatar', () => ({
|
|
10
|
-
Avatar: ({ id }: { id: string }) => (
|
|
11
|
-
<div data-testid="avatar" data-user-id={id} />
|
|
12
|
-
),
|
|
13
|
-
}))
|
|
14
|
-
|
|
15
|
-
vi.mock('../AttachmentCard/MediaPlayer', () => ({
|
|
16
|
-
default: ({ source, mimeType }: { source: string; mimeType: string }) => (
|
|
17
|
-
<div
|
|
18
|
-
role="button"
|
|
19
|
-
tabIndex={0}
|
|
20
|
-
data-testid="media-player"
|
|
21
|
-
data-source={source}
|
|
22
|
-
data-mime-type={mimeType}
|
|
23
|
-
/>
|
|
24
|
-
),
|
|
25
|
-
}))
|
|
26
|
-
|
|
27
|
-
vi.mock('../AttachmentCard/utils/icons', () => ({
|
|
28
|
-
renderTypeIcon: () => <span data-testid="type-icon" />,
|
|
29
|
-
}))
|
|
30
|
-
|
|
31
|
-
vi.mock('../AttachmentCard/utils/mimeType', () => ({
|
|
32
|
-
getSourceType: (mimeType: string) => {
|
|
33
|
-
if (mimeType.startsWith('video/')) return 'video'
|
|
34
|
-
if (mimeType.startsWith('image/')) return 'image'
|
|
35
|
-
if (mimeType.startsWith('audio/')) return 'audio'
|
|
36
|
-
return 'document'
|
|
37
|
-
},
|
|
38
|
-
}))
|
|
39
|
-
|
|
40
|
-
const msg = (overrides: Record<string, unknown> = {}): LocalMessage =>
|
|
41
|
-
({
|
|
42
|
-
id: 'msg-1',
|
|
43
|
-
text: '',
|
|
44
|
-
type: 'regular',
|
|
45
|
-
created_at: new Date(),
|
|
46
|
-
updated_at: new Date(),
|
|
47
|
-
deleted_at: null,
|
|
48
|
-
pinned_at: null,
|
|
49
|
-
status: 'received',
|
|
50
|
-
user: { id: 'user-1', name: 'Alice' },
|
|
51
|
-
attachments: [],
|
|
52
|
-
...overrides,
|
|
53
|
-
}) as unknown as LocalMessage
|
|
54
|
-
|
|
55
|
-
describe('MediaMessage', () => {
|
|
56
|
-
it('renders nothing when no media URL is resolvable', () => {
|
|
57
|
-
const { container } = renderWithProviders(<MediaMessage message={msg()} />)
|
|
58
|
-
expect(container.firstChild).toBeNull()
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('renders MediaPlayer immediately for a video attachment', () => {
|
|
62
|
-
renderWithProviders(
|
|
63
|
-
<MediaMessage
|
|
64
|
-
message={msg({
|
|
65
|
-
attachments: [
|
|
66
|
-
{
|
|
67
|
-
type: 'video',
|
|
68
|
-
asset_url: 'https://cdn.example.com/clip.mp4',
|
|
69
|
-
mime_type: 'video/mp4',
|
|
70
|
-
title: 'My Clip',
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
|
-
})}
|
|
74
|
-
/>
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
const player = screen.getByTestId('media-player')
|
|
78
|
-
expect(player).toBeInTheDocument()
|
|
79
|
-
expect(player).toHaveAttribute(
|
|
80
|
-
'data-source',
|
|
81
|
-
'https://cdn.example.com/clip.mp4'
|
|
82
|
-
)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('renders MediaPlayer for an audio attachment', () => {
|
|
86
|
-
renderWithProviders(
|
|
87
|
-
<MediaMessage
|
|
88
|
-
message={msg({
|
|
89
|
-
attachments: [
|
|
90
|
-
{
|
|
91
|
-
type: 'audio',
|
|
92
|
-
asset_url: 'https://cdn.example.com/track.mp3',
|
|
93
|
-
mime_type: 'audio/mpeg',
|
|
94
|
-
},
|
|
95
|
-
],
|
|
96
|
-
})}
|
|
97
|
-
/>
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
expect(screen.getByTestId('media-player')).toHaveAttribute(
|
|
101
|
-
'data-source',
|
|
102
|
-
'https://cdn.example.com/track.mp3'
|
|
103
|
-
)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('renders an img for an image attachment', () => {
|
|
107
|
-
renderWithProviders(
|
|
108
|
-
<MediaMessage
|
|
109
|
-
message={msg({
|
|
110
|
-
attachments: [
|
|
111
|
-
{
|
|
112
|
-
type: 'image',
|
|
113
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
114
|
-
mime_type: 'image/jpeg',
|
|
115
|
-
title: 'My Photo',
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
})}
|
|
119
|
-
/>
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
const image = screen.getByRole('img', { name: 'My Photo' })
|
|
123
|
-
expect(image).toHaveAttribute('src', 'https://cdn.example.com/photo.jpg')
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
it('renders unknown file type as icon placeholder, not a link', () => {
|
|
127
|
-
renderWithProviders(
|
|
128
|
-
<MediaMessage
|
|
129
|
-
message={msg({
|
|
130
|
-
attachments: [
|
|
131
|
-
{
|
|
132
|
-
type: 'file',
|
|
133
|
-
asset_url: 'https://cdn.example.com/data.bin',
|
|
134
|
-
mime_type: 'application/octet-stream',
|
|
135
|
-
title: 'Unknown File',
|
|
136
|
-
},
|
|
137
|
-
],
|
|
138
|
-
})}
|
|
139
|
-
/>
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
expect(screen.queryByRole('link')).not.toBeInTheDocument()
|
|
143
|
-
expect(screen.getByText('Unknown File')).toBeInTheDocument()
|
|
144
|
-
expect(screen.getAllByTestId('type-icon').length).toBeGreaterThanOrEqual(1)
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('shows title and file size in meta row', () => {
|
|
148
|
-
renderWithProviders(
|
|
149
|
-
<MediaMessage
|
|
150
|
-
message={msg({
|
|
151
|
-
attachments: [
|
|
152
|
-
{
|
|
153
|
-
type: 'video',
|
|
154
|
-
asset_url: 'https://cdn.example.com/clip.mp4',
|
|
155
|
-
mime_type: 'video/mp4',
|
|
156
|
-
title: 'Clip',
|
|
157
|
-
file_size: 2048,
|
|
158
|
-
},
|
|
159
|
-
],
|
|
160
|
-
})}
|
|
161
|
-
/>
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
expect(screen.getByText('Clip')).toBeInTheDocument()
|
|
165
|
-
expect(screen.getByText('2.0 KB')).toBeInTheDocument()
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
it('renders Avatar for a message from another user', () => {
|
|
169
|
-
renderWithProviders(
|
|
170
|
-
<MediaMessage
|
|
171
|
-
isMyMessage={false}
|
|
172
|
-
message={msg({
|
|
173
|
-
attachments: [
|
|
174
|
-
{
|
|
175
|
-
type: 'image',
|
|
176
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
177
|
-
mime_type: 'image/jpeg',
|
|
178
|
-
},
|
|
179
|
-
],
|
|
180
|
-
})}
|
|
181
|
-
/>
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
expect(screen.getByTestId('avatar')).toBeInTheDocument()
|
|
185
|
-
expect(screen.getByTestId('avatar')).toHaveAttribute(
|
|
186
|
-
'data-user-id',
|
|
187
|
-
'user-1'
|
|
188
|
-
)
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
it('does not render Avatar for own messages', () => {
|
|
192
|
-
renderWithProviders(
|
|
193
|
-
<MediaMessage
|
|
194
|
-
isMyMessage={true}
|
|
195
|
-
message={msg({
|
|
196
|
-
attachments: [
|
|
197
|
-
{
|
|
198
|
-
type: 'image',
|
|
199
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
200
|
-
mime_type: 'image/jpeg',
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
})}
|
|
204
|
-
/>
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
expect(screen.queryByTestId('avatar')).not.toBeInTheDocument()
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
it('applies the --me class for own messages', () => {
|
|
211
|
-
const { container } = renderWithProviders(
|
|
212
|
-
<MediaMessage
|
|
213
|
-
isMyMessage={true}
|
|
214
|
-
message={msg({
|
|
215
|
-
attachments: [
|
|
216
|
-
{
|
|
217
|
-
type: 'image',
|
|
218
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
219
|
-
mime_type: 'image/jpeg',
|
|
220
|
-
},
|
|
221
|
-
],
|
|
222
|
-
})}
|
|
223
|
-
/>
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
expect(container.firstChild).toHaveClass('str-chat__message--me')
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
it('applies the --other class for messages from other users', () => {
|
|
230
|
-
const { container } = renderWithProviders(
|
|
231
|
-
<MediaMessage
|
|
232
|
-
isMyMessage={false}
|
|
233
|
-
message={msg({
|
|
234
|
-
attachments: [
|
|
235
|
-
{
|
|
236
|
-
type: 'image',
|
|
237
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
238
|
-
mime_type: 'image/jpeg',
|
|
239
|
-
},
|
|
240
|
-
],
|
|
241
|
-
})}
|
|
242
|
-
/>
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
expect(container.firstChild).toHaveClass('str-chat__message--other')
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
it('renders nothing when no attachments', () => {
|
|
249
|
-
const { container } = renderWithProviders(
|
|
250
|
-
<MediaMessage message={msg({ attachments: [] })} />
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
expect(container.firstChild).toBeNull()
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
it('shows Download action for received (Visitor) image attachment', () => {
|
|
257
|
-
renderWithProviders(
|
|
258
|
-
<MediaMessage
|
|
259
|
-
message={msg({
|
|
260
|
-
attachments: [
|
|
261
|
-
{
|
|
262
|
-
type: 'image',
|
|
263
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
264
|
-
mime_type: 'image/jpeg',
|
|
265
|
-
title: 'My Photo',
|
|
266
|
-
},
|
|
267
|
-
],
|
|
268
|
-
})}
|
|
269
|
-
/>
|
|
270
|
-
)
|
|
271
|
-
|
|
272
|
-
expect(screen.getByRole('button', { name: 'Download' })).toBeInTheDocument()
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
it('does not show literal placeholder title for sent image without title', () => {
|
|
276
|
-
renderWithProviders(
|
|
277
|
-
<MediaMessage
|
|
278
|
-
isMyMessage={true}
|
|
279
|
-
message={msg({
|
|
280
|
-
attachments: [
|
|
281
|
-
{
|
|
282
|
-
type: 'image',
|
|
283
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
284
|
-
mime_type: 'image/jpeg',
|
|
285
|
-
},
|
|
286
|
-
],
|
|
287
|
-
})}
|
|
288
|
-
/>
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
expect(screen.queryByText('Attachment title')).not.toBeInTheDocument()
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
it('does not show Download button for sent (Creator) image attachment', () => {
|
|
295
|
-
renderWithProviders(
|
|
296
|
-
<MediaMessage
|
|
297
|
-
isMyMessage={true}
|
|
298
|
-
message={msg({
|
|
299
|
-
attachments: [
|
|
300
|
-
{
|
|
301
|
-
type: 'image',
|
|
302
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
303
|
-
mime_type: 'image/jpeg',
|
|
304
|
-
title: 'My Photo',
|
|
305
|
-
},
|
|
306
|
-
],
|
|
307
|
-
})}
|
|
308
|
-
/>
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
expect(
|
|
312
|
-
screen.queryByRole('button', { name: 'Download' })
|
|
313
|
-
).not.toBeInTheDocument()
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
it('shows Download action for received audio attachment', () => {
|
|
317
|
-
renderWithProviders(
|
|
318
|
-
<MediaMessage
|
|
319
|
-
message={msg({
|
|
320
|
-
attachments: [
|
|
321
|
-
{
|
|
322
|
-
type: 'audio',
|
|
323
|
-
asset_url: 'https://cdn.example.com/track.mp3',
|
|
324
|
-
mime_type: 'audio/mpeg',
|
|
325
|
-
title: 'My Track',
|
|
326
|
-
},
|
|
327
|
-
],
|
|
328
|
-
})}
|
|
329
|
-
/>
|
|
330
|
-
)
|
|
331
|
-
|
|
332
|
-
expect(screen.getByRole('button', { name: 'Download' })).toBeInTheDocument()
|
|
333
|
-
})
|
|
334
|
-
|
|
335
|
-
it('renders a link card for a link attachment', () => {
|
|
336
|
-
renderWithProviders(
|
|
337
|
-
<MediaMessage
|
|
338
|
-
message={msg({
|
|
339
|
-
attachments: [
|
|
340
|
-
{
|
|
341
|
-
type: 'link',
|
|
342
|
-
og_scrape_url: 'https://linktr.ee/someone',
|
|
343
|
-
title: 'My Linktree',
|
|
344
|
-
text: 'Check out my links',
|
|
345
|
-
image_url: 'https://cdn.example.com/thumb.jpg',
|
|
346
|
-
},
|
|
347
|
-
],
|
|
348
|
-
})}
|
|
349
|
-
/>
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
const link = screen.getByRole('link')
|
|
353
|
-
expect(link).toHaveAttribute('href', 'https://linktr.ee/someone')
|
|
354
|
-
expect(screen.getByText('My Linktree')).toBeInTheDocument()
|
|
355
|
-
expect(screen.getByText('Check out my links')).toBeInTheDocument()
|
|
356
|
-
expect(screen.getByText('https://linktr.ee/someone')).toBeInTheDocument()
|
|
357
|
-
const img = screen.getByRole('img', { name: 'My Linktree' })
|
|
358
|
-
expect(img).toHaveAttribute('src', 'https://cdn.example.com/thumb.jpg')
|
|
359
|
-
})
|
|
360
|
-
|
|
361
|
-
it('renders a link fallback icon when link has no image', () => {
|
|
362
|
-
renderWithProviders(
|
|
363
|
-
<MediaMessage
|
|
364
|
-
message={msg({
|
|
365
|
-
attachments: [
|
|
366
|
-
{
|
|
367
|
-
type: 'link',
|
|
368
|
-
og_scrape_url: 'https://linktr.ee/someone',
|
|
369
|
-
title: 'My Linktree',
|
|
370
|
-
},
|
|
371
|
-
],
|
|
372
|
-
})}
|
|
373
|
-
/>
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
expect(screen.queryByRole('img')).not.toBeInTheDocument()
|
|
377
|
-
expect(screen.getByText('My Linktree')).toBeInTheDocument()
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
it('does not show download or expand buttons for link cards', () => {
|
|
381
|
-
renderWithProviders(
|
|
382
|
-
<MediaMessage
|
|
383
|
-
message={msg({
|
|
384
|
-
attachments: [
|
|
385
|
-
{
|
|
386
|
-
type: 'link',
|
|
387
|
-
og_scrape_url: 'https://linktr.ee/someone',
|
|
388
|
-
title: 'My Linktree',
|
|
389
|
-
},
|
|
390
|
-
],
|
|
391
|
-
})}
|
|
392
|
-
/>
|
|
393
|
-
)
|
|
394
|
-
|
|
395
|
-
expect(
|
|
396
|
-
screen.queryByRole('button', { name: 'Download' })
|
|
397
|
-
).not.toBeInTheDocument()
|
|
398
|
-
expect(
|
|
399
|
-
screen.queryByRole('button', { name: 'View full screen' })
|
|
400
|
-
).not.toBeInTheDocument()
|
|
401
|
-
})
|
|
402
|
-
|
|
403
|
-
it('MediaMessage.Visitor renders card without chat shell', () => {
|
|
404
|
-
const { container } = renderWithProviders(
|
|
405
|
-
<MediaMessage.Visitor
|
|
406
|
-
message={msg({
|
|
407
|
-
attachments: [
|
|
408
|
-
{
|
|
409
|
-
type: 'image',
|
|
410
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
411
|
-
mime_type: 'image/jpeg',
|
|
412
|
-
title: 'Solo',
|
|
413
|
-
},
|
|
414
|
-
],
|
|
415
|
-
})}
|
|
416
|
-
/>
|
|
417
|
-
)
|
|
418
|
-
|
|
419
|
-
expect(
|
|
420
|
-
container.querySelector('.str-chat__message')
|
|
421
|
-
).not.toBeInTheDocument()
|
|
422
|
-
expect(screen.getByRole('button', { name: 'Download' })).toBeInTheDocument()
|
|
423
|
-
})
|
|
424
|
-
|
|
425
|
-
it('MediaMessage.Creator renders card without chat shell', () => {
|
|
426
|
-
const { container } = renderWithProviders(
|
|
427
|
-
<MediaMessage.Creator
|
|
428
|
-
message={msg({
|
|
429
|
-
attachments: [
|
|
430
|
-
{
|
|
431
|
-
type: 'image',
|
|
432
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
433
|
-
mime_type: 'image/jpeg',
|
|
434
|
-
},
|
|
435
|
-
],
|
|
436
|
-
})}
|
|
437
|
-
/>
|
|
438
|
-
)
|
|
439
|
-
|
|
440
|
-
expect(
|
|
441
|
-
container.querySelector('.str-chat__message')
|
|
442
|
-
).not.toBeInTheDocument()
|
|
443
|
-
expect(
|
|
444
|
-
screen.queryByRole('button', { name: 'Download' })
|
|
445
|
-
).not.toBeInTheDocument()
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
it('resolveLinkAttachment ignores empty-string og_scrape_url without asset_url', () => {
|
|
449
|
-
const message = msg({
|
|
450
|
-
attachments: [
|
|
451
|
-
{
|
|
452
|
-
type: 'image',
|
|
453
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
454
|
-
mime_type: 'image/jpeg',
|
|
455
|
-
og_scrape_url: '',
|
|
456
|
-
},
|
|
457
|
-
],
|
|
458
|
-
})
|
|
459
|
-
expect(resolveLinkAttachment(message)).toBeUndefined()
|
|
460
|
-
})
|
|
461
|
-
|
|
462
|
-
it('Download button triggers fetch and uses fallback on failure', async () => {
|
|
463
|
-
const fetchSpy = vi
|
|
464
|
-
.spyOn(global, 'fetch')
|
|
465
|
-
.mockRejectedValue(new Error('fail'))
|
|
466
|
-
const openSpy = vi.spyOn(window, 'open').mockReturnValue({
|
|
467
|
-
location: { href: '' },
|
|
468
|
-
close: vi.fn(),
|
|
469
|
-
} as unknown as Window)
|
|
470
|
-
|
|
471
|
-
renderWithProviders(
|
|
472
|
-
<MediaMessage
|
|
473
|
-
message={msg({
|
|
474
|
-
attachments: [
|
|
475
|
-
{
|
|
476
|
-
type: 'image',
|
|
477
|
-
image_url: 'https://cdn.example.com/photo.jpg',
|
|
478
|
-
mime_type: 'image/jpeg',
|
|
479
|
-
title: 'My Photo',
|
|
480
|
-
},
|
|
481
|
-
],
|
|
482
|
-
})}
|
|
483
|
-
/>
|
|
484
|
-
)
|
|
485
|
-
|
|
486
|
-
fireEvent.click(screen.getByRole('button', { name: 'Download' }))
|
|
487
|
-
await vi.waitFor(() => {
|
|
488
|
-
expect(fetchSpy).toHaveBeenCalled()
|
|
489
|
-
})
|
|
490
|
-
|
|
491
|
-
fetchSpy.mockRestore()
|
|
492
|
-
openSpy.mockRestore()
|
|
493
|
-
})
|
|
494
|
-
})
|