@amityco/react-native-social-uikit 4.1.1-fe2678f6.0 → 4.2.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/README.md +7 -0
- package/lib/commonjs/core/assets/icons/post.js +1 -1
- package/lib/commonjs/core/assets/icons/post.js.map +1 -1
- package/lib/commonjs/core/assets/icons/story.js +1 -1
- package/lib/commonjs/core/assets/icons/story.js.map +1 -1
- package/lib/commonjs/core/assets/icons/xml.js +16 -2
- package/lib/commonjs/core/assets/icons/xml.js.map +1 -1
- package/lib/commonjs/core/legacy/file.js +145 -62
- package/lib/commonjs/core/legacy/file.js.map +1 -1
- package/lib/commonjs/core/stores/slices/feedSlice.js +3 -0
- package/lib/commonjs/core/stores/slices/feedSlice.js.map +1 -1
- package/lib/commonjs/core/stores/slices/globalfeedSlice.js +5 -0
- package/lib/commonjs/core/stores/slices/globalfeedSlice.js.map +1 -1
- package/lib/commonjs/core/stores/slices/localVideoThumbnailSlice.js +46 -0
- package/lib/commonjs/core/stores/slices/localVideoThumbnailSlice.js.map +1 -0
- package/lib/commonjs/core/stores/slices/uiSlice.js +5 -1
- package/lib/commonjs/core/stores/slices/uiSlice.js.map +1 -1
- package/lib/commonjs/core/stores/store/index.js +3 -1
- package/lib/commonjs/core/stores/store/index.js.map +1 -1
- package/lib/commonjs/social/components/LoadingImage/index.js +73 -12
- package/lib/commonjs/social/components/LoadingImage/index.js.map +1 -1
- package/lib/commonjs/social/components/LoadingImage/styles.js +8 -3
- package/lib/commonjs/social/components/LoadingImage/styles.js.map +1 -1
- package/lib/commonjs/social/components/LoadingVideo/index.js +90 -16
- package/lib/commonjs/social/components/LoadingVideo/index.js.map +1 -1
- package/lib/commonjs/social/components/LoadingVideo/styles.js +14 -5
- package/lib/commonjs/social/components/LoadingVideo/styles.js.map +1 -1
- package/lib/commonjs/social/components/MediaViewer/MediaViewer.js +13 -13
- package/lib/commonjs/social/components/MediaViewer/MediaViewer.js.map +1 -1
- package/lib/commonjs/social/components/MediaViewer/styles.js +11 -1
- package/lib/commonjs/social/components/MediaViewer/styles.js.map +1 -1
- package/lib/commonjs/social/components/PostContent/index.js +8 -29
- package/lib/commonjs/social/components/PostContent/index.js.map +1 -1
- package/lib/commonjs/social/components/SelectedMediaComponent/SelectedMediaComponent.js +109 -17
- package/lib/commonjs/social/components/SelectedMediaComponent/SelectedMediaComponent.js.map +1 -1
- package/lib/commonjs/social/components/legacy/LoadingVideo/index.js +17 -6
- package/lib/commonjs/social/components/legacy/LoadingVideo/index.js.map +1 -1
- package/lib/commonjs/social/elements/PostMediaElement/PostMediaElement.js +121 -23
- package/lib/commonjs/social/elements/PostMediaElement/PostMediaElement.js.map +1 -1
- package/lib/commonjs/social/elements/PostMediaElement/styles.js +7 -0
- package/lib/commonjs/social/elements/PostMediaElement/styles.js.map +1 -1
- package/lib/commonjs/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.js +11 -5
- package/lib/commonjs/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.js.map +1 -1
- package/lib/commonjs/social/features/feed/components/NewsFeed/NewsFeed.js +14 -0
- package/lib/commonjs/social/features/feed/components/NewsFeed/NewsFeed.js.map +1 -1
- package/lib/commonjs/social/features/post/Composer/PostComposer.js +234 -86
- package/lib/commonjs/social/features/post/Composer/PostComposer.js.map +1 -1
- package/lib/commonjs/social/hooks/index.js +11 -0
- package/lib/commonjs/social/hooks/index.js.map +1 -1
- package/lib/commonjs/social/hooks/useCommunities.js +5 -3
- package/lib/commonjs/social/hooks/useCommunities.js.map +1 -1
- package/lib/commonjs/social/hooks/useCommunityPermission.js +43 -0
- package/lib/commonjs/social/hooks/useCommunityPermission.js.map +1 -0
- package/lib/commonjs/social/hooks/useCustomRankingGlobalFeed.js +10 -1
- package/lib/commonjs/social/hooks/useCustomRankingGlobalFeed.js.map +1 -1
- package/lib/commonjs/social/hooks/useFile.js +11 -0
- package/lib/commonjs/social/hooks/useFile.js.map +1 -1
- package/lib/commonjs/social/screens/CommunityProfile/components/Header/Header.js +18 -7
- package/lib/commonjs/social/screens/CommunityProfile/components/Header/Header.js.map +1 -1
- package/lib/commonjs/social/utils/mediaAttachments.js +150 -0
- package/lib/commonjs/social/utils/mediaAttachments.js.map +1 -0
- package/lib/commonjs/social/utils/postChildren.js +72 -0
- package/lib/commonjs/social/utils/postChildren.js.map +1 -0
- package/lib/module/core/assets/icons/post.js +1 -1
- package/lib/module/core/assets/icons/post.js.map +1 -1
- package/lib/module/core/assets/icons/story.js +1 -1
- package/lib/module/core/assets/icons/story.js.map +1 -1
- package/lib/module/core/assets/icons/xml.js +13 -0
- package/lib/module/core/assets/icons/xml.js.map +1 -1
- package/lib/module/core/legacy/file.js +145 -62
- package/lib/module/core/legacy/file.js.map +1 -1
- package/lib/module/core/stores/slices/feedSlice.js +3 -0
- package/lib/module/core/stores/slices/feedSlice.js.map +1 -1
- package/lib/module/core/stores/slices/globalfeedSlice.js +5 -0
- package/lib/module/core/stores/slices/globalfeedSlice.js.map +1 -1
- package/lib/module/core/stores/slices/localVideoThumbnailSlice.js +41 -0
- package/lib/module/core/stores/slices/localVideoThumbnailSlice.js.map +1 -0
- package/lib/module/core/stores/slices/uiSlice.js +5 -1
- package/lib/module/core/stores/slices/uiSlice.js.map +1 -1
- package/lib/module/core/stores/store/index.js +3 -1
- package/lib/module/core/stores/store/index.js.map +1 -1
- package/lib/module/social/components/LoadingImage/index.js +74 -14
- package/lib/module/social/components/LoadingImage/index.js.map +1 -1
- package/lib/module/social/components/LoadingImage/styles.js +8 -3
- package/lib/module/social/components/LoadingImage/styles.js.map +1 -1
- package/lib/module/social/components/LoadingVideo/index.js +91 -17
- package/lib/module/social/components/LoadingVideo/index.js.map +1 -1
- package/lib/module/social/components/LoadingVideo/styles.js +14 -5
- package/lib/module/social/components/LoadingVideo/styles.js.map +1 -1
- package/lib/module/social/components/MediaViewer/MediaViewer.js +13 -13
- package/lib/module/social/components/MediaViewer/MediaViewer.js.map +1 -1
- package/lib/module/social/components/MediaViewer/styles.js +11 -1
- package/lib/module/social/components/MediaViewer/styles.js.map +1 -1
- package/lib/module/social/components/PostContent/index.js +8 -29
- package/lib/module/social/components/PostContent/index.js.map +1 -1
- package/lib/module/social/components/SelectedMediaComponent/SelectedMediaComponent.js +110 -18
- package/lib/module/social/components/SelectedMediaComponent/SelectedMediaComponent.js.map +1 -1
- package/lib/module/social/components/legacy/LoadingVideo/index.js +17 -6
- package/lib/module/social/components/legacy/LoadingVideo/index.js.map +1 -1
- package/lib/module/social/elements/PostMediaElement/PostMediaElement.js +123 -25
- package/lib/module/social/elements/PostMediaElement/PostMediaElement.js.map +1 -1
- package/lib/module/social/elements/PostMediaElement/styles.js +7 -0
- package/lib/module/social/elements/PostMediaElement/styles.js.map +1 -1
- package/lib/module/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.js +12 -6
- package/lib/module/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.js.map +1 -1
- package/lib/module/social/features/feed/components/NewsFeed/NewsFeed.js +15 -1
- package/lib/module/social/features/feed/components/NewsFeed/NewsFeed.js.map +1 -1
- package/lib/module/social/features/post/Composer/PostComposer.js +236 -89
- package/lib/module/social/features/post/Composer/PostComposer.js.map +1 -1
- package/lib/module/social/hooks/index.js +1 -0
- package/lib/module/social/hooks/index.js.map +1 -1
- package/lib/module/social/hooks/useCommunities.js +5 -3
- package/lib/module/social/hooks/useCommunities.js.map +1 -1
- package/lib/module/social/hooks/useCommunityPermission.js +37 -0
- package/lib/module/social/hooks/useCommunityPermission.js.map +1 -0
- package/lib/module/social/hooks/useCustomRankingGlobalFeed.js +10 -1
- package/lib/module/social/hooks/useCustomRankingGlobalFeed.js.map +1 -1
- package/lib/module/social/hooks/useFile.js +11 -0
- package/lib/module/social/hooks/useFile.js.map +1 -1
- package/lib/module/social/screens/CommunityProfile/components/Header/Header.js +19 -8
- package/lib/module/social/screens/CommunityProfile/components/Header/Header.js.map +1 -1
- package/lib/module/social/utils/mediaAttachments.js +139 -0
- package/lib/module/social/utils/mediaAttachments.js.map +1 -0
- package/lib/module/social/utils/postChildren.js +66 -0
- package/lib/module/social/utils/postChildren.js.map +1 -0
- package/lib/typescript/core/assets/icons/xml.d.ts +1 -0
- package/lib/typescript/core/assets/icons/xml.d.ts.map +1 -1
- package/lib/typescript/core/legacy/file.d.ts +32 -0
- package/lib/typescript/core/legacy/file.d.ts.map +1 -1
- package/lib/typescript/core/stores/slices/feedSlice.d.ts.map +1 -1
- package/lib/typescript/core/stores/slices/globalfeedSlice.d.ts.map +1 -1
- package/lib/typescript/core/stores/slices/localVideoThumbnailSlice.d.ts +35 -0
- package/lib/typescript/core/stores/slices/localVideoThumbnailSlice.d.ts.map +1 -0
- package/lib/typescript/core/stores/slices/uiSlice.d.ts +5 -0
- package/lib/typescript/core/stores/slices/uiSlice.d.ts.map +1 -1
- package/lib/typescript/core/stores/store/index.d.ts.map +1 -1
- package/lib/typescript/core/types/mention.d.ts +34 -0
- package/lib/typescript/core/types/mention.d.ts.map +1 -1
- package/lib/typescript/social/components/LoadingImage/index.d.ts +8 -2
- package/lib/typescript/social/components/LoadingImage/index.d.ts.map +1 -1
- package/lib/typescript/social/components/LoadingImage/styles.d.ts.map +1 -1
- package/lib/typescript/social/components/LoadingVideo/index.d.ts +9 -2
- package/lib/typescript/social/components/LoadingVideo/index.d.ts.map +1 -1
- package/lib/typescript/social/components/LoadingVideo/styles.d.ts +1 -0
- package/lib/typescript/social/components/LoadingVideo/styles.d.ts.map +1 -1
- package/lib/typescript/social/components/MediaViewer/MediaViewer.d.ts.map +1 -1
- package/lib/typescript/social/components/MediaViewer/styles.d.ts +1 -0
- package/lib/typescript/social/components/MediaViewer/styles.d.ts.map +1 -1
- package/lib/typescript/social/components/PostContent/index.d.ts.map +1 -1
- package/lib/typescript/social/components/SelectedMediaComponent/SelectedMediaComponent.d.ts +2 -2
- package/lib/typescript/social/components/SelectedMediaComponent/SelectedMediaComponent.d.ts.map +1 -1
- package/lib/typescript/social/components/legacy/LoadingVideo/index.d.ts.map +1 -1
- package/lib/typescript/social/elements/PostMediaElement/PostMediaElement.d.ts.map +1 -1
- package/lib/typescript/social/elements/PostMediaElement/styles.d.ts +4 -0
- package/lib/typescript/social/elements/PostMediaElement/styles.d.ts.map +1 -1
- package/lib/typescript/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.d.ts.map +1 -1
- package/lib/typescript/social/features/feed/components/NewsFeed/NewsFeed.d.ts.map +1 -1
- package/lib/typescript/social/features/post/Composer/PostComposer.d.ts.map +1 -1
- package/lib/typescript/social/hooks/index.d.ts +1 -0
- package/lib/typescript/social/hooks/index.d.ts.map +1 -1
- package/lib/typescript/social/hooks/useCommunities.d.ts +2 -1
- package/lib/typescript/social/hooks/useCommunities.d.ts.map +1 -1
- package/lib/typescript/social/hooks/useCommunityPermission.d.ts +14 -0
- package/lib/typescript/social/hooks/useCommunityPermission.d.ts.map +1 -0
- package/lib/typescript/social/hooks/useCustomRankingGlobalFeed.d.ts.map +1 -1
- package/lib/typescript/social/hooks/useFile.d.ts +11 -0
- package/lib/typescript/social/hooks/useFile.d.ts.map +1 -1
- package/lib/typescript/social/screens/CommunityProfile/components/Header/Header.d.ts.map +1 -1
- package/lib/typescript/social/utils/mediaAttachments.d.ts +91 -0
- package/lib/typescript/social/utils/mediaAttachments.d.ts.map +1 -0
- package/lib/typescript/social/utils/postChildren.d.ts +38 -0
- package/lib/typescript/social/utils/postChildren.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/core/assets/icons/post.tsx +1 -1
- package/src/core/assets/icons/story.tsx +1 -1
- package/src/core/assets/icons/xml.ts +15 -0
- package/src/core/legacy/file.tsx +169 -84
- package/src/core/stores/slices/feedSlice.ts +3 -0
- package/src/core/stores/slices/globalfeedSlice.ts +5 -0
- package/src/core/stores/slices/localVideoThumbnailSlice.ts +58 -0
- package/src/core/stores/slices/uiSlice.ts +8 -0
- package/src/core/stores/store/index.ts +2 -0
- package/src/core/types/mention.ts +34 -0
- package/src/social/components/LoadingImage/index.tsx +81 -12
- package/src/social/components/LoadingImage/styles.ts +8 -3
- package/src/social/components/LoadingVideo/index.tsx +111 -21
- package/src/social/components/LoadingVideo/styles.ts +13 -4
- package/src/social/components/MediaViewer/MediaViewer.tsx +11 -12
- package/src/social/components/MediaViewer/styles.ts +9 -1
- package/src/social/components/PostContent/index.tsx +3 -35
- package/src/social/components/SelectedMediaComponent/SelectedMediaComponent.tsx +103 -13
- package/src/social/components/legacy/LoadingVideo/index.tsx +26 -15
- package/src/social/elements/PostMediaElement/PostMediaElement.tsx +139 -29
- package/src/social/elements/PostMediaElement/styles.ts +7 -0
- package/src/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.tsx +11 -6
- package/src/social/features/feed/components/NewsFeed/NewsFeed.tsx +16 -1
- package/src/social/features/post/Composer/PostComposer.tsx +304 -91
- package/src/social/hooks/index.ts +1 -0
- package/src/social/hooks/useCommunities.ts +4 -2
- package/src/social/hooks/useCommunityPermission.ts +40 -0
- package/src/social/hooks/useCustomRankingGlobalFeed.ts +8 -1
- package/src/social/hooks/useFile.ts +11 -0
- package/src/social/screens/CommunityProfile/components/Header/Header.tsx +19 -10
- package/src/social/utils/mediaAttachments.ts +158 -0
- package/src/social/utils/postChildren.ts +85 -0
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
StatusBar,
|
|
11
11
|
Linking,
|
|
12
12
|
} from 'react-native';
|
|
13
|
-
import { FC, memo, useCallback, useEffect, useState } from 'react';
|
|
13
|
+
import { FC, memo, useCallback, useEffect, useMemo, useState } from 'react';
|
|
14
14
|
import {
|
|
15
15
|
ComponentID,
|
|
16
16
|
ElementID,
|
|
@@ -34,11 +34,11 @@ import { IDisplayImage, IMentionPosition } from '../../../../core/types';
|
|
|
34
34
|
import CloseButtonIconElement from '../../../elements/CloseButtonIconElement/CloseButtonIconElement';
|
|
35
35
|
import { useNavigation } from '@react-navigation/native';
|
|
36
36
|
import uiSlice from '../../../../core/stores/slices/uiSlice';
|
|
37
|
+
import localVideoThumbnailSlice from '../../../../core/stores/slices/localVideoThumbnailSlice';
|
|
37
38
|
import { amityPostsFormatter } from '../../../../core/utils/post';
|
|
38
39
|
import useAuth from '../../../../core/hooks/useAuth';
|
|
39
40
|
import globalfeedSlice from '../../../../core/stores/slices/globalfeedSlice';
|
|
40
41
|
import { createPostToFeed, editPost } from '../../../../core/legacy/feed';
|
|
41
|
-
import { usePostByIds } from '../../../../core/hooks/usePostByIds';
|
|
42
42
|
import TextKeyElement from '../../../elements/TextKeyElement/TextKeyElement';
|
|
43
43
|
import AmityMediaAttachmentComponent from '../components/MediaAttachment';
|
|
44
44
|
import AmityDetailedMediaAttachmentComponent from '../components/DetailedMediaAttachment';
|
|
@@ -53,12 +53,20 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
|
|
53
53
|
import { RootStackParamList } from '../../../../core/routes/RouteParamList';
|
|
54
54
|
import {
|
|
55
55
|
CommunityRepository,
|
|
56
|
+
FileRepository,
|
|
56
57
|
PostRepository,
|
|
57
58
|
UserRepository,
|
|
58
59
|
} from '@amityco/ts-sdk-react-native';
|
|
59
|
-
import { useFile } from '../../../hooks';
|
|
60
60
|
import useMention from '../../../hooks/useMention';
|
|
61
61
|
import { getPostErrorMessage } from '../../../utils/errors';
|
|
62
|
+
import {
|
|
63
|
+
MAX_MEDIA_ATTACHMENTS,
|
|
64
|
+
appendWithinCap,
|
|
65
|
+
applyFinishedUpload,
|
|
66
|
+
dedupeMediaPicks,
|
|
67
|
+
toLocalVideoThumbnails,
|
|
68
|
+
toggleUploadingSource,
|
|
69
|
+
} from '../../../utils/mediaAttachments';
|
|
62
70
|
import {
|
|
63
71
|
ALERT,
|
|
64
72
|
MAX_MENTION_USERS,
|
|
@@ -86,7 +94,6 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
86
94
|
const { AmityPostComposerPageBehavior } = useBehaviour();
|
|
87
95
|
const { isExcluded, themeStyles, accessibilityId } = useAmityPage({ pageId });
|
|
88
96
|
const styles = useStyles(themeStyles);
|
|
89
|
-
const { getImage } = useFile();
|
|
90
97
|
const isEditMode = mode === AmityPostComposerMode.EDIT;
|
|
91
98
|
const navigation =
|
|
92
99
|
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
|
@@ -99,6 +106,7 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
99
106
|
const currentUser = useUser((client as Amity.Client)?.userId || '');
|
|
100
107
|
const isCommunityModerator = isModerator(currentUser?.roles);
|
|
101
108
|
const { showToastMessage, hideToastMessage } = uiSlice.actions;
|
|
109
|
+
const { setLocalVideoThumbnails } = localVideoThumbnailSlice.actions;
|
|
102
110
|
const [inputMessage, setInputMessage] = useState<string>(
|
|
103
111
|
(post?.data as Amity.ContentDataText)?.text ?? ''
|
|
104
112
|
);
|
|
@@ -111,7 +119,16 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
111
119
|
const [mentionUsers, setMentionUsers] = useState<TSearchItem[]>([]);
|
|
112
120
|
const [isSwipeup, setIsSwipeup] = useState(true);
|
|
113
121
|
const [deletedPostIds, setDeletedPostIds] = useState<string[]>([]);
|
|
114
|
-
|
|
122
|
+
// Upload progress is tracked per media item, keyed by the item's `source`
|
|
123
|
+
// url — the same shape as `imageErrors`/`videoErrors` below. A
|
|
124
|
+
// single shared boolean was flipped back to false by whichever upload
|
|
125
|
+
// finished first, so Post/Save unlocked while the remaining frames were
|
|
126
|
+
// still in flight. Sources are unique across images and videos, so one set
|
|
127
|
+
// covers both attachment types.
|
|
128
|
+
const [uploadingSources, setUploadingSources] = useState<Set<string>>(
|
|
129
|
+
new Set()
|
|
130
|
+
);
|
|
131
|
+
const isUploading = uploadingSources.size > 0;
|
|
115
132
|
const [hasChangedAttachment, setHasChangedAttachment] = useState(false);
|
|
116
133
|
const [imageErrors, setImageErrors] = useState<Set<string>>(new Set());
|
|
117
134
|
const [videoErrors, setVideoErrors] = useState<Set<string>>(new Set());
|
|
@@ -148,7 +165,8 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
148
165
|
(inputMessage.trim().length > 0 ||
|
|
149
166
|
displayImages.length > 0 ||
|
|
150
167
|
displayVideos.length > 0) &&
|
|
151
|
-
(displayImages.length <=
|
|
168
|
+
(displayImages.length <= MAX_MEDIA_ATTACHMENTS ||
|
|
169
|
+
displayVideos.length <= MAX_MEDIA_ATTACHMENTS);
|
|
152
170
|
|
|
153
171
|
const { renderInput, renderSuggestions } = useMention({
|
|
154
172
|
value: inputMessage,
|
|
@@ -170,7 +188,26 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
170
188
|
},
|
|
171
189
|
});
|
|
172
190
|
|
|
191
|
+
// The edited post carries its own hydrated children: every post payload
|
|
192
|
+
// ingests `postChildren` into the `post` cache (PAYLOAD2MODEL maps it to
|
|
193
|
+
// 'post'), and postLinkedObject reads them straight back out. So the media is
|
|
194
|
+
// known on the first frame, with no fetch — no second source to reconcile.
|
|
195
|
+
const editChildren = post?.childrenPosts ?? [];
|
|
196
|
+
|
|
197
|
+
// `childrenPosts` drops cache misses silently (postLinkedObject filters with
|
|
198
|
+
// isNonNullable), so a short array means we cannot see every child. There is
|
|
199
|
+
// no fallback fetch behind it, so treat that state as read-only: no
|
|
200
|
+
// attachment bar, no Save.
|
|
201
|
+
const editChildrenIncomplete =
|
|
202
|
+
isEditMode && editChildren.length !== (post?.children?.length ?? 0);
|
|
203
|
+
|
|
173
204
|
const checkIsEditValid = () => {
|
|
205
|
+
// `type` in onPressPost is derived from displayImages/displayVideos, so
|
|
206
|
+
// saving while a child is missing submits it as removed — `attachments: []`
|
|
207
|
+
// and type 'text' strips the post's media outright. Nothing the user can do
|
|
208
|
+
// in the composer makes an unrendered child safe to save, so key this on
|
|
209
|
+
// completeness alone rather than on hasChangedAttachment.
|
|
210
|
+
if (editChildrenIncomplete) return false;
|
|
174
211
|
return (
|
|
175
212
|
isInputValid &&
|
|
176
213
|
(inputMessage !== (post?.data as Amity.ContentDataText)?.text ||
|
|
@@ -192,9 +229,34 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
192
229
|
[]
|
|
193
230
|
);
|
|
194
231
|
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
|
|
232
|
+
// A child post's file is usually already in the SDK cache, where the linked
|
|
233
|
+
// object exposes it synchronously (getImageInfo / getVideoInfo /
|
|
234
|
+
// getVideoThumbnailInfo are `pullFromCache(['file','get', …])` over the very
|
|
235
|
+
// ids we'd otherwise refetch). Use the cached `fileUrl` when it's there and
|
|
236
|
+
// fall back to the round trip on a cache miss. `fileUrlWithSize` is just
|
|
237
|
+
// `?size=`, so both branches produce the same url.
|
|
238
|
+
const resolveFileUrl = useCallback(
|
|
239
|
+
async (fileUrl: string | undefined, fileId: string | undefined) => {
|
|
240
|
+
if (fileUrl)
|
|
241
|
+
return FileRepository.fileUrlWithSize(fileUrl, ImageSizeState.full);
|
|
242
|
+
if (!fileId) return undefined;
|
|
243
|
+
|
|
244
|
+
// Resolve the file directly rather than through useFile: that hook
|
|
245
|
+
// answers a miss with the default *avatar* image, which is right for a
|
|
246
|
+
// profile picture and wrong for post media. A video whose thumbnail the
|
|
247
|
+
// server has not produced yet has no file to resolve, and handing back an
|
|
248
|
+
// avatar made the carousel render a person icon behind the play button as
|
|
249
|
+
// though it were the real frame — and, because the url looked valid, it
|
|
250
|
+
// was also stored as the local thumbnail the feed falls back on.
|
|
251
|
+
const file = await FileRepository.getFile(fileId);
|
|
252
|
+
const resolved = file?.data?.fileUrl;
|
|
253
|
+
|
|
254
|
+
return resolved
|
|
255
|
+
? FileRepository.fileUrlWithSize(resolved, ImageSizeState.full)
|
|
256
|
+
: undefined;
|
|
257
|
+
},
|
|
258
|
+
[]
|
|
259
|
+
);
|
|
198
260
|
|
|
199
261
|
const getPostInfo = useCallback(
|
|
200
262
|
async (response: Amity.Post[]) => {
|
|
@@ -205,38 +267,57 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
205
267
|
for (const item of response) {
|
|
206
268
|
if (item?.dataType === 'image') {
|
|
207
269
|
const fileId = (item as Amity.Post<'image'>)?.data?.fileId;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
});
|
|
270
|
+
// Dimensions for composer frame-ratio classification (REQ-003d).
|
|
271
|
+
const imageInfo = item?.getImageInfo?.();
|
|
272
|
+
const url = await resolveFileUrl(imageInfo?.fileUrl, fileId);
|
|
212
273
|
images.push({
|
|
213
274
|
url,
|
|
214
275
|
fileId,
|
|
215
276
|
fileName: fileId,
|
|
277
|
+
// A hydrated child has no local pick behind it, so its identity
|
|
278
|
+
// is the file it already is. Unique per child, stable for the
|
|
279
|
+
// whole session, and never equal to a picker file name — so a
|
|
280
|
+
// later pick is de-duplicated against the other staged picks
|
|
281
|
+
// only, which is the most the picker can tell us.
|
|
282
|
+
localId: fileId ?? item.postId,
|
|
216
283
|
isUploaded: true,
|
|
217
284
|
postId: item.postId,
|
|
285
|
+
width: imageInfo?.getWidth?.(),
|
|
286
|
+
height: imageInfo?.getHeight?.(),
|
|
218
287
|
});
|
|
219
288
|
} else if (item?.dataType === 'video') {
|
|
220
289
|
const videoData = (item as Amity.Post<'video'>)?.data;
|
|
221
290
|
const fileId = videoData?.videoFileId?.original;
|
|
222
291
|
const thumbnailFileId = videoData?.thumbnailFileId;
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
)
|
|
292
|
+
// STORED dims + rotation; rotation is applied at classify time
|
|
293
|
+
// (SelectedMediaComponent REQ-003d1 / AmityVideo REQ-SDK-002).
|
|
294
|
+
const videoInfo = item?.getVideoInfo?.();
|
|
295
|
+
const thumbnailInfo = item?.getVideoThumbnailInfo?.();
|
|
296
|
+
// Settle the two urls independently: a missing or unreachable
|
|
297
|
+
// thumbnail must not cost us the video itself. `Promise.all` would
|
|
298
|
+
// reject the pair, abort this loop, and leave displayVideos empty
|
|
299
|
+
// — which now also keeps Save disabled (see checkIsEditValid).
|
|
300
|
+
const [urlResult, thumbNailResult] = await Promise.allSettled([
|
|
301
|
+
resolveFileUrl(videoInfo?.fileUrl, fileId),
|
|
302
|
+
resolveFileUrl(thumbnailInfo?.fileUrl, thumbnailFileId),
|
|
303
|
+
]);
|
|
304
|
+
const url =
|
|
305
|
+
urlResult.status === 'fulfilled' ? urlResult.value : undefined;
|
|
306
|
+
const thumbNail =
|
|
307
|
+
thumbNailResult.status === 'fulfilled'
|
|
308
|
+
? thumbNailResult.value
|
|
309
|
+
: undefined;
|
|
231
310
|
videos.push({
|
|
232
|
-
|
|
233
|
-
url: fileUrls[0]?.value,
|
|
311
|
+
url,
|
|
234
312
|
fileId: fileId,
|
|
235
313
|
fileName: fileId,
|
|
314
|
+
localId: fileId ?? item.postId,
|
|
236
315
|
isUploaded: true,
|
|
237
|
-
|
|
238
|
-
thumbNail: fileUrls[1]?.value,
|
|
316
|
+
thumbNail,
|
|
239
317
|
postId: item.postId,
|
|
318
|
+
width: videoInfo?.getWidth?.(),
|
|
319
|
+
height: videoInfo?.getHeight?.(),
|
|
320
|
+
rotation: videoInfo?.getRotation?.(),
|
|
240
321
|
});
|
|
241
322
|
}
|
|
242
323
|
}
|
|
@@ -251,7 +332,7 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
251
332
|
console.log('error: ', error);
|
|
252
333
|
}
|
|
253
334
|
},
|
|
254
|
-
[
|
|
335
|
+
[resolveFileUrl]
|
|
255
336
|
);
|
|
256
337
|
|
|
257
338
|
useEffect(() => {
|
|
@@ -259,9 +340,39 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
259
340
|
return () => setDeletedPostIds([]);
|
|
260
341
|
}, []);
|
|
261
342
|
|
|
343
|
+
// `post` comes from route params, so `editChildren` is reference-stable and
|
|
344
|
+
// this hydrates once. The hasChangedAttachment guard is belt-and-braces:
|
|
345
|
+
// getPostInfo assigns displayImages/displayVideos wholesale, so a re-run must
|
|
346
|
+
// never be able to wipe media the user added.
|
|
262
347
|
useEffect(() => {
|
|
263
|
-
if (
|
|
264
|
-
|
|
348
|
+
if (editChildren.length === 0 || hasChangedAttachment) return;
|
|
349
|
+
getPostInfo(editChildren);
|
|
350
|
+
}, [getPostInfo, post?.childrenPosts, hasChangedAttachment]);
|
|
351
|
+
|
|
352
|
+
// Media type of the post being edited, read off the children's dataType —
|
|
353
|
+
// the same signal the web UIKit feeds into useMediaAttachmentVisible.
|
|
354
|
+
const editMediaType = useMemo(() => {
|
|
355
|
+
if (editChildren.some((item) => item?.dataType === 'image'))
|
|
356
|
+
return mediaAttachment.image;
|
|
357
|
+
if (editChildren.some((item) => item?.dataType === 'video'))
|
|
358
|
+
return mediaAttachment.video;
|
|
359
|
+
return null;
|
|
360
|
+
}, [post?.childrenPosts]);
|
|
361
|
+
|
|
362
|
+
// The post's single media type, as every media entry point must see it: the
|
|
363
|
+
// staged attachments when there are any, otherwise the type the edited post
|
|
364
|
+
// already has. Read the display arrays rather than `chosenMediaType`, which
|
|
365
|
+
// is assigned in an effect and so trails them by a render. Once the user has
|
|
366
|
+
// cleared the attachments itself the post has no type again and all three
|
|
367
|
+
// buttons come back, matching web.
|
|
368
|
+
const activeMediaType =
|
|
369
|
+
displayImages.length > 0
|
|
370
|
+
? mediaAttachment.image
|
|
371
|
+
: displayVideos.length > 0
|
|
372
|
+
? mediaAttachment.video
|
|
373
|
+
: hasChangedAttachment
|
|
374
|
+
? null
|
|
375
|
+
: editMediaType;
|
|
265
376
|
|
|
266
377
|
const getMentionPositions = useCallback(
|
|
267
378
|
(text: string, mentioneeIds: string[]) => {
|
|
@@ -420,6 +531,23 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
420
531
|
return;
|
|
421
532
|
}
|
|
422
533
|
dispatch(hideToastMessage());
|
|
534
|
+
// The server has no thumbnail for these videos until it finishes
|
|
535
|
+
// transcoding, so hand the feed the frames the composer already decoded
|
|
536
|
+
// to bridge that window (PDT-4904, web parity: LayoutProvider
|
|
537
|
+
// videoThumbnail). Keyed by the uploaded original video's fileId, which
|
|
538
|
+
// is what the created post exposes as `data.videoFileId.original`.
|
|
539
|
+
if (type === 'video' && displayVideos.length > 0) {
|
|
540
|
+
dispatch(
|
|
541
|
+
setLocalVideoThumbnails({
|
|
542
|
+
postId: response.postId,
|
|
543
|
+
// Only frames that actually decoded: an entry with no thumbnail
|
|
544
|
+
// would still satisfy the feed's lookup and suppress its own
|
|
545
|
+
// pending state, leaving the frame blank instead of showing the
|
|
546
|
+
// play affordance over a plain background.
|
|
547
|
+
videos: toLocalVideoThumbnails(displayVideos),
|
|
548
|
+
})
|
|
549
|
+
);
|
|
550
|
+
}
|
|
423
551
|
if (
|
|
424
552
|
targetType === 'community' &&
|
|
425
553
|
(effectiveCommunity?.postSetting === 'ADMIN_REVIEW_POST_REQUIRED' ||
|
|
@@ -473,6 +601,7 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
473
601
|
mentionUsers,
|
|
474
602
|
mentionsPosition,
|
|
475
603
|
onPressClose,
|
|
604
|
+
setLocalVideoThumbnails,
|
|
476
605
|
post,
|
|
477
606
|
showToastMessage,
|
|
478
607
|
targetId,
|
|
@@ -506,6 +635,14 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
506
635
|
return {
|
|
507
636
|
url: url,
|
|
508
637
|
fileName: fileName,
|
|
638
|
+
// Camera captures land on a freshly generated temp path, so the path
|
|
639
|
+
// itself is the identity: unique per shot, and stable once the upload
|
|
640
|
+
// rewrites `url` to the remote one. Deliberately no
|
|
641
|
+
// `localFileName` — two shots of the same scene are two distinct
|
|
642
|
+
// attachments and must never de-duplicate against each other, nor
|
|
643
|
+
// against a library pick that happens to share a basename.
|
|
644
|
+
localId: url,
|
|
645
|
+
localUrl: url,
|
|
509
646
|
fileId: '',
|
|
510
647
|
isUploaded: false,
|
|
511
648
|
};
|
|
@@ -521,12 +658,18 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
521
658
|
|
|
522
659
|
const pickCamera = useCallback(
|
|
523
660
|
async (mediaType: 'mixed' | 'photo' | 'video') => {
|
|
524
|
-
if (
|
|
661
|
+
if (
|
|
662
|
+
mediaType === 'photo' &&
|
|
663
|
+
displayImages.length >= MAX_MEDIA_ATTACHMENTS
|
|
664
|
+
)
|
|
525
665
|
return Alert.alert(
|
|
526
666
|
'Maximum upload limit reached',
|
|
527
667
|
"You've reached the upload limit of 10 images. Any additional images will not be saved."
|
|
528
668
|
);
|
|
529
|
-
if (
|
|
669
|
+
if (
|
|
670
|
+
mediaType === 'video' &&
|
|
671
|
+
displayVideos.length >= MAX_MEDIA_ATTACHMENTS
|
|
672
|
+
)
|
|
530
673
|
return Alert.alert(
|
|
531
674
|
'Maximum upload limit reached',
|
|
532
675
|
"You've reached the upload limit of 10 videos. Any additional videos will not be saved."
|
|
@@ -567,22 +710,34 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
567
710
|
[displayImages.length, displayVideos.length, processMedia]
|
|
568
711
|
);
|
|
569
712
|
const onPressCamera = useCallback(async () => {
|
|
570
|
-
|
|
571
|
-
|
|
713
|
+
// The camera button is always rendered — unlike photo/video, neither
|
|
714
|
+
// attachment bar hides it on `chosenMediaType` — so this is the one entry
|
|
715
|
+
// point that must decide the capture type itself. Keying it on the staged
|
|
716
|
+
// arrays alone offered both types while an edited post's media was still
|
|
717
|
+
// hydrating, which is how a video could be shot onto an image post.
|
|
718
|
+
if (activeMediaType === mediaAttachment.image) return pickCamera('photo');
|
|
719
|
+
if (activeMediaType === mediaAttachment.video) return pickCamera('video');
|
|
572
720
|
if (Platform.OS === 'ios') return pickCamera('mixed');
|
|
573
721
|
Alert.alert('Open Camera', null, [
|
|
574
722
|
{ text: 'Photo', onPress: async () => pickCamera('photo') },
|
|
575
723
|
{ text: 'Video', onPress: async () => pickCamera('video') },
|
|
576
724
|
]);
|
|
577
|
-
}, [
|
|
725
|
+
}, [activeMediaType, pickCamera]);
|
|
578
726
|
|
|
579
727
|
// Open the native OS camera roll (matches the web UIKit's native file
|
|
580
728
|
// picker). Appends the picked media, de-duplicating by uri and capping at 10.
|
|
581
729
|
const pickFromLibrary = useCallback(
|
|
582
730
|
async (mediaType: 'photo' | 'video') => {
|
|
583
731
|
const isPhoto = mediaType === 'photo';
|
|
732
|
+
// A post holds one media type. The bar hides the mismatched button, but
|
|
733
|
+
// guard the entry point too so a tap landing before the button updates
|
|
734
|
+
// can't replace the existing attachments with the other type.
|
|
735
|
+
const pickedType = isPhoto
|
|
736
|
+
? mediaAttachment.image
|
|
737
|
+
: mediaAttachment.video;
|
|
738
|
+
if (activeMediaType && activeMediaType !== pickedType) return;
|
|
584
739
|
const current = isPhoto ? displayImages.length : displayVideos.length;
|
|
585
|
-
if (current >=
|
|
740
|
+
if (current >= MAX_MEDIA_ATTACHMENTS)
|
|
586
741
|
return Alert.alert(
|
|
587
742
|
'Maximum upload limit reached',
|
|
588
743
|
`You've reached the upload limit of 10 ${
|
|
@@ -592,36 +747,80 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
592
747
|
const result: ImagePicker.ImagePickerResponse = await launchImageLibrary({
|
|
593
748
|
mediaType,
|
|
594
749
|
quality: 1,
|
|
595
|
-
selectionLimit:
|
|
750
|
+
selectionLimit: MAX_MEDIA_ATTACHMENTS - current,
|
|
751
|
+
// Required for `asset.id`, which is what recognises a re-pick of an
|
|
752
|
+
// asset already staged. Without it the iOS picker builds its
|
|
753
|
+
// PHPickerConfiguration without a photo library, so results carry no
|
|
754
|
+
// assetIdentifier and the native side never resolves a PHAsset —
|
|
755
|
+
// leaving `id` undefined and the fallback key, `fileName`, a fresh
|
|
756
|
+
// UUID per pick. Both dedupe keys were therefore unique every time
|
|
757
|
+
// and picking the same photo twice staged it twice. Android was
|
|
758
|
+
// unaffected: its filename is the MediaStore display name, stable
|
|
759
|
+
// across picks.
|
|
760
|
+
includeExtra: true,
|
|
596
761
|
});
|
|
597
762
|
if (result.didCancel || !result.assets?.length) return;
|
|
598
763
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
764
|
+
// iOS video is the one pick whose dims cannot be trusted: the picker
|
|
765
|
+
// reports the track's stored `naturalSize` with no rotation alongside
|
|
766
|
+
// it, so a portrait iPhone clip arrives as 1920×1080 and would
|
|
767
|
+
// classify the frame 16:9 (PDT-4904). Images on both platforms, and
|
|
768
|
+
// Android video (its extractor swaps w/h on 90°/270°), are already
|
|
769
|
+
// display-oriented and unaffected. With no rotation to correct the iOS
|
|
770
|
+
// value by, carry nothing and let SelectedMediaComponent measure the
|
|
771
|
+
// generated thumbnail, which is a rendered frame.
|
|
772
|
+
const isIosVideoDimsUnusable = !isPhoto && Platform.OS === 'ios';
|
|
773
|
+
const candidates = (result.assets ?? []).flatMap((asset) => {
|
|
774
|
+
if (!asset.uri) return [];
|
|
775
|
+
const fileName =
|
|
776
|
+
asset.fileName ?? asset.uri.substring(asset.uri.lastIndexOf('/') + 1);
|
|
777
|
+
return [
|
|
778
|
+
{
|
|
614
779
|
url: asset.uri,
|
|
615
780
|
fileName,
|
|
781
|
+
// `asset.id` is the library's own identifier for the asset (PHAsset
|
|
782
|
+
// localIdentifier / MediaStore id) and is the one key that survives
|
|
783
|
+
// the picker copying the pick to a new temp uri every time. It is
|
|
784
|
+
// not populated by every picker configuration, so fall back to the
|
|
785
|
+
// file name and keep the name as a second key either way — that is
|
|
786
|
+
// what catches a re-pick when no id came back.
|
|
787
|
+
localId: asset.id ?? fileName,
|
|
788
|
+
localUrl: asset.uri,
|
|
789
|
+
localFileName: fileName,
|
|
616
790
|
fileId: '',
|
|
617
791
|
isUploaded: false,
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
792
|
+
width: isIosVideoDimsUnusable ? undefined : asset.width,
|
|
793
|
+
height: isIosVideoDimsUnusable ? undefined : asset.height,
|
|
794
|
+
},
|
|
795
|
+
];
|
|
622
796
|
});
|
|
797
|
+
|
|
798
|
+
// Stage the picks and claim their upload slots in one go. Staging alone
|
|
799
|
+
// used to flip `isInputValid` true on the very next render — the frames
|
|
800
|
+
// exist, and `uploadingSources` is still empty because each child only
|
|
801
|
+
// registers from its own mount effect — so Post sat enabled for ~940 ms
|
|
802
|
+
// with nothing uploaded and a quick tap posted media that had no fileId
|
|
803
|
+
// yet. Seeding here makes "staged" and "gated" the same update; children
|
|
804
|
+
// still clear their own entry when they finish, and the unmount cleanup
|
|
805
|
+
// still covers a frame removed mid-flight.
|
|
806
|
+
const staged = isPhoto ? displayImages : displayVideos;
|
|
807
|
+
const accepted = appendWithinCap(
|
|
808
|
+
staged,
|
|
809
|
+
dedupeMediaPicks(staged, candidates)
|
|
810
|
+
);
|
|
811
|
+
// Only the entries that actually survived dedupe and the cap — seeding a
|
|
812
|
+
// key no child will ever mount for would leave Post disabled forever.
|
|
813
|
+
const newlyStaged = accepted.slice(staged.length);
|
|
814
|
+
const setter = isPhoto ? setDisplayImages : setDisplayVideos;
|
|
815
|
+
setter(accepted);
|
|
816
|
+
setUploadingSources((prev) =>
|
|
817
|
+
newlyStaged.reduce(
|
|
818
|
+
(acc, item) => toggleUploadingSource(acc, item.url, true),
|
|
819
|
+
prev
|
|
820
|
+
)
|
|
821
|
+
);
|
|
623
822
|
},
|
|
624
|
-
[displayImages
|
|
823
|
+
[activeMediaType, displayImages, displayVideos]
|
|
625
824
|
);
|
|
626
825
|
|
|
627
826
|
const onPressImage = useCallback(
|
|
@@ -634,6 +833,19 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
634
833
|
[pickFromLibrary]
|
|
635
834
|
);
|
|
636
835
|
|
|
836
|
+
// Children report their own upload start/end keyed by `source`, and a child
|
|
837
|
+
// that unmounts mid-upload (frame removed while still uploading) clears its
|
|
838
|
+
// own entry from its cleanup — otherwise the set would never empty and Post
|
|
839
|
+
// would stay disabled forever.
|
|
840
|
+
const handleUploadingChange = useCallback(
|
|
841
|
+
(uploading: boolean, source: string) => {
|
|
842
|
+
setUploadingSources((prev) =>
|
|
843
|
+
toggleUploadingSource(prev, source, uploading)
|
|
844
|
+
);
|
|
845
|
+
},
|
|
846
|
+
[]
|
|
847
|
+
);
|
|
848
|
+
|
|
637
849
|
const handleImageUploadError = useCallback(
|
|
638
850
|
(hasError: boolean, source: string) => {
|
|
639
851
|
setImageErrors((prev) => {
|
|
@@ -667,7 +879,7 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
667
879
|
const handleOnCloseImage = useCallback(
|
|
668
880
|
(originalPath: string, _, postId: string) => {
|
|
669
881
|
setHasChangedAttachment(true);
|
|
670
|
-
setDeletedPostIds((prev) => [...prev, postId]);
|
|
882
|
+
if (postId) setDeletedPostIds((prev) => [...prev, postId]);
|
|
671
883
|
setImageErrors((prev) => {
|
|
672
884
|
const newSet = new Set(prev);
|
|
673
885
|
newSet.delete(originalPath);
|
|
@@ -685,7 +897,7 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
685
897
|
const handleOnCloseVideo = useCallback(
|
|
686
898
|
(originalPath: string, _, postId: string) => {
|
|
687
899
|
setHasChangedAttachment(true);
|
|
688
|
-
setDeletedPostIds((prev) => [...prev, postId]);
|
|
900
|
+
if (postId) setDeletedPostIds((prev) => [...prev, postId]);
|
|
689
901
|
setVideoErrors((prev) => {
|
|
690
902
|
const newSet = new Set(prev);
|
|
691
903
|
newSet.delete(originalPath);
|
|
@@ -701,19 +913,22 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
701
913
|
[]
|
|
702
914
|
);
|
|
703
915
|
const handleOnFinishImage = useCallback(
|
|
704
|
-
(
|
|
916
|
+
(
|
|
917
|
+
fileId: string,
|
|
918
|
+
fileUrl: string,
|
|
919
|
+
fileName: string,
|
|
920
|
+
_index: number,
|
|
921
|
+
originalPath: string
|
|
922
|
+
) => {
|
|
705
923
|
setHasChangedAttachment(true);
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
newData[index] = imageObject;
|
|
715
|
-
return newData;
|
|
716
|
-
});
|
|
924
|
+
setDisplayImages((prevData) =>
|
|
925
|
+
applyFinishedUpload(prevData, originalPath, {
|
|
926
|
+
url: fileUrl,
|
|
927
|
+
fileId: fileId,
|
|
928
|
+
fileName: fileName,
|
|
929
|
+
isUploaded: true,
|
|
930
|
+
})
|
|
931
|
+
);
|
|
717
932
|
},
|
|
718
933
|
[]
|
|
719
934
|
);
|
|
@@ -722,23 +937,20 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
722
937
|
fileId: string,
|
|
723
938
|
fileUrl: string,
|
|
724
939
|
fileName: string,
|
|
725
|
-
|
|
726
|
-
|
|
940
|
+
_index: number,
|
|
941
|
+
originalPath: string,
|
|
727
942
|
thumbnail: string
|
|
728
943
|
) => {
|
|
729
944
|
setHasChangedAttachment(true);
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
newData[index] = imageObject;
|
|
740
|
-
return newData;
|
|
741
|
-
});
|
|
945
|
+
setDisplayVideos((prevData) =>
|
|
946
|
+
applyFinishedUpload(prevData, originalPath, {
|
|
947
|
+
url: fileUrl,
|
|
948
|
+
fileId: fileId,
|
|
949
|
+
fileName: fileName,
|
|
950
|
+
isUploaded: true,
|
|
951
|
+
thumbNail: thumbnail,
|
|
952
|
+
})
|
|
953
|
+
);
|
|
742
954
|
},
|
|
743
955
|
[]
|
|
744
956
|
);
|
|
@@ -746,17 +958,18 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
746
958
|
// PDT-4310 / PDT-4312: at the 10-attachment cap the camera + gallery icons
|
|
747
959
|
// are disabled until the user removes a frame.
|
|
748
960
|
const isMediaCapReached =
|
|
749
|
-
displayImages.length >=
|
|
961
|
+
displayImages.length >= MAX_MEDIA_ATTACHMENTS ||
|
|
962
|
+
displayVideos.length >= MAX_MEDIA_ATTACHMENTS;
|
|
750
963
|
|
|
751
964
|
const renderDetailedAttachment = useCallback(() => {
|
|
752
|
-
if (
|
|
965
|
+
if (editChildrenIncomplete) return null;
|
|
753
966
|
if (shouldShowDetailAttachment) {
|
|
754
967
|
return (
|
|
755
968
|
<AmityDetailedMediaAttachmentComponent
|
|
756
969
|
onPressCamera={onPressCamera}
|
|
757
970
|
onPressImage={onPressImage}
|
|
758
971
|
onPressVideo={onPressVideo}
|
|
759
|
-
chosenMediaType={
|
|
972
|
+
chosenMediaType={activeMediaType}
|
|
760
973
|
onHeightChange={setAttachmentBarHeight}
|
|
761
974
|
disabled={isMediaCapReached}
|
|
762
975
|
/>
|
|
@@ -767,14 +980,14 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
767
980
|
onPressCamera={onPressCamera}
|
|
768
981
|
onPressImage={onPressImage}
|
|
769
982
|
onPressVideo={onPressVideo}
|
|
770
|
-
chosenMediaType={
|
|
983
|
+
chosenMediaType={activeMediaType}
|
|
771
984
|
onHeightChange={setAttachmentBarHeight}
|
|
772
985
|
disabled={isMediaCapReached}
|
|
773
986
|
/>
|
|
774
987
|
);
|
|
775
988
|
}, [
|
|
776
|
-
|
|
777
|
-
|
|
989
|
+
activeMediaType,
|
|
990
|
+
editChildrenIncomplete,
|
|
778
991
|
isMediaCapReached,
|
|
779
992
|
onPressCamera,
|
|
780
993
|
onPressImage,
|
|
@@ -841,7 +1054,7 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
841
1054
|
onLoadFinish={handleOnFinishImage}
|
|
842
1055
|
onUploadError={handleImageUploadError}
|
|
843
1056
|
isEditMode={isEditMode}
|
|
844
|
-
|
|
1057
|
+
onUploadingChange={handleUploadingChange}
|
|
845
1058
|
/>
|
|
846
1059
|
)}
|
|
847
1060
|
{displayVideos.length > 0 && (
|
|
@@ -852,7 +1065,7 @@ const AmityPostComposerPage: FC<AmityPostComposerPageType> = ({
|
|
|
852
1065
|
onLoadFinish={handleOnFinishVideo}
|
|
853
1066
|
onUploadError={handleVideoUploadError}
|
|
854
1067
|
isEditMode={isEditMode}
|
|
855
|
-
|
|
1068
|
+
onUploadingChange={handleUploadingChange}
|
|
856
1069
|
/>
|
|
857
1070
|
)}
|
|
858
1071
|
</View>
|
|
@@ -12,6 +12,7 @@ export * from './useUiKitConfig';
|
|
|
12
12
|
export * from './useAmityGlobalSearchViewModel';
|
|
13
13
|
export * from './useCommunities';
|
|
14
14
|
export * from './useCommunity';
|
|
15
|
+
export * from './useCommunityPermission';
|
|
15
16
|
export * from './useCategory';
|
|
16
17
|
export * from './useUiKitReference';
|
|
17
18
|
export * from './isModerator';
|