Skip to content

Commit 230cfa1

Browse files
authored
feat: allow custom icons (#3731)
## 🎯 Goal Allow providing custom icons Docs: GetStream/docs-content#1438 Fixes #3449 ## 🛠 Implementation details <!-- Provide a description of the implementation --> ## 🎨 UI Changes <!-- Add relevant screenshots --> <details> <summary>iOS</summary> <table> <thead> <tr> <td>Before</td> <td>After</td> </tr> </thead> <tbody> <tr> <td> <!--<img src="" /> --> </td> <td> <!--<img src="" /> --> </td> </tr> </tbody> </table> </details> <details> <summary>Android</summary> <table> <thead> <tr> <td>Before</td> <td>After</td> </tr> </thead> <tbody> <tr> <td> <!--<img src="" /> --> </td> <td> <!--<img src="" /> --> </td> </tr> </tbody> </table> </details> ## 🧪 Testing <!-- Explain how this change can be tested (or why it can't be tested) --> ## ☑️ Checklist - [ ] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [ ] PR targets the `develop` branch - [ ] Documentation is updated - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android
1 parent 0a38205 commit 230cfa1

97 files changed

Lines changed: 758 additions & 448 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.

package/src/components/Attachment/Audio/PlayPauseButton.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import React, { useMemo } from 'react';
22
import { Pressable, PressableProps, StyleProp, StyleSheet, ViewStyle } from 'react-native';
33

44
import { useTheme } from '../../../contexts';
5-
import { Pause } from '../../../icons/pause-fill';
6-
import { Play } from '../../../icons/play-fill';
5+
import { useComponentsContext } from '../../../contexts/componentsContext/ComponentsContext';
76
import { primitives } from '../../../theme';
87
import { buttonSizes } from '../../ui/Button/constants';
98

@@ -31,6 +30,7 @@ export const PlayPauseButton = ({
3130
const {
3231
theme: { semantics },
3332
} = useTheme();
33+
const { icons } = useComponentsContext();
3434
const styles = useStyles();
3535
return (
3636
<Pressable
@@ -45,9 +45,9 @@ export const PlayPauseButton = ({
4545
{...rest}
4646
>
4747
{isPlaying ? (
48-
<Pause fill={semantics.textSecondary} height={20} width={20} strokeWidth={1.5} />
48+
<icons.Pause fill={semantics.textSecondary} height={20} width={20} strokeWidth={1.5} />
4949
) : (
50-
<Play fill={semantics.textSecondary} height={20} width={20} strokeWidth={1.5} />
50+
<icons.Play fill={semantics.textSecondary} height={20} width={20} strokeWidth={1.5} />
5151
)}
5252
</Pressable>
5353
);

package/src/components/Attachment/FileIcon.tsx

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
11
import React from 'react';
22

3+
import { useComponentsContext } from '../../contexts/componentsContext/ComponentsContext';
4+
import type { IconsMap } from '../../contexts/componentsContext/defaultComponents';
35
import { useTheme } from '../../contexts/themeContext/ThemeContext';
46

5-
import { Audio } from '../../icons/filetype-audio-xl';
6-
import { Code } from '../../icons/filetype-code-xl';
7-
import { ZIP } from '../../icons/filetype-compression-xl';
8-
import { OtherFileIcon } from '../../icons/filetype-other-xl';
9-
import { PDF } from '../../icons/filetype-pdf-xl';
10-
import { Presentation } from '../../icons/filetype-presentation-xl';
11-
import { SpreadSheet } from '../../icons/filetype-spreadsheet-xl';
12-
import { DOC } from '../../icons/filetype-text-xl';
13-
import { Video } from '../../icons/filetype-video-xl';
14-
import type { IconProps } from '../../icons/utils/base';
15-
167
// https://www.iana.org/assignments/media-types/media-types.xhtml#audio
178
const audioFileTypes = [
189
'audio/1d-interleaved-parityfec',
@@ -349,49 +340,49 @@ const codeFileTypes = [
349340
'text/plain',
350341
];
351342

352-
const mimeTypeToIconMap: Record<string, React.ComponentType<IconProps>> = {
353-
'application/pdf': PDF, // .pdf
343+
const mimeTypeToIconKeyMap: Record<string, keyof IconsMap> = {
344+
'application/pdf': 'PDF', // .pdf
354345
};
355346

356347
for (const type of audioFileTypes) {
357-
mimeTypeToIconMap[type] = Audio;
348+
mimeTypeToIconKeyMap[type] = 'Audio';
358349
}
359350

360351
for (const type of docMimeTypes) {
361-
mimeTypeToIconMap[type] = DOC;
352+
mimeTypeToIconKeyMap[type] = 'DOC';
362353
}
363354

364355
for (const type of excelMimeTypes) {
365-
mimeTypeToIconMap[type] = SpreadSheet;
356+
mimeTypeToIconKeyMap[type] = 'SpreadSheet';
366357
}
367358

368359
for (const type of powerpointMimeTypes) {
369-
mimeTypeToIconMap[type] = Presentation;
360+
mimeTypeToIconKeyMap[type] = 'Presentation';
370361
}
371362

372363
for (const type of zipFileTypes) {
373-
mimeTypeToIconMap[type] = ZIP;
364+
mimeTypeToIconKeyMap[type] = 'ZIP';
374365
}
375366

376367
for (const type of videoFileTypes) {
377-
mimeTypeToIconMap[type] = Video;
368+
mimeTypeToIconKeyMap[type] = 'Video';
378369
}
379370

380371
for (const type of codeFileTypes) {
381-
mimeTypeToIconMap[type] = Code;
372+
mimeTypeToIconKeyMap[type] = 'Code';
382373
}
383374

384-
function mimeTypeToIcon(mimeType?: string): React.ComponentType<IconProps> {
375+
function mimeTypeToIconKey(mimeType?: string): keyof IconsMap {
385376
if (!mimeType) {
386-
return OtherFileIcon;
377+
return 'OtherFileIcon';
387378
}
388379

389-
const Icon = mimeTypeToIconMap[mimeType];
390-
if (Icon) {
391-
return Icon;
380+
const iconKey = mimeTypeToIconKeyMap[mimeType];
381+
if (iconKey) {
382+
return iconKey;
392383
}
393384

394-
return OtherFileIcon;
385+
return 'OtherFileIcon';
395386
}
396387

397388
export type FileIconProps = {
@@ -426,8 +417,9 @@ export const FileIcon = ({ mimeType, size = 'md' }: FileIconProps) => {
426417
},
427418
},
428419
} = useTheme();
420+
const { icons } = useComponentsContext();
429421

430-
const Icon = mimeTypeToIcon(mimeType);
422+
const Icon = icons[mimeTypeToIconKey(mimeType)];
431423

432424
return <Icon {...(size ? sizeToNumber(size) : {})} {...icon} />;
433425
};

package/src/components/Attachment/Giphy/Giphy.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { Attachment } from 'stream-chat';
55

66
import { GiphyImage } from './GiphyImage';
77

8+
import { useComponentsContext } from '../../../contexts/componentsContext/ComponentsContext';
89
import {
910
MessageContextValue,
1011
useMessageContext,
@@ -16,7 +17,6 @@ import {
1617
import { useTheme } from '../../../contexts/themeContext/ThemeContext';
1718
import { useTranslationContext } from '../../../contexts/translationContext/TranslationContext';
1819

19-
import { EyeOpen } from '../../../icons/EyeOpen';
2020
import { components, primitives } from '../../../theme';
2121
import { Button } from '../../ui/';
2222

@@ -43,6 +43,7 @@ const GiphyWithContext = (props: GiphyPropsWithContext) => {
4343

4444
const { actions, image_url, thumb_url } = attachment;
4545

46+
const { icons } = useComponentsContext();
4647
const { t } = useTranslationContext();
4748

4849
const {
@@ -68,7 +69,7 @@ const GiphyWithContext = (props: GiphyPropsWithContext) => {
6869
testID='giphy-action-attachment'
6970
>
7071
<View style={[styles.header, header]}>
71-
<EyeOpen height={16} width={16} fill={semantics.chatTextOutgoing} />
72+
<icons.EyeOpen height={16} width={16} fill={semantics.chatTextOutgoing} />
7273
<Text style={[styles.headerText, giphyHeaderText]}>{t('Only visible to you')}</Text>
7374
</View>
7475
<GiphyImage attachment={attachment} giphyVersion={giphyVersion} preview />

package/src/components/Attachment/UrlPreview/URLPreview.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import {
2525
useMessagesContext,
2626
} from '../../../contexts/messagesContext/MessagesContext';
2727
import { useTheme } from '../../../contexts/themeContext/ThemeContext';
28-
import { Link } from '../../../icons/link';
2928
import { primitives } from '../../../theme';
3029
import { FileTypes } from '../../../types/types';
3130
import { makeImageCompatibleUrl } from '../../../utils/utils';
@@ -67,6 +66,8 @@ const URLPreviewWithContext = (props: URLPreviewPropsWithContext) => {
6766
styles: stylesProp = {},
6867
} = props;
6968

69+
const { icons } = useComponentsContext();
70+
7071
const {
7172
theme: { semantics },
7273
} = useTheme();
@@ -155,7 +156,7 @@ const URLPreviewWithContext = (props: URLPreviewPropsWithContext) => {
155156
</Text>
156157
) : null}
157158
<View style={[styles.linkPreview, linkPreview, stylesProp.linkPreview]}>
158-
<Link height={12} width={12} stroke={semantics.chatTextIncoming} />
159+
<icons.Link height={12} width={12} stroke={semantics.chatTextIncoming} />
159160
<Text
160161
numberOfLines={1}
161162
style={[styles.linkPreviewText, linkPreviewText, stylesProp.linkPreviewText]}

package/src/components/Attachment/UrlPreview/URLPreviewCompact.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import {
2525
useMessagesContext,
2626
} from '../../../contexts/messagesContext/MessagesContext';
2727
import { useTheme } from '../../../contexts/themeContext/ThemeContext';
28-
import { Link } from '../../../icons/link';
2928
import { primitives } from '../../../theme';
3029
import { FileTypes } from '../../../types/types';
3130
import { makeImageCompatibleUrl } from '../../../utils/utils';
@@ -63,6 +62,8 @@ const URLPreviewCompactWithContext = (props: URLPreviewCompactPropsWithContext)
6362
styles: stylesProp = {},
6463
} = props;
6564

65+
const { icons } = useComponentsContext();
66+
6667
const {
6768
theme: { semantics },
6869
} = useTheme();
@@ -157,7 +158,7 @@ const URLPreviewCompactWithContext = (props: URLPreviewCompactPropsWithContext)
157158
</Text>
158159
) : null}
159160
<View style={[styles.linkPreview, linkPreview, stylesProp.linkPreview]}>
160-
<Link height={12} width={12} stroke={semantics.chatTextIncoming} />
161+
<icons.Link height={12} width={12} stroke={semantics.chatTextIncoming} />
161162
<Text
162163
numberOfLines={1}
163164
style={[styles.linkPreviewText, linkPreviewText, stylesProp.linkPreviewText]}

package/src/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentMediaPicker.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { renderAttachmentPickerItem } from './AttachmentPickerItem';
55
import { IOS_LIMITED_DEEPLINK, type PhotoContentItemType } from './shared';
66

77
import { useAttachmentPickerContext, useTheme, useTranslationContext } from '../../../../contexts';
8+
import { useComponentsContext } from '../../../../contexts/componentsContext/ComponentsContext';
89

910
import { useStableCallback } from '../../../../hooks';
10-
import { Picture } from '../../../../icons';
1111

1212
import { NativeHandlers } from '../../../../native';
1313
import { BottomSheetFlatList } from '../../../BottomSheetCompatibility/BottomSheetFlatList';
@@ -38,8 +38,9 @@ export const AttachmentMediaPickerIcon = () => {
3838
const {
3939
theme: { semantics },
4040
} = useTheme();
41+
const { icons } = useComponentsContext();
4142

42-
return <Picture height={22} stroke={semantics.textTertiary} width={22} />;
43+
return <icons.Picture height={22} stroke={semantics.textTertiary} width={22} />;
4344
};
4445

4546
export const AttachmentMediaPicker = (props: AttachmentPickerContentProps) => {

package/src/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { useMessageInputContext } from '../../../../contexts/messageInputContext
1515
import { useTheme } from '../../../../contexts/themeContext/ThemeContext';
1616
import { useTranslationContext } from '../../../../contexts/translationContext/TranslationContext';
1717
import { useViewport } from '../../../../hooks/useViewport';
18-
import { Plus } from '../../../../icons/plus';
1918
import { NativeHandlers } from '../../../../native';
2019
import { primitives } from '../../../../theme';
2120
import type { File } from '../../../../types/types';
@@ -163,6 +162,7 @@ const AttachmentImage = (props: AttachmentPickerItemType) => {
163162

164163
const AttachmentIosLimited = () => {
165164
const { numberOfAttachmentPickerImageColumns } = useAttachmentPickerContext();
165+
const { icons } = useComponentsContext();
166166
const { vw } = useViewport();
167167
const { t } = useTranslationContext();
168168
const size = vw(100) / (numberOfAttachmentPickerImageColumns || 3) - 2;
@@ -178,7 +178,7 @@ const AttachmentIosLimited = () => {
178178
]}
179179
onPress={NativeHandlers.iOS14RefreshGallerySelection}
180180
>
181-
<Plus width={20} height={20} stroke={styles.iosLimitedIcon.color} strokeWidth={1.5} />
181+
<icons.Plus width={20} height={20} stroke={styles.iosLimitedIcon.color} strokeWidth={1.5} />
182182
<Text style={styles.iosLimitedText}>{t('Add more')}</Text>
183183
</BottomSheetTouchableOpacity>
184184
);

package/src/components/AttachmentPicker/components/AttachmentPickerContent.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ import {
1919
useMessageInputContext,
2020
useTranslationContext,
2121
} from '../../../contexts';
22+
import { useComponentsContext } from '../../../contexts/componentsContext/ComponentsContext';
2223
import { useTheme } from '../../../contexts/themeContext/ThemeContext';
2324
import { useAttachmentPickerState, useStableCallback } from '../../../hooks';
24-
import { Camera, FilePickerIcon, PollThumbnail, VideoIcon } from '../../../icons';
2525
import { primitives } from '../../../theme';
2626
import { CommandSuggestionItem } from '../../AutoCompleteInput/AutoCompleteSuggestionItem';
2727

@@ -149,6 +149,7 @@ export const AttachmentCommandPicker = () => {
149149

150150
export const AttachmentPollPicker = (props: AttachmentPickerContentProps) => {
151151
const { t } = useTranslationContext();
152+
const { icons } = useComponentsContext();
152153
const { height } = props;
153154
const { openPollCreationDialog, sendMessage } = useMessageInputContext();
154155

@@ -158,7 +159,7 @@ export const AttachmentPollPicker = (props: AttachmentPickerContentProps) => {
158159

159160
return (
160161
<AttachmentPickerGenericContent
161-
Icon={PollThumbnail}
162+
Icon={icons.PollThumbnail}
162163
onPress={openPollCreationModal}
163164
height={height}
164165
buttonText={t('Create Poll')}
@@ -172,6 +173,7 @@ export const AttachmentCameraPicker = (
172173
) => {
173174
const [permissionDenied, setPermissionDenied] = useState(false);
174175
const { t } = useTranslationContext();
176+
const { icons } = useComponentsContext();
175177
const { height, videoOnly } = props;
176178
const { takeAndUploadImage } = useMessageInputContext();
177179

@@ -198,15 +200,15 @@ export const AttachmentCameraPicker = (
198200

199201
return permissionDenied ? (
200202
<AttachmentPickerGenericContent
201-
Icon={Camera}
203+
Icon={icons.Camera}
202204
onPress={openSettings}
203205
height={height}
204206
buttonText={t('Change in Settings')}
205207
description={t('You have not granted access to your camera')}
206208
/>
207209
) : (
208210
<AttachmentPickerGenericContent
209-
Icon={videoOnly ? VideoIcon : Camera}
211+
Icon={videoOnly ? icons.VideoIcon : icons.Camera}
210212
onPress={openCameraPicker}
211213
height={height}
212214
buttonText={t('Open Camera')}
@@ -217,12 +219,13 @@ export const AttachmentCameraPicker = (
217219

218220
export const AttachmentFilePicker = (props: AttachmentPickerContentProps) => {
219221
const { t } = useTranslationContext();
222+
const { icons } = useComponentsContext();
220223
const { height } = props;
221224
const { pickFile } = useMessageInputContext();
222225

223226
return (
224227
<AttachmentPickerGenericContent
225-
Icon={FilePickerIcon}
228+
Icon={icons.FilePickerIcon}
226229
onPress={pickFile}
227230
height={height}
228231
buttonText={t('Open Files')}

0 commit comments

Comments
 (0)