@amityco/react-native-social-uikit 4.0.0-RC9 → 4.0.0-abecf5a.0
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/lib/commonjs/components/Toast/Toast.js +2 -1
- package/lib/commonjs/components/Toast/Toast.js.map +1 -1
- package/lib/commonjs/providers/file-provider.js +38 -19
- package/lib/commonjs/providers/file-provider.js.map +1 -1
- package/lib/commonjs/svg/svg-xml-list.js +6 -1
- package/lib/commonjs/svg/svg-xml-list.js.map +1 -1
- package/lib/commonjs/v4/PublicApi/Pages/AmityPostComposerPage/AmityPostComposerPage.js +38 -2
- package/lib/commonjs/v4/PublicApi/Pages/AmityPostComposerPage/AmityPostComposerPage.js.map +1 -1
- package/lib/commonjs/v4/PublicApi/Pages/AmityViewStoryPage/Components/AmityViewStoryItem.js +54 -21
- package/lib/commonjs/v4/PublicApi/Pages/AmityViewStoryPage/Components/AmityViewStoryItem.js.map +1 -1
- package/lib/commonjs/v4/PublicApi/Pages/AmityViewStoryPage/styles.js +24 -2
- package/lib/commonjs/v4/PublicApi/Pages/AmityViewStoryPage/styles.js.map +1 -1
- package/lib/commonjs/v4/assets/icons/toast.js +6 -6
- package/lib/commonjs/v4/assets/icons/toast.js.map +1 -1
- package/lib/commonjs/v4/component/CommunityStories/index.js +9 -4
- package/lib/commonjs/v4/component/CommunityStories/index.js.map +1 -1
- package/lib/commonjs/v4/component/CommunityStories/styles.js +7 -2
- package/lib/commonjs/v4/component/CommunityStories/styles.js.map +1 -1
- package/lib/commonjs/v4/component/LoadingImage/index.js +22 -25
- package/lib/commonjs/v4/component/LoadingImage/index.js.map +1 -1
- package/lib/commonjs/v4/component/LoadingImage/styles.js +19 -2
- package/lib/commonjs/v4/component/LoadingImage/styles.js.map +1 -1
- package/lib/commonjs/v4/component/LoadingVideo/index.js +12 -6
- package/lib/commonjs/v4/component/LoadingVideo/index.js.map +1 -1
- package/lib/commonjs/v4/component/MyStories/StoryCircleItem.js +11 -4
- package/lib/commonjs/v4/component/MyStories/StoryCircleItem.js.map +1 -1
- package/lib/commonjs/v4/component/MyStories/styles.js +5 -0
- package/lib/commonjs/v4/component/MyStories/styles.js.map +1 -1
- package/lib/commonjs/v4/component/PollContent/PollResults.js +14 -6
- package/lib/commonjs/v4/component/PollContent/PollResults.js.map +1 -1
- package/lib/commonjs/v4/component/PreviewLink/LinkPreview.js +3 -3
- package/lib/commonjs/v4/component/PreviewLink/LinkPreview.js.map +1 -1
- package/lib/commonjs/v4/component/PreviewLink/utils.js +9 -73
- package/lib/commonjs/v4/component/PreviewLink/utils.js.map +1 -1
- package/lib/module/components/Toast/Toast.js +2 -1
- package/lib/module/components/Toast/Toast.js.map +1 -1
- package/lib/module/providers/file-provider.js +38 -19
- package/lib/module/providers/file-provider.js.map +1 -1
- package/lib/module/svg/svg-xml-list.js +4 -0
- package/lib/module/svg/svg-xml-list.js.map +1 -1
- package/lib/module/v4/PublicApi/Pages/AmityPostComposerPage/AmityPostComposerPage.js +38 -2
- package/lib/module/v4/PublicApi/Pages/AmityPostComposerPage/AmityPostComposerPage.js.map +1 -1
- package/lib/module/v4/PublicApi/Pages/AmityViewStoryPage/Components/AmityViewStoryItem.js +54 -21
- package/lib/module/v4/PublicApi/Pages/AmityViewStoryPage/Components/AmityViewStoryItem.js.map +1 -1
- package/lib/module/v4/PublicApi/Pages/AmityViewStoryPage/styles.js +24 -2
- package/lib/module/v4/PublicApi/Pages/AmityViewStoryPage/styles.js.map +1 -1
- package/lib/module/v4/assets/icons/toast.js +6 -6
- package/lib/module/v4/assets/icons/toast.js.map +1 -1
- package/lib/module/v4/component/CommunityStories/index.js +10 -5
- package/lib/module/v4/component/CommunityStories/index.js.map +1 -1
- package/lib/module/v4/component/CommunityStories/styles.js +7 -2
- package/lib/module/v4/component/CommunityStories/styles.js.map +1 -1
- package/lib/module/v4/component/LoadingImage/index.js +22 -24
- package/lib/module/v4/component/LoadingImage/index.js.map +1 -1
- package/lib/module/v4/component/LoadingImage/styles.js +19 -2
- package/lib/module/v4/component/LoadingImage/styles.js.map +1 -1
- package/lib/module/v4/component/LoadingVideo/index.js +12 -6
- package/lib/module/v4/component/LoadingVideo/index.js.map +1 -1
- package/lib/module/v4/component/MyStories/StoryCircleItem.js +13 -6
- package/lib/module/v4/component/MyStories/StoryCircleItem.js.map +1 -1
- package/lib/module/v4/component/MyStories/styles.js +5 -0
- package/lib/module/v4/component/MyStories/styles.js.map +1 -1
- package/lib/module/v4/component/PollContent/PollResults.js +14 -6
- package/lib/module/v4/component/PollContent/PollResults.js.map +1 -1
- package/lib/module/v4/component/PreviewLink/LinkPreview.js +3 -3
- package/lib/module/v4/component/PreviewLink/LinkPreview.js.map +1 -1
- package/lib/module/v4/component/PreviewLink/utils.js +9 -73
- package/lib/module/v4/component/PreviewLink/utils.js.map +1 -1
- package/lib/typescript/src/components/Toast/Toast.d.ts.map +1 -1
- package/lib/typescript/src/providers/file-provider.d.ts.map +1 -1
- package/lib/typescript/src/svg/svg-xml-list.d.ts +1 -0
- package/lib/typescript/src/svg/svg-xml-list.d.ts.map +1 -1
- package/lib/typescript/src/v4/PublicApi/Pages/AmityPostComposerPage/AmityPostComposerPage.d.ts.map +1 -1
- package/lib/typescript/src/v4/PublicApi/Pages/AmityViewStoryPage/Components/AmityViewStoryItem.d.ts.map +1 -1
- package/lib/typescript/src/v4/PublicApi/Pages/AmityViewStoryPage/styles.d.ts +22 -0
- package/lib/typescript/src/v4/PublicApi/Pages/AmityViewStoryPage/styles.d.ts.map +1 -1
- package/lib/typescript/src/v4/assets/icons/toast.d.ts +3 -3
- package/lib/typescript/src/v4/assets/icons/toast.d.ts.map +1 -1
- package/lib/typescript/src/v4/component/CommunityStories/index.d.ts.map +1 -1
- package/lib/typescript/src/v4/component/CommunityStories/styles.d.ts +5 -0
- package/lib/typescript/src/v4/component/CommunityStories/styles.d.ts.map +1 -1
- package/lib/typescript/src/v4/component/LoadingImage/index.d.ts +2 -1
- package/lib/typescript/src/v4/component/LoadingImage/index.d.ts.map +1 -1
- package/lib/typescript/src/v4/component/LoadingImage/styles.d.ts +21 -0
- package/lib/typescript/src/v4/component/LoadingImage/styles.d.ts.map +1 -1
- package/lib/typescript/src/v4/component/LoadingVideo/index.d.ts +2 -1
- package/lib/typescript/src/v4/component/LoadingVideo/index.d.ts.map +1 -1
- package/lib/typescript/src/v4/component/MyStories/StoryCircleItem.d.ts.map +1 -1
- package/lib/typescript/src/v4/component/MyStories/styles.d.ts +5 -0
- package/lib/typescript/src/v4/component/MyStories/styles.d.ts.map +1 -1
- package/lib/typescript/src/v4/component/PollContent/PollResults.d.ts.map +1 -1
- package/lib/typescript/src/v4/component/PreviewLink/utils.d.ts +6 -1
- package/lib/typescript/src/v4/component/PreviewLink/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Toast/Toast.tsx +1 -0
- package/src/providers/file-provider.tsx +42 -20
- package/src/svg/svg-xml-list.ts +6 -0
- package/src/v4/PublicApi/Pages/AmityPostComposerPage/AmityPostComposerPage.tsx +47 -1
- package/src/v4/PublicApi/Pages/AmityViewStoryPage/Components/AmityViewStoryItem.tsx +122 -57
- package/src/v4/PublicApi/Pages/AmityViewStoryPage/styles.ts +23 -1
- package/src/v4/assets/icons/toast.tsx +12 -9
- package/src/v4/component/CommunityStories/index.tsx +26 -17
- package/src/v4/component/CommunityStories/styles.ts +7 -2
- package/src/v4/component/LoadingImage/index.tsx +28 -25
- package/src/v4/component/LoadingImage/styles.ts +17 -0
- package/src/v4/component/LoadingVideo/index.tsx +13 -7
- package/src/v4/component/MyStories/StoryCircleItem.tsx +19 -12
- package/src/v4/component/MyStories/styles.ts +5 -0
- package/src/v4/component/PollContent/PollResults.tsx +16 -11
- package/src/v4/component/PreviewLink/LinkPreview.tsx +3 -3
- package/src/v4/component/PreviewLink/utils.ts +9 -108
|
@@ -8,10 +8,6 @@ import {
|
|
|
8
8
|
} from '../../../providers/file-provider';
|
|
9
9
|
import { closeIcon, toastIcon } from '../../../svg/svg-xml-list';
|
|
10
10
|
import { useStyles } from './styles';
|
|
11
|
-
import { useTheme } from 'react-native-paper';
|
|
12
|
-
import type { MyMD3Theme } from '../../../providers/amity-ui-kit-provider';
|
|
13
|
-
import uiSlice from '../../../redux/slices/uiSlice';
|
|
14
|
-
import { useUIKitDispatch } from '../../../redux/store';
|
|
15
11
|
|
|
16
12
|
interface OverlayImageProps {
|
|
17
13
|
source: string;
|
|
@@ -23,6 +19,7 @@ interface OverlayImageProps {
|
|
|
23
19
|
index: number,
|
|
24
20
|
originalPath: string
|
|
25
21
|
) => void;
|
|
22
|
+
onUploadError?: (hasError: boolean, source: string) => void;
|
|
26
23
|
index?: number;
|
|
27
24
|
isUploaded: boolean;
|
|
28
25
|
fileId?: string;
|
|
@@ -36,6 +33,7 @@ const LoadingImage = ({
|
|
|
36
33
|
onClose,
|
|
37
34
|
index,
|
|
38
35
|
onLoadFinish,
|
|
36
|
+
onUploadError,
|
|
39
37
|
isUploaded = false,
|
|
40
38
|
fileId = '',
|
|
41
39
|
isEditMode = false,
|
|
@@ -43,9 +41,6 @@ const LoadingImage = ({
|
|
|
43
41
|
postId,
|
|
44
42
|
setIsUploading,
|
|
45
43
|
}: OverlayImageProps) => {
|
|
46
|
-
const theme = useTheme() as MyMD3Theme;
|
|
47
|
-
const dispatch = useUIKitDispatch();
|
|
48
|
-
const { showToastMessage } = uiSlice.actions;
|
|
49
44
|
const [loading, setLoading] = useState(true);
|
|
50
45
|
const [progress, setProgress] = useState(0);
|
|
51
46
|
const [isProcess, setIsProcess] = useState<boolean>(false);
|
|
@@ -84,33 +79,39 @@ const LoadingImage = ({
|
|
|
84
79
|
source
|
|
85
80
|
);
|
|
86
81
|
} else {
|
|
82
|
+
setIsUploading(false);
|
|
87
83
|
handleLoadEnd();
|
|
88
|
-
|
|
84
|
+
setIsProcess(false);
|
|
89
85
|
setIsUploadError(true);
|
|
86
|
+
onUploadError?.(true, source);
|
|
90
87
|
}
|
|
91
88
|
} catch (error) {
|
|
92
89
|
handleLoadEnd();
|
|
93
|
-
|
|
90
|
+
setIsProcess(false);
|
|
91
|
+
setIsUploading(false);
|
|
94
92
|
setIsUploadError(true);
|
|
93
|
+
onUploadError?.(true, source);
|
|
95
94
|
}
|
|
96
95
|
}, [
|
|
97
|
-
dispatch,
|
|
98
96
|
handleLoadEnd,
|
|
99
97
|
index,
|
|
100
98
|
onLoadFinish,
|
|
99
|
+
onUploadError,
|
|
101
100
|
setIsUploading,
|
|
102
|
-
showToastMessage,
|
|
103
101
|
source,
|
|
104
102
|
]);
|
|
105
103
|
|
|
106
104
|
const handleDelete = async () => {
|
|
107
|
-
if (!
|
|
108
|
-
if (!isEditMode) {
|
|
105
|
+
if (fileId && !isEditMode) {
|
|
109
106
|
await deleteAmityFile(fileId);
|
|
110
107
|
}
|
|
111
108
|
onClose && onClose(source, fileId, postId);
|
|
112
109
|
};
|
|
113
110
|
useEffect(() => {
|
|
111
|
+
setIsUploadError(false);
|
|
112
|
+
onUploadError?.(false, source);
|
|
113
|
+
setProgress(0);
|
|
114
|
+
setIsProcess(false);
|
|
114
115
|
if (isUploaded) {
|
|
115
116
|
setLoading(false);
|
|
116
117
|
} else {
|
|
@@ -151,19 +152,21 @@ const LoadingImage = ({
|
|
|
151
152
|
)}
|
|
152
153
|
</View>
|
|
153
154
|
)}
|
|
154
|
-
{!loading && isUploadError
|
|
155
|
-
<
|
|
156
|
-
<
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
style={styles.closeButton}
|
|
161
|
-
disabled={loading || isProcess}
|
|
162
|
-
onPress={handleDelete}
|
|
163
|
-
>
|
|
164
|
-
<SvgXml xml={closeIcon(theme.colors.base)} width="12" height="12" />
|
|
165
|
-
</TouchableOpacity>
|
|
155
|
+
{!loading && isUploadError && (
|
|
156
|
+
<View style={styles.failedOverlay}>
|
|
157
|
+
<TouchableOpacity style={styles.errorOverlay} onPress={onRetryUpload}>
|
|
158
|
+
<SvgXml xml={toastIcon()} width="28" height="28" />
|
|
159
|
+
</TouchableOpacity>
|
|
160
|
+
</View>
|
|
166
161
|
)}
|
|
162
|
+
|
|
163
|
+
<TouchableOpacity
|
|
164
|
+
style={styles.closeButton}
|
|
165
|
+
disabled={(loading || isProcess) && !isUploadError}
|
|
166
|
+
onPress={handleDelete}
|
|
167
|
+
>
|
|
168
|
+
<SvgXml xml={closeIcon('white')} width="12" height="12" />
|
|
169
|
+
</TouchableOpacity>
|
|
167
170
|
</View>
|
|
168
171
|
);
|
|
169
172
|
};
|
|
@@ -18,12 +18,28 @@ export const useStyles = () => {
|
|
|
18
18
|
height: '100%',
|
|
19
19
|
resizeMode: 'cover',
|
|
20
20
|
borderRadius: 5,
|
|
21
|
+
position: 'relative',
|
|
21
22
|
},
|
|
22
23
|
overlay: {
|
|
23
24
|
...StyleSheet.absoluteFillObject,
|
|
24
25
|
justifyContent: 'center',
|
|
25
26
|
alignItems: 'center',
|
|
26
27
|
},
|
|
28
|
+
failedOverlay: {
|
|
29
|
+
...StyleSheet.absoluteFillObject,
|
|
30
|
+
justifyContent: 'center',
|
|
31
|
+
alignItems: 'center',
|
|
32
|
+
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
33
|
+
borderRadius: 5,
|
|
34
|
+
},
|
|
35
|
+
errorOverlay: {
|
|
36
|
+
position: 'absolute',
|
|
37
|
+
top: '50%',
|
|
38
|
+
left: 0,
|
|
39
|
+
right: 0,
|
|
40
|
+
alignItems: 'center',
|
|
41
|
+
justifyContent: 'center',
|
|
42
|
+
},
|
|
27
43
|
progressBar: {
|
|
28
44
|
marginVertical: 10,
|
|
29
45
|
},
|
|
@@ -40,6 +56,7 @@ export const useStyles = () => {
|
|
|
40
56
|
padding: 7,
|
|
41
57
|
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
42
58
|
borderRadius: 72,
|
|
59
|
+
zIndex: 10,
|
|
43
60
|
},
|
|
44
61
|
});
|
|
45
62
|
};
|
|
@@ -18,8 +18,6 @@ import { useStyles } from './styles';
|
|
|
18
18
|
import Video from 'react-native-video';
|
|
19
19
|
import { useNavigation } from '@react-navigation/native';
|
|
20
20
|
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
|
21
|
-
import { useTheme } from 'react-native-paper';
|
|
22
|
-
import type { MyMD3Theme } from '../../../providers/amity-ui-kit-provider';
|
|
23
21
|
import uiSlice from '../../../redux/slices/uiSlice';
|
|
24
22
|
import { createVideoThumbnail } from 'react-native-compressor';
|
|
25
23
|
import { useUIKitDispatch } from '../../../redux/store';
|
|
@@ -35,6 +33,7 @@ interface OverlayImageProps {
|
|
|
35
33
|
originalPath: string,
|
|
36
34
|
thumbNail: string
|
|
37
35
|
) => void;
|
|
36
|
+
onUploadError?: (hasError: boolean, source: string) => void;
|
|
38
37
|
index?: number;
|
|
39
38
|
isUploaded: boolean;
|
|
40
39
|
fileId?: string;
|
|
@@ -50,6 +49,7 @@ const LoadingVideo = ({
|
|
|
50
49
|
onClose,
|
|
51
50
|
index,
|
|
52
51
|
onLoadFinish,
|
|
52
|
+
onUploadError,
|
|
53
53
|
isUploaded = false,
|
|
54
54
|
thumbNail,
|
|
55
55
|
onPlay,
|
|
@@ -59,7 +59,6 @@ const LoadingVideo = ({
|
|
|
59
59
|
postId,
|
|
60
60
|
setIsUploading,
|
|
61
61
|
}: OverlayImageProps) => {
|
|
62
|
-
const theme = useTheme() as MyMD3Theme;
|
|
63
62
|
const dispatch = useUIKitDispatch();
|
|
64
63
|
const { showToastMessage } = uiSlice.actions;
|
|
65
64
|
const [loading, setLoading] = useState(true);
|
|
@@ -129,23 +128,30 @@ const LoadingVideo = ({
|
|
|
129
128
|
} else {
|
|
130
129
|
handleLoadEnd();
|
|
131
130
|
dispatch(showToastMessage({ toastMessage: 'Failed to upload file' }));
|
|
131
|
+
setIsProcess(false);
|
|
132
132
|
setIsUploadError(true);
|
|
133
|
+
onUploadError?.(true, source);
|
|
133
134
|
}
|
|
134
135
|
} catch (error) {
|
|
135
136
|
handleLoadEnd();
|
|
136
137
|
dispatch(showToastMessage({ toastMessage: 'Failed to upload file' }));
|
|
138
|
+
setIsProcess(false);
|
|
137
139
|
setIsUploadError(true);
|
|
140
|
+
onUploadError?.(true, source);
|
|
138
141
|
}
|
|
139
142
|
}, [source]);
|
|
140
143
|
|
|
141
144
|
const handleDelete = async () => {
|
|
142
|
-
if (!
|
|
143
|
-
if (!isEditMode) {
|
|
145
|
+
if (fileId && !isEditMode) {
|
|
144
146
|
await deleteAmityFile(fileId);
|
|
145
147
|
}
|
|
146
148
|
onClose && onClose(source, fileId, postId);
|
|
147
149
|
};
|
|
148
150
|
useEffect(() => {
|
|
151
|
+
setIsUploadError(false);
|
|
152
|
+
onUploadError?.(false, source);
|
|
153
|
+
setProgress(0);
|
|
154
|
+
setIsProcess(false);
|
|
149
155
|
if (isUploaded) {
|
|
150
156
|
setLoading(false);
|
|
151
157
|
} else {
|
|
@@ -216,10 +222,10 @@ const LoadingVideo = ({
|
|
|
216
222
|
) : (
|
|
217
223
|
<TouchableOpacity
|
|
218
224
|
style={styles.closeButton}
|
|
219
|
-
disabled={loading || isProcess}
|
|
225
|
+
disabled={(loading || isProcess) && !isUploadError}
|
|
220
226
|
onPress={handleDelete}
|
|
221
227
|
>
|
|
222
|
-
<SvgXml xml={closeIcon(
|
|
228
|
+
<SvgXml xml={closeIcon('white')} width="12" height="12" />
|
|
223
229
|
</TouchableOpacity>
|
|
224
230
|
)}
|
|
225
231
|
</View>
|
|
@@ -2,12 +2,13 @@ import { Image, Text, TouchableOpacity, View } from 'react-native';
|
|
|
2
2
|
import React, { FC, useEffect, useState } from 'react';
|
|
3
3
|
import { SvgXml } from 'react-native-svg';
|
|
4
4
|
import {
|
|
5
|
+
errorIcon,
|
|
5
6
|
officialIcon,
|
|
6
7
|
privateIcon,
|
|
7
8
|
storyRing,
|
|
8
9
|
} from '../../../svg/svg-xml-list';
|
|
9
10
|
import { ComponentID, ElementID, ImageSizeState, PageID } from '../../enum';
|
|
10
|
-
import { useFile } from '../../hook';
|
|
11
|
+
import { useFile, useStoryPermission } from '../../hook';
|
|
11
12
|
import useConfig from '../../hook/useConfig';
|
|
12
13
|
import { useStyles } from './styles';
|
|
13
14
|
import { CommunityRepository } from '@amityco/ts-sdk-react-native';
|
|
@@ -26,18 +27,20 @@ const StoryCircleItem: FC<IStoryCircleItem> = ({
|
|
|
26
27
|
const theme = useTheme() as MyMD3Theme;
|
|
27
28
|
const [avatarUrl, setAvatarUrl] = useState(null);
|
|
28
29
|
const [communityData, setCommunityData] = useState<Amity.Community>(null);
|
|
30
|
+
const hasStoryPermission = useStoryPermission(storyTarget.targetId);
|
|
29
31
|
const { getImage } = useFile();
|
|
30
32
|
const { getUiKitConfig } = useConfig();
|
|
31
33
|
const styles = useStyles();
|
|
32
|
-
const storyRingColor: string[] =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
const storyRingColor: string[] =
|
|
35
|
+
hasStoryPermission && storyTarget?.failedStoriesCount > 0
|
|
36
|
+
? ['#DE1029', '#DE1029']
|
|
37
|
+
: storyTarget?.hasUnseen
|
|
38
|
+
? (getUiKitConfig({
|
|
39
|
+
page: PageID.StoryPage,
|
|
40
|
+
component: ComponentID.StoryTab,
|
|
41
|
+
element: ElementID.StoryRing,
|
|
42
|
+
})?.progress_color as string[]) ?? ['#e2e2e2', '#e2e2e2']
|
|
43
|
+
: ['#e2e2e2', '#e2e2e2'];
|
|
41
44
|
|
|
42
45
|
useEffect(() => {
|
|
43
46
|
if (storyTarget.targetType !== 'community') return;
|
|
@@ -80,12 +83,16 @@ const StoryCircleItem: FC<IStoryCircleItem> = ({
|
|
|
80
83
|
height={68}
|
|
81
84
|
xml={storyRing(storyRingColor[0], storyRingColor[1])}
|
|
82
85
|
/>
|
|
83
|
-
{
|
|
86
|
+
{hasStoryPermission && storyTarget?.failedStoriesCount > 0 ? (
|
|
87
|
+
<View style={styles.errorIcon}>
|
|
88
|
+
<SvgXml width={16} height={16} xml={errorIcon()} />
|
|
89
|
+
</View>
|
|
90
|
+
) : communityData?.isOfficial ? (
|
|
84
91
|
<SvgXml
|
|
85
92
|
style={styles.officialIcon}
|
|
86
93
|
xml={officialIcon(theme.colors.primary)}
|
|
87
94
|
/>
|
|
88
|
-
)}
|
|
95
|
+
) : null}
|
|
89
96
|
<View style={styles.textRow}>
|
|
90
97
|
{!communityData?.isPublic && (
|
|
91
98
|
<SvgXml width={17} height={17} xml={privateIcon(theme.colors.base)} />
|
|
@@ -26,18 +26,23 @@ export function PollResults({ options, totalVotes }: PollResultsProps) {
|
|
|
26
26
|
100
|
|
27
27
|
).toFixed(2);
|
|
28
28
|
|
|
29
|
-
const voteBy = (option: Amity.PollAnswer) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
const voteBy = (option: Amity.PollAnswer): string => {
|
|
30
|
+
const { voteCount, isVotedByUser } = option;
|
|
31
|
+
|
|
32
|
+
if (voteCount === 0) return 'No votes';
|
|
33
|
+
|
|
34
|
+
if (isVotedByUser) {
|
|
35
|
+
const otherVotes = voteCount - 1;
|
|
36
|
+
if (otherVotes === 0) return 'Voted by you';
|
|
37
|
+
|
|
38
|
+
const formattedCount = formatVoteCount(otherVotes);
|
|
39
|
+
const plural = otherVotes > 1 ? 's' : '';
|
|
40
|
+
return `Voted by ${formattedCount} participant${plural} and you`;
|
|
39
41
|
}
|
|
40
|
-
|
|
42
|
+
|
|
43
|
+
const formattedCount = formatVoteCount(voteCount);
|
|
44
|
+
const plural = voteCount > 1 ? 's' : '';
|
|
45
|
+
return `Voted by ${formattedCount} participant${plural}`;
|
|
41
46
|
};
|
|
42
47
|
|
|
43
48
|
return (
|
|
@@ -21,7 +21,7 @@ export const LinkPreview = React.memo(
|
|
|
21
21
|
|
|
22
22
|
const fetchData = async () => {
|
|
23
23
|
setData(undefined);
|
|
24
|
-
const newData = await getPreviewData(text
|
|
24
|
+
const newData = await getPreviewData(text);
|
|
25
25
|
if (!isCancelled) {
|
|
26
26
|
setData(newData);
|
|
27
27
|
}
|
|
@@ -37,8 +37,8 @@ export const LinkPreview = React.memo(
|
|
|
37
37
|
|
|
38
38
|
const renderImageNode = React.useCallback(
|
|
39
39
|
(image: PreviewDataImage) => {
|
|
40
|
-
const imageUrl = image
|
|
41
|
-
? { uri: image
|
|
40
|
+
const imageUrl = image
|
|
41
|
+
? { uri: image }
|
|
42
42
|
: require('../../assets/images/previewLinkDefaultBackground.png');
|
|
43
43
|
|
|
44
44
|
return (
|
|
@@ -2,6 +2,7 @@ import { decode } from 'html-entities';
|
|
|
2
2
|
import { Image } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { PreviewData, PreviewDataImage, Size } from './types';
|
|
5
|
+
import { Client } from '@amityco/ts-sdk-react-native';
|
|
5
6
|
|
|
6
7
|
export const getActualImageUrl = (baseUrl: string, imageUrl?: string) => {
|
|
7
8
|
let actualImageUrl = imageUrl?.trim();
|
|
@@ -54,7 +55,7 @@ export const getImageSize = (url: string) => {
|
|
|
54
55
|
|
|
55
56
|
// Functions below use functions from the same file and mocks are not working
|
|
56
57
|
/* istanbul ignore next */
|
|
57
|
-
export const getPreviewData = async (text: string
|
|
58
|
+
export const getPreviewData = async (text: string) => {
|
|
58
59
|
const previewData: PreviewData = {
|
|
59
60
|
description: undefined,
|
|
60
61
|
image: undefined,
|
|
@@ -77,114 +78,14 @@ export const getPreviewData = async (text: string, requestTimeout = 5000) => {
|
|
|
77
78
|
url = 'https://' + url;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
const abortController = new AbortController();
|
|
81
|
+
const request = await Client.fetchLinkPreview(url);
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
abortControllerTimeout = setTimeout(() => {
|
|
92
|
-
abortController.abort();
|
|
93
|
-
}, requestTimeout);
|
|
94
|
-
|
|
95
|
-
const response = await request;
|
|
96
|
-
|
|
97
|
-
clearTimeout(abortControllerTimeout);
|
|
98
|
-
|
|
99
|
-
previewData.link = url;
|
|
100
|
-
|
|
101
|
-
const contentType = response.headers.get('content-type') ?? '';
|
|
102
|
-
|
|
103
|
-
if (REGEX_IMAGE_CONTENT_TYPE.test(contentType)) {
|
|
104
|
-
const image = await getPreviewDataImage(url);
|
|
105
|
-
previewData.image = image;
|
|
106
|
-
return previewData;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const html = await response.text();
|
|
110
|
-
|
|
111
|
-
// Some pages return undefined
|
|
112
|
-
if (!html) return previewData;
|
|
113
|
-
|
|
114
|
-
const head = html.substring(0, html.indexOf('<body'));
|
|
115
|
-
|
|
116
|
-
// Get page title
|
|
117
|
-
const title = REGEX_TITLE.exec(head);
|
|
118
|
-
previewData.title = getHtmlEntitiesDecodedText(title?.[1]);
|
|
119
|
-
|
|
120
|
-
let matches: RegExpMatchArray | null;
|
|
121
|
-
const meta: RegExpMatchArray[] = [];
|
|
122
|
-
while ((matches = REGEX_META.exec(head)) !== null) {
|
|
123
|
-
// @ts-ignore
|
|
124
|
-
meta.push([...matches]);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const metaPreviewData = meta.reduce<{
|
|
128
|
-
description?: string;
|
|
129
|
-
imageUrl?: string;
|
|
130
|
-
title?: string;
|
|
131
|
-
}>(
|
|
132
|
-
(acc, curr) => {
|
|
133
|
-
// Verify that we have property/name and content
|
|
134
|
-
// Note that if a page will specify property, name and content in the same meta, regex will fail
|
|
135
|
-
if (!curr[2] || !curr[3]) return acc;
|
|
136
|
-
|
|
137
|
-
// Only take the first occurrence
|
|
138
|
-
// For description take the meta description tag into consideration
|
|
139
|
-
const description =
|
|
140
|
-
!acc.description &&
|
|
141
|
-
(getContent(curr[2], curr[3], 'og:description') ||
|
|
142
|
-
getContent(curr[2], curr[3], 'description'));
|
|
143
|
-
const ogImage =
|
|
144
|
-
!acc.imageUrl && getContent(curr[2], curr[3], 'og:image');
|
|
145
|
-
const ogTitle = !acc.title && getContent(curr[2], curr[3], 'og:title');
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
description: description
|
|
149
|
-
? getHtmlEntitiesDecodedText(description)
|
|
150
|
-
: acc.description,
|
|
151
|
-
imageUrl: ogImage ? getActualImageUrl(url, ogImage) : acc.imageUrl,
|
|
152
|
-
title: ogTitle ? getHtmlEntitiesDecodedText(ogTitle) : acc.title,
|
|
153
|
-
};
|
|
154
|
-
},
|
|
155
|
-
{ title: previewData.title }
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
previewData.description = metaPreviewData.description;
|
|
159
|
-
previewData.image = await getPreviewDataImage(metaPreviewData.imageUrl);
|
|
160
|
-
previewData.title = metaPreviewData.title;
|
|
161
|
-
|
|
162
|
-
if (!previewData.image) {
|
|
163
|
-
let imageMatches: RegExpMatchArray | null;
|
|
164
|
-
const tags: RegExpMatchArray[] = [];
|
|
165
|
-
while ((imageMatches = REGEX_IMAGE_TAG.exec(html)) !== null) {
|
|
166
|
-
// @ts-ignore
|
|
167
|
-
tags.push([...imageMatches]);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
let images: PreviewDataImage[] = [];
|
|
171
|
-
|
|
172
|
-
for (const tag of tags
|
|
173
|
-
.filter((t) => !t[1].startsWith('data'))
|
|
174
|
-
.slice(0, 5)) {
|
|
175
|
-
const image = await getPreviewDataImage(getActualImageUrl(url, tag[1]));
|
|
176
|
-
|
|
177
|
-
if (!image) continue;
|
|
178
|
-
|
|
179
|
-
images = [...images, image];
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
previewData.image = images.sort(
|
|
183
|
-
(a, b) => b.height * b.width - a.height * a.width
|
|
184
|
-
)[0];
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return previewData;
|
|
83
|
+
return {
|
|
84
|
+
description: request.description || undefined,
|
|
85
|
+
image: request.image || undefined,
|
|
86
|
+
link: url,
|
|
87
|
+
title: request.title || undefined,
|
|
88
|
+
};
|
|
188
89
|
} catch {
|
|
189
90
|
return previewData;
|
|
190
91
|
}
|