Skip to content

Commit 1f7a922

Browse files
Enigma-I-ammadsroskarNwagba Okechukwu
authored
feat: validates file extension before uploading files (#1281)
* using appSetting in FileUploadPreview to know if file type is in blocked types list in appSetting * completed naive implementation of permitted file/image types * fix: reimplemented supported attachment types to use response from server rather than appSettings * feat:supported image and file attachment state via backend response * feat: check if file/image is supported using appSettings * fix: move all styles from components to StyleSheet * fix: moved appSettings to InputMessageInputContextValue * fix: removed else block * Debugging image upload when file type is not supported * refactor: removes active state from upload indicator * refactor: extracts adding file states to a function This lets us easier reuse the function rather than copy every time * refactor: simplifies error handling after image upload * Revert "Revert "feat: add appSettings to ChatContext (#1262)"" This reverts commit 450ef9d. * fix: improved code readability * style: improved code readability, unsupport File and text warning moved to components of their own and text added to i18n * style: applies autolint fixes * test: add missing testIDs * fix: UnsupportedImageTypeIndicator displays on Error & keys added to i18n * style: resolved linter errors * fix: added appSettings to required components * style: fixed types errors * fix: not supported i18n keys * fix: not supported i18n keys added again and fixed lost keys * fix: removed Irrelevant test * fix: put required test back and fixed test * fix: test description * fix: updated tests snapshot * fix: removed unnecessary logs * fix: fixes concerning code-review from Mads * Fixes after first code-review with Mads & steve Removed appSettings from components and contexts where it was not needed Improved warning icon styling Refactored uploadNewFile and uploadNewImage function to run sequentially to avoid logical errors * style: chnages variable name for search in blocked files and images list * fix: checks mime types to know if file is blocked * fix: upload-progress-indicator components tests written for the four possible states * fix: removed appSettings from useCreateMessageInputContext hook * fix: Recovered lost translations * fix: refactored how list of blocked file/image/mime types are gotten from appSettings * refactor: handling image and file uploads errors in on function * refactor: removed white color from close icon * fix: updated test snapshots * fix: use relative positioning rather than absolute positioning in styling * fix: extract tenaries to variables * fix: fixed linting issue * fix: refactor functions to get file and image config from appSettings * fix: update variable name that indicates boolean to indicate file and image state rathar * fix: add correc typing to indicator map to improve maintainability * fix: remove array from unsupported view * fix: rectify indicatorStates type * fix: again, rectify indicatorStates by remove string and adding ProgressIndicatorState as value in type def * fix: resolve file upload state bug for not supported file types * fix: remove appSettings from fileuploadpreview component * fix: test for unsupported state in imageuploadpreview component * fix: test for unsupported state in fileuploadpreview component * fix: update test snapshot * fix: add better typing to indicatior type function * fix: linting fix * fix: remove unneeded reassignment * fix: remove unneeded rerender function calls and snapshot * fix: update snapshot * fix: removed unneeded exports * fix: give more descriptive variable names * fix: lint fix * fix: remove unnecessary exports and refactor indicatortype function * fix: refactor casting * fix: removed casting and renamed function * fix: refactor mapFileState function * fix: give more descriptive variable names * fix: extract icon size to variable * fix: use arrow function expression for consistency Co-authored-by: Mads Røskar <[email protected]> Co-authored-by: Mads Røskar <[email protected]> Co-authored-by: Nwagba Okechukwu <[email protected]>
1 parent 088d2bd commit 1f7a922

32 files changed

+747
-7166
lines changed

examples/TypeScriptMessaging/yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6861,10 +6861,10 @@ [email protected]:
68616861
resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4"
68626862
integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ=
68636863

6864-
stream-chat-react-native-core@4.2.0:
6865-
version "4.2.0"
6866-
resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-4.2.0.tgz#f3d183e2340aa515051582975ec8c9f9a2ffe284"
6867-
integrity sha512-d8gfi9OljPKB1l9+VhdLBfacBbZyKVsd/kZ9+neVuPIAtigrfCbGjhPDyxzeQfE8lg0iQEn4A+tHMuBpGGKrdg==
6864+
stream-chat-react-native-core@4.3.1:
6865+
version "4.3.1"
6866+
resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-4.3.1.tgz#bc53a044c45b60f6bd3feb425e70100d077cc064"
6867+
integrity sha512-2DZO89EyDtXtmc5MhivD1kOOU6aklK/rgZVvo4RZ15419VlpVFqMjswvFAcF41z2ZJujP86cSSKg5YMhNy7uZw==
68686868
dependencies:
68696869
"@babel/runtime" "^7.12.5"
68706870
"@gorhom/bottom-sheet" "4.1.5"

package/native-package/yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,10 +1619,10 @@ source-map@^0.5.0:
16191619
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
16201620
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
16211621

1622-
stream-chat-react-native-core@4.2.0:
1623-
version "4.2.0"
1624-
resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-4.2.0.tgz#f3d183e2340aa515051582975ec8c9f9a2ffe284"
1625-
integrity sha512-d8gfi9OljPKB1l9+VhdLBfacBbZyKVsd/kZ9+neVuPIAtigrfCbGjhPDyxzeQfE8lg0iQEn4A+tHMuBpGGKrdg==
1622+
stream-chat-react-native-core@4.3.1:
1623+
version "4.3.1"
1624+
resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-4.3.1.tgz#bc53a044c45b60f6bd3feb425e70100d077cc064"
1625+
integrity sha512-2DZO89EyDtXtmc5MhivD1kOOU6aklK/rgZVvo4RZ15419VlpVFqMjswvFAcF41z2ZJujP86cSSKg5YMhNy7uZw==
16261626
dependencies:
16271627
"@babel/runtime" "^7.12.5"
16281628
"@gorhom/bottom-sheet" "4.1.5"

package/src/components/Channel/Channel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,7 @@ const ChannelWithContext = <
16061606
watchers,
16071607
});
16081608

1609-
const inputMessageInputContext = useCreateInputMessageInputContext({
1609+
const inputMessageInputContext = useCreateInputMessageInputContext<StreamChatGenerics>({
16101610
additionalTextInputProps,
16111611
AttachButton,
16121612
autoCompleteSuggestionsLimit,

package/src/components/ChannelList/hooks/listeners/__tests__/useChannelUpdated.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ describe('useChannelUpdated', () => {
5555
render(
5656
<ChatContext.Provider
5757
value={{
58+
appSettings: null,
5859
client: mockClient,
5960
connectionRecovering: false,
6061
isOnline: true,

package/src/components/Chat/Chat.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Dayjs from 'dayjs';
55

66
import type { Channel } from 'stream-chat';
77

8+
import { useAppSettings } from './hooks/useAppSettings';
89
import { useCreateChatContext } from './hooks/useCreateChatContext';
910
import { useIsOnline } from './hooks/useIsOnline';
1011
import { useMutedUsers } from './hooks/useMutedUsers';
@@ -161,7 +162,10 @@ const ChatWithContext = <
161162

162163
const setActiveChannel = (newChannel?: Channel<StreamChatGenerics>) => setChannel(newChannel);
163164

165+
const appSettings = useAppSettings(client, isOnline);
166+
164167
const chatContext = useCreateChatContext({
168+
appSettings,
165169
channel,
166170
client,
167171
connectionRecovering,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import { Text } from 'react-native';
3+
4+
import { render, waitFor } from '@testing-library/react-native';
5+
6+
import type { StreamChat } from 'stream-chat';
7+
8+
import { useAppSettings } from '../useAppSettings';
9+
10+
describe('useAppSettings', () => {
11+
it('will return a settings object if the backend call is successful', async () => {
12+
const TestComponent = () => {
13+
const isOnline = true;
14+
const appSettings = useAppSettings(
15+
{
16+
getAppSettings: jest.fn().mockReturnValue(
17+
Promise.resolve({
18+
auto_translation_enabled: true,
19+
}),
20+
),
21+
} as unknown as StreamChat,
22+
isOnline,
23+
);
24+
25+
return <Text>{JSON.stringify(appSettings)}</Text>;
26+
};
27+
28+
const { getByText } = render(<TestComponent />);
29+
30+
await waitFor(() => {
31+
expect(getByText(JSON.stringify({ auto_translation_enabled: true }))).toBeTruthy();
32+
});
33+
});
34+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useEffect, useRef, useState } from 'react';
2+
3+
import type { AppSettingsAPIResponse, StreamChat } from 'stream-chat';
4+
import type { DefaultStreamChatGenerics } from 'stream-chat-react-native';
5+
6+
export const useAppSettings = <
7+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
8+
>(
9+
client: StreamChat<StreamChatGenerics>,
10+
isOnline: boolean,
11+
): AppSettingsAPIResponse | null => {
12+
const [appSettings, setAppSettings] = useState<AppSettingsAPIResponse | null>(null);
13+
const isMounted = useRef(true);
14+
15+
useEffect(() => {
16+
async function getAppSettings() {
17+
try {
18+
const appSettings = await client.getAppSettings();
19+
if (isMounted.current) {
20+
setAppSettings(appSettings);
21+
}
22+
} catch (error: unknown) {
23+
if (error instanceof Error) {
24+
console.error(`An error occurred while getting app settings: ${error}`);
25+
}
26+
}
27+
}
28+
29+
if (isOnline) {
30+
getAppSettings();
31+
}
32+
33+
return () => {
34+
isMounted.current = false;
35+
};
36+
}, [client, isOnline]);
37+
38+
return appSettings;
39+
};

package/src/components/Chat/hooks/useCreateChatContext.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { DefaultStreamChatGenerics } from '../../../types/types';
66
export const useCreateChatContext = <
77
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
88
>({
9+
appSettings,
910
channel,
1011
client,
1112
connectionRecovering,
@@ -21,6 +22,7 @@ export const useCreateChatContext = <
2122

2223
const chatContext: ChatContextValue<StreamChatGenerics> = useMemo(
2324
() => ({
25+
appSettings,
2426
channel,
2527
client,
2628
connectionRecovering,

package/src/components/MessageInput/FileUploadPreview.tsx

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ import {
1313
useMessagesContext,
1414
} from '../../contexts/messagesContext/MessagesContext';
1515
import { useTheme } from '../../contexts/themeContext/ThemeContext';
16+
import { useTranslationContext } from '../../contexts/translationContext/TranslationContext';
1617
import { Close } from '../../icons/Close';
18+
import { Warning } from '../../icons/Warning';
1719
import type { DefaultStreamChatGenerics } from '../../types/types';
18-
import { FileState, ProgressIndicatorTypes } from '../../utils/utils';
19-
20+
import { getIndicatorTypeForFileState, ProgressIndicatorTypes } from '../../utils/utils';
2021
import { getFileSizeDisplayText } from '../Attachment/FileAttachment';
2122

2223
const FILE_PREVIEW_HEIGHT = 60;
24+
const WARNING_ICON_SIZE = 16;
2325

2426
const styles = StyleSheet.create({
2527
dismiss: {
@@ -59,8 +61,55 @@ const styles = StyleSheet.create({
5961
marginLeft: 8,
6062
marginRight: 8,
6163
},
64+
unsupportedFile: {
65+
flexDirection: 'row',
66+
paddingLeft: 10,
67+
},
68+
unsupportedFileText: {
69+
fontSize: 16,
70+
},
71+
warningIconStyle: {
72+
borderRadius: 24,
73+
marginTop: 4,
74+
},
6275
});
6376

77+
const UnsupportedFileTypeOrFileSizeIndicator = ({
78+
indicatorType,
79+
item,
80+
}: {
81+
indicatorType: typeof ProgressIndicatorTypes[keyof typeof ProgressIndicatorTypes];
82+
item: FileUpload;
83+
}) => {
84+
const {
85+
theme: {
86+
colors: { accent_red, grey },
87+
messageInput: {
88+
fileUploadPreview: { fileSizeText },
89+
},
90+
},
91+
} = useTheme();
92+
93+
const { t } = useTranslationContext();
94+
return indicatorType === ProgressIndicatorTypes.NOT_SUPPORTED ? (
95+
<View style={styles.unsupportedFile}>
96+
<Warning
97+
height={WARNING_ICON_SIZE}
98+
pathFill={accent_red}
99+
style={styles.warningIconStyle}
100+
width={WARNING_ICON_SIZE}
101+
/>
102+
<Text style={[styles.unsupportedFileText, { color: grey }]}>
103+
{t('File type not supported')}
104+
</Text>
105+
</View>
106+
) : (
107+
<Text style={[styles.fileSizeText, { color: grey }, fileSizeText]}>
108+
{getFileSizeDisplayText(item.file.size)}
109+
</Text>
110+
);
111+
};
112+
64113
type FileUploadPreviewPropsWithContext<
65114
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
66115
> = Pick<
@@ -81,14 +130,13 @@ const FileUploadPreviewWithContext = <
81130

82131
const {
83132
theme: {
84-
colors: { black, grey, grey_whisper, overlay },
133+
colors: { black, grey_whisper, overlay },
85134
messageInput: {
86135
fileUploadPreview: {
87136
dismiss,
88137
fileContainer,
89138
fileContentContainer,
90139
filenameText,
91-
fileSizeText,
92140
fileTextContainer,
93141
flatList,
94142
},
@@ -97,20 +145,14 @@ const FileUploadPreviewWithContext = <
97145
} = useTheme();
98146

99147
const renderItem = ({ index, item }: { index: number; item: FileUpload }) => {
100-
const indicatorType =
101-
item.state === FileState.UPLOADING
102-
? ProgressIndicatorTypes.IN_PROGRESS
103-
: item.state === FileState.UPLOAD_FAILED
104-
? ProgressIndicatorTypes.RETRY
105-
: undefined;
148+
const indicatorType = getIndicatorTypeForFileState(item.state);
106149

107150
return (
108151
<>
109152
<UploadProgressIndicator
110153
action={() => {
111154
uploadFile({ newFile: item });
112155
}}
113-
active={item.state !== FileState.UPLOADED && item.state !== FileState.FINISHED}
114156
style={styles.overlay}
115157
type={indicatorType}
116158
>
@@ -150,9 +192,12 @@ const FileUploadPreviewWithContext = <
150192
>
151193
{item.file.name || ''}
152194
</Text>
153-
<Text style={[styles.fileSizeText, { color: grey }, fileSizeText]}>
154-
{getFileSizeDisplayText(item.file.size)}
155-
</Text>
195+
{indicatorType !== null && (
196+
<UnsupportedFileTypeOrFileSizeIndicator
197+
indicatorType={indicatorType}
198+
item={item}
199+
/>
200+
)}
156201
</View>
157202
</View>
158203
</View>
@@ -235,6 +280,7 @@ export const FileUploadPreview = <
235280
props: FileUploadPreviewProps<StreamChatGenerics>,
236281
) => {
237282
const { fileUploads, removeFile, uploadFile } = useMessageInputContext<StreamChatGenerics>();
283+
238284
const { FileAttachmentIcon } = useMessagesContext<StreamChatGenerics>();
239285

240286
return (

0 commit comments

Comments
 (0)