@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
|
@@ -13,11 +13,11 @@ var _types = require("../../../types");
|
|
|
13
13
|
var _CloseButtonIconElement = _interopRequireDefault(require("../../../elements/CloseButtonIconElement/CloseButtonIconElement"));
|
|
14
14
|
var _native = require("@react-navigation/native");
|
|
15
15
|
var _uiSlice = _interopRequireDefault(require("../../../../core/stores/slices/uiSlice"));
|
|
16
|
+
var _localVideoThumbnailSlice = _interopRequireDefault(require("../../../../core/stores/slices/localVideoThumbnailSlice"));
|
|
16
17
|
var _post = require("../../../../core/utils/post");
|
|
17
18
|
var _useAuth = _interopRequireDefault(require("../../../../core/hooks/useAuth"));
|
|
18
19
|
var _globalfeedSlice = _interopRequireDefault(require("../../../../core/stores/slices/globalfeedSlice"));
|
|
19
20
|
var _feed = require("../../../../core/legacy/feed");
|
|
20
|
-
var _usePostByIds = require("../../../../core/hooks/usePostByIds");
|
|
21
21
|
var _TextKeyElement = _interopRequireDefault(require("../../../elements/TextKeyElement/TextKeyElement"));
|
|
22
22
|
var _MediaAttachment = _interopRequireDefault(require("../components/MediaAttachment"));
|
|
23
23
|
var _DetailedMediaAttachment = _interopRequireDefault(require("../components/DetailedMediaAttachment"));
|
|
@@ -26,6 +26,7 @@ var _SelectedMediaComponent = require("../../../components/SelectedMediaComponen
|
|
|
26
26
|
var _tsSdkReactNative = require("@amityco/ts-sdk-react-native");
|
|
27
27
|
var _useMention = _interopRequireDefault(require("../../../hooks/useMention"));
|
|
28
28
|
var _errors = require("../../../utils/errors");
|
|
29
|
+
var _mediaAttachments = require("../../../utils/mediaAttachments");
|
|
29
30
|
var _constants = require("../../../../core/constants");
|
|
30
31
|
var _reactNativeControlledMentions = require("react-native-controlled-mentions");
|
|
31
32
|
var _store = require("../../../../core/stores/store");
|
|
@@ -39,7 +40,7 @@ const AmityPostComposerPage = ({
|
|
|
39
40
|
community,
|
|
40
41
|
post
|
|
41
42
|
}) => {
|
|
42
|
-
var _post$data;
|
|
43
|
+
var _post$data, _post$children;
|
|
43
44
|
(0, _hooks.useRequestPermission)({
|
|
44
45
|
onRequestPermissionFailed: () => {
|
|
45
46
|
_reactNative.Linking.openSettings();
|
|
@@ -58,9 +59,6 @@ const AmityPostComposerPage = ({
|
|
|
58
59
|
pageId
|
|
59
60
|
});
|
|
60
61
|
const styles = (0, _styles.useStyles)(themeStyles);
|
|
61
|
-
const {
|
|
62
|
-
getImage
|
|
63
|
-
} = (0, _hooks.useFile)();
|
|
64
62
|
const isEditMode = mode === _types.AmityPostComposerMode.EDIT;
|
|
65
63
|
const navigation = (0, _native.useNavigation)();
|
|
66
64
|
const {
|
|
@@ -81,6 +79,9 @@ const AmityPostComposerPage = ({
|
|
|
81
79
|
showToastMessage,
|
|
82
80
|
hideToastMessage
|
|
83
81
|
} = _uiSlice.default.actions;
|
|
82
|
+
const {
|
|
83
|
+
setLocalVideoThumbnails
|
|
84
|
+
} = _localVideoThumbnailSlice.default.actions;
|
|
84
85
|
const [inputMessage, setInputMessage] = (0, _react.useState)((post === null || post === void 0 || (_post$data = post.data) === null || _post$data === void 0 ? void 0 : _post$data.text) ?? '');
|
|
85
86
|
const [mentionsPosition, setMentionsPosition] = (0, _react.useState)([]);
|
|
86
87
|
const [chosenMediaType, setChosenMediaType] = (0, _react.useState)(null);
|
|
@@ -89,7 +90,14 @@ const AmityPostComposerPage = ({
|
|
|
89
90
|
const [mentionUsers, setMentionUsers] = (0, _react.useState)([]);
|
|
90
91
|
const [isSwipeup, setIsSwipeup] = (0, _react.useState)(true);
|
|
91
92
|
const [deletedPostIds, setDeletedPostIds] = (0, _react.useState)([]);
|
|
92
|
-
|
|
93
|
+
// Upload progress is tracked per media item, keyed by the item's `source`
|
|
94
|
+
// url — the same shape as `imageErrors`/`videoErrors` below. A
|
|
95
|
+
// single shared boolean was flipped back to false by whichever upload
|
|
96
|
+
// finished first, so Post/Save unlocked while the remaining frames were
|
|
97
|
+
// still in flight. Sources are unique across images and videos, so one set
|
|
98
|
+
// covers both attachment types.
|
|
99
|
+
const [uploadingSources, setUploadingSources] = (0, _react.useState)(new Set());
|
|
100
|
+
const isUploading = uploadingSources.size > 0;
|
|
93
101
|
const [hasChangedAttachment, setHasChangedAttachment] = (0, _react.useState)(false);
|
|
94
102
|
const [imageErrors, setImageErrors] = (0, _react.useState)(new Set());
|
|
95
103
|
const [videoErrors, setVideoErrors] = (0, _react.useState)(new Set());
|
|
@@ -116,7 +124,7 @@ const AmityPostComposerPage = ({
|
|
|
116
124
|
const effectiveCommunity = community ?? fetchedCommunity;
|
|
117
125
|
const privateCommunityId = !(effectiveCommunity !== null && effectiveCommunity !== void 0 && effectiveCommunity.isPublic) && (effectiveCommunity === null || effectiveCommunity === void 0 ? void 0 : effectiveCommunity.communityId);
|
|
118
126
|
const title = isEditMode ? 'Edit Post' : (effectiveCommunity === null || effectiveCommunity === void 0 ? void 0 : effectiveCommunity.displayName) ?? 'My Timeline';
|
|
119
|
-
const isInputValid = !isUploading && imageErrors.size === 0 && videoErrors.size === 0 && inputMessage.trim().length <= _constants.MAXIMUM_POST_CHARACTERS && (inputMessage.trim().length > 0 || displayImages.length > 0 || displayVideos.length > 0) && (displayImages.length <=
|
|
127
|
+
const isInputValid = !isUploading && imageErrors.size === 0 && videoErrors.size === 0 && inputMessage.trim().length <= _constants.MAXIMUM_POST_CHARACTERS && (inputMessage.trim().length > 0 || displayImages.length > 0 || displayVideos.length > 0) && (displayImages.length <= _mediaAttachments.MAX_MEDIA_ATTACHMENTS || displayVideos.length <= _mediaAttachments.MAX_MEDIA_ATTACHMENTS);
|
|
120
128
|
const {
|
|
121
129
|
renderInput,
|
|
122
130
|
renderSuggestions
|
|
@@ -137,8 +145,26 @@ const AmityPostComposerPage = ({
|
|
|
137
145
|
}]);
|
|
138
146
|
}
|
|
139
147
|
});
|
|
148
|
+
|
|
149
|
+
// The edited post carries its own hydrated children: every post payload
|
|
150
|
+
// ingests `postChildren` into the `post` cache (PAYLOAD2MODEL maps it to
|
|
151
|
+
// 'post'), and postLinkedObject reads them straight back out. So the media is
|
|
152
|
+
// known on the first frame, with no fetch — no second source to reconcile.
|
|
153
|
+
const editChildren = (post === null || post === void 0 ? void 0 : post.childrenPosts) ?? [];
|
|
154
|
+
|
|
155
|
+
// `childrenPosts` drops cache misses silently (postLinkedObject filters with
|
|
156
|
+
// isNonNullable), so a short array means we cannot see every child. There is
|
|
157
|
+
// no fallback fetch behind it, so treat that state as read-only: no
|
|
158
|
+
// attachment bar, no Save.
|
|
159
|
+
const editChildrenIncomplete = isEditMode && editChildren.length !== ((post === null || post === void 0 || (_post$children = post.children) === null || _post$children === void 0 ? void 0 : _post$children.length) ?? 0);
|
|
140
160
|
const checkIsEditValid = () => {
|
|
141
161
|
var _post$data2;
|
|
162
|
+
// `type` in onPressPost is derived from displayImages/displayVideos, so
|
|
163
|
+
// saving while a child is missing submits it as removed — `attachments: []`
|
|
164
|
+
// and type 'text' strips the post's media outright. Nothing the user can do
|
|
165
|
+
// in the composer makes an unrendered child safe to save, so key this on
|
|
166
|
+
// completeness alone rather than on hasChangedAttachment.
|
|
167
|
+
if (editChildrenIncomplete) return false;
|
|
142
168
|
return isInputValid && (inputMessage !== (post === null || post === void 0 || (_post$data2 = post.data) === null || _post$data2 === void 0 ? void 0 : _post$data2.text) || hasChangedAttachment);
|
|
143
169
|
};
|
|
144
170
|
const parsePostText = (0, _react.useCallback)((text, mentionUsersArr) => {
|
|
@@ -150,48 +176,81 @@ const AmityPostComposerPage = ({
|
|
|
150
176
|
return parsedText;
|
|
151
177
|
}, []);
|
|
152
178
|
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
179
|
+
// A child post's file is usually already in the SDK cache, where the linked
|
|
180
|
+
// object exposes it synchronously (getImageInfo / getVideoInfo /
|
|
181
|
+
// getVideoThumbnailInfo are `pullFromCache(['file','get', …])` over the very
|
|
182
|
+
// ids we'd otherwise refetch). Use the cached `fileUrl` when it's there and
|
|
183
|
+
// fall back to the round trip on a cache miss. `fileUrlWithSize` is just
|
|
184
|
+
// `?size=`, so both branches produce the same url.
|
|
185
|
+
const resolveFileUrl = (0, _react.useCallback)(async (fileUrl, fileId) => {
|
|
186
|
+
var _file$data;
|
|
187
|
+
if (fileUrl) return _tsSdkReactNative.FileRepository.fileUrlWithSize(fileUrl, _enums.ImageSizeState.full);
|
|
188
|
+
if (!fileId) return undefined;
|
|
189
|
+
|
|
190
|
+
// Resolve the file directly rather than through useFile: that hook
|
|
191
|
+
// answers a miss with the default *avatar* image, which is right for a
|
|
192
|
+
// profile picture and wrong for post media. A video whose thumbnail the
|
|
193
|
+
// server has not produced yet has no file to resolve, and handing back an
|
|
194
|
+
// avatar made the carousel render a person icon behind the play button as
|
|
195
|
+
// though it were the real frame — and, because the url looked valid, it
|
|
196
|
+
// was also stored as the local thumbnail the feed falls back on.
|
|
197
|
+
const file = await _tsSdkReactNative.FileRepository.getFile(fileId);
|
|
198
|
+
const resolved = file === null || file === void 0 || (_file$data = file.data) === null || _file$data === void 0 ? void 0 : _file$data.fileUrl;
|
|
199
|
+
return resolved ? _tsSdkReactNative.FileRepository.fileUrlWithSize(resolved, _enums.ImageSizeState.full) : undefined;
|
|
200
|
+
}, []);
|
|
156
201
|
const getPostInfo = (0, _react.useCallback)(async response => {
|
|
157
202
|
try {
|
|
158
203
|
const images = [];
|
|
159
204
|
const videos = [];
|
|
160
205
|
for (const item of response) {
|
|
161
206
|
if ((item === null || item === void 0 ? void 0 : item.dataType) === 'image') {
|
|
162
|
-
var _data;
|
|
207
|
+
var _data, _item$getImageInfo, _imageInfo$getWidth, _imageInfo$getHeight;
|
|
163
208
|
const fileId = item === null || item === void 0 || (_data = item.data) === null || _data === void 0 ? void 0 : _data.fileId;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
});
|
|
209
|
+
// Dimensions for composer frame-ratio classification (REQ-003d).
|
|
210
|
+
const imageInfo = item === null || item === void 0 || (_item$getImageInfo = item.getImageInfo) === null || _item$getImageInfo === void 0 ? void 0 : _item$getImageInfo.call(item);
|
|
211
|
+
const url = await resolveFileUrl(imageInfo === null || imageInfo === void 0 ? void 0 : imageInfo.fileUrl, fileId);
|
|
168
212
|
images.push({
|
|
169
213
|
url,
|
|
170
214
|
fileId,
|
|
171
215
|
fileName: fileId,
|
|
216
|
+
// A hydrated child has no local pick behind it, so its identity
|
|
217
|
+
// is the file it already is. Unique per child, stable for the
|
|
218
|
+
// whole session, and never equal to a picker file name — so a
|
|
219
|
+
// later pick is de-duplicated against the other staged picks
|
|
220
|
+
// only, which is the most the picker can tell us.
|
|
221
|
+
localId: fileId ?? item.postId,
|
|
172
222
|
isUploaded: true,
|
|
173
|
-
postId: item.postId
|
|
223
|
+
postId: item.postId,
|
|
224
|
+
width: imageInfo === null || imageInfo === void 0 || (_imageInfo$getWidth = imageInfo.getWidth) === null || _imageInfo$getWidth === void 0 ? void 0 : _imageInfo$getWidth.call(imageInfo),
|
|
225
|
+
height: imageInfo === null || imageInfo === void 0 || (_imageInfo$getHeight = imageInfo.getHeight) === null || _imageInfo$getHeight === void 0 ? void 0 : _imageInfo$getHeight.call(imageInfo)
|
|
174
226
|
});
|
|
175
227
|
} else if ((item === null || item === void 0 ? void 0 : item.dataType) === 'video') {
|
|
176
|
-
var _videoData$videoFileI,
|
|
228
|
+
var _videoData$videoFileI, _item$getVideoInfo, _item$getVideoThumbna, _videoInfo$getWidth, _videoInfo$getHeight, _videoInfo$getRotatio;
|
|
177
229
|
const videoData = item === null || item === void 0 ? void 0 : item.data;
|
|
178
230
|
const fileId = videoData === null || videoData === void 0 || (_videoData$videoFileI = videoData.videoFileId) === null || _videoData$videoFileI === void 0 ? void 0 : _videoData$videoFileI.original;
|
|
179
231
|
const thumbnailFileId = videoData === null || videoData === void 0 ? void 0 : videoData.thumbnailFileId;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
232
|
+
// STORED dims + rotation; rotation is applied at classify time
|
|
233
|
+
// (SelectedMediaComponent REQ-003d1 / AmityVideo REQ-SDK-002).
|
|
234
|
+
const videoInfo = item === null || item === void 0 || (_item$getVideoInfo = item.getVideoInfo) === null || _item$getVideoInfo === void 0 ? void 0 : _item$getVideoInfo.call(item);
|
|
235
|
+
const thumbnailInfo = item === null || item === void 0 || (_item$getVideoThumbna = item.getVideoThumbnailInfo) === null || _item$getVideoThumbna === void 0 ? void 0 : _item$getVideoThumbna.call(item);
|
|
236
|
+
// Settle the two urls independently: a missing or unreachable
|
|
237
|
+
// thumbnail must not cost us the video itself. `Promise.all` would
|
|
238
|
+
// reject the pair, abort this loop, and leave displayVideos empty
|
|
239
|
+
// — which now also keeps Save disabled (see checkIsEditValid).
|
|
240
|
+
const [urlResult, thumbNailResult] = await Promise.allSettled([resolveFileUrl(videoInfo === null || videoInfo === void 0 ? void 0 : videoInfo.fileUrl, fileId), resolveFileUrl(thumbnailInfo === null || thumbnailInfo === void 0 ? void 0 : thumbnailInfo.fileUrl, thumbnailFileId)]);
|
|
241
|
+
const url = urlResult.status === 'fulfilled' ? urlResult.value : undefined;
|
|
242
|
+
const thumbNail = thumbNailResult.status === 'fulfilled' ? thumbNailResult.value : undefined;
|
|
186
243
|
videos.push({
|
|
187
|
-
|
|
188
|
-
url: (_fileUrls$ = fileUrls[0]) === null || _fileUrls$ === void 0 ? void 0 : _fileUrls$.value,
|
|
244
|
+
url,
|
|
189
245
|
fileId: fileId,
|
|
190
246
|
fileName: fileId,
|
|
247
|
+
localId: fileId ?? item.postId,
|
|
191
248
|
isUploaded: true,
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
249
|
+
thumbNail,
|
|
250
|
+
postId: item.postId,
|
|
251
|
+
width: videoInfo === null || videoInfo === void 0 || (_videoInfo$getWidth = videoInfo.getWidth) === null || _videoInfo$getWidth === void 0 ? void 0 : _videoInfo$getWidth.call(videoInfo),
|
|
252
|
+
height: videoInfo === null || videoInfo === void 0 || (_videoInfo$getHeight = videoInfo.getHeight) === null || _videoInfo$getHeight === void 0 ? void 0 : _videoInfo$getHeight.call(videoInfo),
|
|
253
|
+
rotation: videoInfo === null || videoInfo === void 0 || (_videoInfo$getRotatio = videoInfo.getRotation) === null || _videoInfo$getRotatio === void 0 ? void 0 : _videoInfo$getRotatio.call(videoInfo)
|
|
195
254
|
});
|
|
196
255
|
}
|
|
197
256
|
}
|
|
@@ -204,14 +263,36 @@ const AmityPostComposerPage = ({
|
|
|
204
263
|
} catch (error) {
|
|
205
264
|
console.log('error: ', error);
|
|
206
265
|
}
|
|
207
|
-
}, [
|
|
266
|
+
}, [resolveFileUrl]);
|
|
208
267
|
(0, _react.useEffect)(() => {
|
|
209
268
|
setDeletedPostIds([]);
|
|
210
269
|
return () => setDeletedPostIds([]);
|
|
211
270
|
}, []);
|
|
271
|
+
|
|
272
|
+
// `post` comes from route params, so `editChildren` is reference-stable and
|
|
273
|
+
// this hydrates once. The hasChangedAttachment guard is belt-and-braces:
|
|
274
|
+
// getPostInfo assigns displayImages/displayVideos wholesale, so a re-run must
|
|
275
|
+
// never be able to wipe media the user added.
|
|
212
276
|
(0, _react.useEffect)(() => {
|
|
213
|
-
if (
|
|
214
|
-
|
|
277
|
+
if (editChildren.length === 0 || hasChangedAttachment) return;
|
|
278
|
+
getPostInfo(editChildren);
|
|
279
|
+
}, [getPostInfo, post === null || post === void 0 ? void 0 : post.childrenPosts, hasChangedAttachment]);
|
|
280
|
+
|
|
281
|
+
// Media type of the post being edited, read off the children's dataType —
|
|
282
|
+
// the same signal the web UIKit feeds into useMediaAttachmentVisible.
|
|
283
|
+
const editMediaType = (0, _react.useMemo)(() => {
|
|
284
|
+
if (editChildren.some(item => (item === null || item === void 0 ? void 0 : item.dataType) === 'image')) return _enums.mediaAttachment.image;
|
|
285
|
+
if (editChildren.some(item => (item === null || item === void 0 ? void 0 : item.dataType) === 'video')) return _enums.mediaAttachment.video;
|
|
286
|
+
return null;
|
|
287
|
+
}, [post === null || post === void 0 ? void 0 : post.childrenPosts]);
|
|
288
|
+
|
|
289
|
+
// The post's single media type, as every media entry point must see it: the
|
|
290
|
+
// staged attachments when there are any, otherwise the type the edited post
|
|
291
|
+
// already has. Read the display arrays rather than `chosenMediaType`, which
|
|
292
|
+
// is assigned in an effect and so trails them by a render. Once the user has
|
|
293
|
+
// cleared the attachments itself the post has no type again and all three
|
|
294
|
+
// buttons come back, matching web.
|
|
295
|
+
const activeMediaType = displayImages.length > 0 ? _enums.mediaAttachment.image : displayVideos.length > 0 ? _enums.mediaAttachment.video : hasChangedAttachment ? null : editMediaType;
|
|
215
296
|
const getMentionPositions = (0, _react.useCallback)((text, mentioneeIds) => {
|
|
216
297
|
let index = 0;
|
|
217
298
|
let mentions = [];
|
|
@@ -325,6 +406,21 @@ const AmityPostComposerPage = ({
|
|
|
325
406
|
return;
|
|
326
407
|
}
|
|
327
408
|
dispatch(hideToastMessage());
|
|
409
|
+
// The server has no thumbnail for these videos until it finishes
|
|
410
|
+
// transcoding, so hand the feed the frames the composer already decoded
|
|
411
|
+
// to bridge that window (PDT-4904, web parity: LayoutProvider
|
|
412
|
+
// videoThumbnail). Keyed by the uploaded original video's fileId, which
|
|
413
|
+
// is what the created post exposes as `data.videoFileId.original`.
|
|
414
|
+
if (type === 'video' && displayVideos.length > 0) {
|
|
415
|
+
dispatch(setLocalVideoThumbnails({
|
|
416
|
+
postId: response.postId,
|
|
417
|
+
// Only frames that actually decoded: an entry with no thumbnail
|
|
418
|
+
// would still satisfy the feed's lookup and suppress its own
|
|
419
|
+
// pending state, leaving the frame blank instead of showing the
|
|
420
|
+
// play affordance over a plain background.
|
|
421
|
+
videos: (0, _mediaAttachments.toLocalVideoThumbnails)(displayVideos)
|
|
422
|
+
}));
|
|
423
|
+
}
|
|
328
424
|
if (targetType === 'community' && ((effectiveCommunity === null || effectiveCommunity === void 0 ? void 0 : effectiveCommunity.postSetting) === 'ADMIN_REVIEW_POST_REQUIRED' || effectiveCommunity !== null && effectiveCommunity !== void 0 && effectiveCommunity.needApprovalOnPostCreation) && !isCommunityModerator) {
|
|
329
425
|
onPressClose();
|
|
330
426
|
return _reactNative.Alert.alert('Post submitted', 'Your post has been submitted to the pending list. It will be reviewed by community moderator', [{
|
|
@@ -355,7 +451,7 @@ const AmityPostComposerPage = ({
|
|
|
355
451
|
toastMessage: errorMessage
|
|
356
452
|
}));
|
|
357
453
|
}
|
|
358
|
-
}, [addPostToGlobalFeed, chosenMediaType, effectiveCommunity, deletedPostIds, dispatch, displayImages, displayVideos, hideToastMessage, inputMessage, isEditMode, isInputValid, isCommunityModerator, mentionUsers, mentionsPosition, onPressClose, post, showToastMessage, targetId, targetType, updateByPostId]);
|
|
454
|
+
}, [addPostToGlobalFeed, chosenMediaType, effectiveCommunity, deletedPostIds, dispatch, displayImages, displayVideos, hideToastMessage, inputMessage, isEditMode, isInputValid, isCommunityModerator, mentionUsers, mentionsPosition, onPressClose, setLocalVideoThumbnails, post, showToastMessage, targetId, targetType, updateByPostId]);
|
|
359
455
|
let tEvents = [];
|
|
360
456
|
const onSwipe = (0, _react.useCallback)(touchEvent => {
|
|
361
457
|
const swipeUp = touchEvent[0] > touchEvent[touchEvent.length - 1];
|
|
@@ -377,6 +473,14 @@ const AmityPostComposerPage = ({
|
|
|
377
473
|
return {
|
|
378
474
|
url: url,
|
|
379
475
|
fileName: fileName,
|
|
476
|
+
// Camera captures land on a freshly generated temp path, so the path
|
|
477
|
+
// itself is the identity: unique per shot, and stable once the upload
|
|
478
|
+
// rewrites `url` to the remote one. Deliberately no
|
|
479
|
+
// `localFileName` — two shots of the same scene are two distinct
|
|
480
|
+
// attachments and must never de-duplicate against each other, nor
|
|
481
|
+
// against a library pick that happens to share a basename.
|
|
482
|
+
localId: url,
|
|
483
|
+
localUrl: url,
|
|
380
484
|
fileId: '',
|
|
381
485
|
isUploaded: false
|
|
382
486
|
};
|
|
@@ -389,8 +493,8 @@ const AmityPostComposerPage = ({
|
|
|
389
493
|
return setChosenMediaType(null);
|
|
390
494
|
}, [displayImages === null || displayImages === void 0 ? void 0 : displayImages.length, displayVideos === null || displayVideos === void 0 ? void 0 : displayVideos.length]);
|
|
391
495
|
const pickCamera = (0, _react.useCallback)(async mediaType => {
|
|
392
|
-
if (mediaType === 'photo' && displayImages.length
|
|
393
|
-
if (mediaType === 'video' && displayVideos.length
|
|
496
|
+
if (mediaType === 'photo' && displayImages.length >= _mediaAttachments.MAX_MEDIA_ATTACHMENTS) return _reactNative.Alert.alert('Maximum upload limit reached', "You've reached the upload limit of 10 images. Any additional images will not be saved.");
|
|
497
|
+
if (mediaType === 'video' && displayVideos.length >= _mediaAttachments.MAX_MEDIA_ATTACHMENTS) return _reactNative.Alert.alert('Maximum upload limit reached', "You've reached the upload limit of 10 videos. Any additional videos will not be saved.");
|
|
394
498
|
try {
|
|
395
499
|
const result = await (0, _reactNativeImagePicker.launchCamera)({
|
|
396
500
|
mediaType: mediaType,
|
|
@@ -419,8 +523,13 @@ const AmityPostComposerPage = ({
|
|
|
419
523
|
}
|
|
420
524
|
}, [displayImages.length, displayVideos.length, processMedia]);
|
|
421
525
|
const onPressCamera = (0, _react.useCallback)(async () => {
|
|
422
|
-
|
|
423
|
-
|
|
526
|
+
// The camera button is always rendered — unlike photo/video, neither
|
|
527
|
+
// attachment bar hides it on `chosenMediaType` — so this is the one entry
|
|
528
|
+
// point that must decide the capture type itself. Keying it on the staged
|
|
529
|
+
// arrays alone offered both types while an edited post's media was still
|
|
530
|
+
// hydrating, which is how a video could be shot onto an image post.
|
|
531
|
+
if (activeMediaType === _enums.mediaAttachment.image) return pickCamera('photo');
|
|
532
|
+
if (activeMediaType === _enums.mediaAttachment.video) return pickCamera('video');
|
|
424
533
|
if (_reactNative.Platform.OS === 'ios') return pickCamera('mixed');
|
|
425
534
|
_reactNative.Alert.alert('Open Camera', null, [{
|
|
426
535
|
text: 'Photo',
|
|
@@ -429,46 +538,95 @@ const AmityPostComposerPage = ({
|
|
|
429
538
|
text: 'Video',
|
|
430
539
|
onPress: async () => pickCamera('video')
|
|
431
540
|
}]);
|
|
432
|
-
}, [
|
|
541
|
+
}, [activeMediaType, pickCamera]);
|
|
433
542
|
|
|
434
543
|
// Open the native OS camera roll (matches the web UIKit's native file
|
|
435
544
|
// picker). Appends the picked media, de-duplicating by uri and capping at 10.
|
|
436
545
|
const pickFromLibrary = (0, _react.useCallback)(async mediaType => {
|
|
437
546
|
var _result$assets;
|
|
438
547
|
const isPhoto = mediaType === 'photo';
|
|
548
|
+
// A post holds one media type. The bar hides the mismatched button, but
|
|
549
|
+
// guard the entry point too so a tap landing before the button updates
|
|
550
|
+
// can't replace the existing attachments with the other type.
|
|
551
|
+
const pickedType = isPhoto ? _enums.mediaAttachment.image : _enums.mediaAttachment.video;
|
|
552
|
+
if (activeMediaType && activeMediaType !== pickedType) return;
|
|
439
553
|
const current = isPhoto ? displayImages.length : displayVideos.length;
|
|
440
|
-
if (current >=
|
|
554
|
+
if (current >= _mediaAttachments.MAX_MEDIA_ATTACHMENTS) return _reactNative.Alert.alert('Maximum upload limit reached', `You've reached the upload limit of 10 ${isPhoto ? 'images' : 'videos'}. Any additional ${isPhoto ? 'images' : 'videos'} will not be saved.`);
|
|
441
555
|
const result = await (0, _reactNativeImagePicker.launchImageLibrary)({
|
|
442
556
|
mediaType,
|
|
443
557
|
quality: 1,
|
|
444
|
-
selectionLimit:
|
|
558
|
+
selectionLimit: _mediaAttachments.MAX_MEDIA_ATTACHMENTS - current,
|
|
559
|
+
// Required for `asset.id`, which is what recognises a re-pick of an
|
|
560
|
+
// asset already staged. Without it the iOS picker builds its
|
|
561
|
+
// PHPickerConfiguration without a photo library, so results carry no
|
|
562
|
+
// assetIdentifier and the native side never resolves a PHAsset —
|
|
563
|
+
// leaving `id` undefined and the fallback key, `fileName`, a fresh
|
|
564
|
+
// UUID per pick. Both dedupe keys were therefore unique every time
|
|
565
|
+
// and picking the same photo twice staged it twice. Android was
|
|
566
|
+
// unaffected: its filename is the MediaStore display name, stable
|
|
567
|
+
// across picks.
|
|
568
|
+
includeExtra: true
|
|
445
569
|
});
|
|
446
570
|
if (result.didCancel || !((_result$assets = result.assets) !== null && _result$assets !== void 0 && _result$assets.length)) return;
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
571
|
+
|
|
572
|
+
// iOS video is the one pick whose dims cannot be trusted: the picker
|
|
573
|
+
// reports the track's stored `naturalSize` with no rotation alongside
|
|
574
|
+
// it, so a portrait iPhone clip arrives as 1920×1080 and would
|
|
575
|
+
// classify the frame 16:9 (PDT-4904). Images on both platforms, and
|
|
576
|
+
// Android video (its extractor swaps w/h on 90°/270°), are already
|
|
577
|
+
// display-oriented and unaffected. With no rotation to correct the iOS
|
|
578
|
+
// value by, carry nothing and let SelectedMediaComponent measure the
|
|
579
|
+
// generated thumbnail, which is a rendered frame.
|
|
580
|
+
const isIosVideoDimsUnusable = !isPhoto && _reactNative.Platform.OS === 'ios';
|
|
581
|
+
const candidates = (result.assets ?? []).flatMap(asset => {
|
|
582
|
+
if (!asset.uri) return [];
|
|
583
|
+
const fileName = asset.fileName ?? asset.uri.substring(asset.uri.lastIndexOf('/') + 1);
|
|
584
|
+
return [{
|
|
585
|
+
url: asset.uri,
|
|
586
|
+
fileName,
|
|
587
|
+
// `asset.id` is the library's own identifier for the asset (PHAsset
|
|
588
|
+
// localIdentifier / MediaStore id) and is the one key that survives
|
|
589
|
+
// the picker copying the pick to a new temp uri every time. It is
|
|
590
|
+
// not populated by every picker configuration, so fall back to the
|
|
591
|
+
// file name and keep the name as a second key either way — that is
|
|
592
|
+
// what catches a re-pick when no id came back.
|
|
593
|
+
localId: asset.id ?? fileName,
|
|
594
|
+
localUrl: asset.uri,
|
|
595
|
+
localFileName: fileName,
|
|
596
|
+
fileId: '',
|
|
597
|
+
isUploaded: false,
|
|
598
|
+
width: isIosVideoDimsUnusable ? undefined : asset.width,
|
|
599
|
+
height: isIosVideoDimsUnusable ? undefined : asset.height
|
|
600
|
+
}];
|
|
468
601
|
});
|
|
469
|
-
|
|
602
|
+
|
|
603
|
+
// Stage the picks and claim their upload slots in one go. Staging alone
|
|
604
|
+
// used to flip `isInputValid` true on the very next render — the frames
|
|
605
|
+
// exist, and `uploadingSources` is still empty because each child only
|
|
606
|
+
// registers from its own mount effect — so Post sat enabled for ~940 ms
|
|
607
|
+
// with nothing uploaded and a quick tap posted media that had no fileId
|
|
608
|
+
// yet. Seeding here makes "staged" and "gated" the same update; children
|
|
609
|
+
// still clear their own entry when they finish, and the unmount cleanup
|
|
610
|
+
// still covers a frame removed mid-flight.
|
|
611
|
+
const staged = isPhoto ? displayImages : displayVideos;
|
|
612
|
+
const accepted = (0, _mediaAttachments.appendWithinCap)(staged, (0, _mediaAttachments.dedupeMediaPicks)(staged, candidates));
|
|
613
|
+
// Only the entries that actually survived dedupe and the cap — seeding a
|
|
614
|
+
// key no child will ever mount for would leave Post disabled forever.
|
|
615
|
+
const newlyStaged = accepted.slice(staged.length);
|
|
616
|
+
const setter = isPhoto ? setDisplayImages : setDisplayVideos;
|
|
617
|
+
setter(accepted);
|
|
618
|
+
setUploadingSources(prev => newlyStaged.reduce((acc, item) => (0, _mediaAttachments.toggleUploadingSource)(acc, item.url, true), prev));
|
|
619
|
+
}, [activeMediaType, displayImages, displayVideos]);
|
|
470
620
|
const onPressImage = (0, _react.useCallback)(() => pickFromLibrary('photo'), [pickFromLibrary]);
|
|
471
621
|
const onPressVideo = (0, _react.useCallback)(() => pickFromLibrary('video'), [pickFromLibrary]);
|
|
622
|
+
|
|
623
|
+
// Children report their own upload start/end keyed by `source`, and a child
|
|
624
|
+
// that unmounts mid-upload (frame removed while still uploading) clears its
|
|
625
|
+
// own entry from its cleanup — otherwise the set would never empty and Post
|
|
626
|
+
// would stay disabled forever.
|
|
627
|
+
const handleUploadingChange = (0, _react.useCallback)((uploading, source) => {
|
|
628
|
+
setUploadingSources(prev => (0, _mediaAttachments.toggleUploadingSource)(prev, source, uploading));
|
|
629
|
+
}, []);
|
|
472
630
|
const handleImageUploadError = (0, _react.useCallback)((hasError, source) => {
|
|
473
631
|
setImageErrors(prev => {
|
|
474
632
|
const newSet = new Set(prev);
|
|
@@ -493,7 +651,7 @@ const AmityPostComposerPage = ({
|
|
|
493
651
|
}, []);
|
|
494
652
|
const handleOnCloseImage = (0, _react.useCallback)((originalPath, _, postId) => {
|
|
495
653
|
setHasChangedAttachment(true);
|
|
496
|
-
setDeletedPostIds(prev => [...prev, postId]);
|
|
654
|
+
if (postId) setDeletedPostIds(prev => [...prev, postId]);
|
|
497
655
|
setImageErrors(prev => {
|
|
498
656
|
const newSet = new Set(prev);
|
|
499
657
|
newSet.delete(originalPath);
|
|
@@ -506,7 +664,7 @@ const AmityPostComposerPage = ({
|
|
|
506
664
|
}, []);
|
|
507
665
|
const handleOnCloseVideo = (0, _react.useCallback)((originalPath, _, postId) => {
|
|
508
666
|
setHasChangedAttachment(true);
|
|
509
|
-
setDeletedPostIds(prev => [...prev, postId]);
|
|
667
|
+
if (postId) setDeletedPostIds(prev => [...prev, postId]);
|
|
510
668
|
setVideoErrors(prev => {
|
|
511
669
|
const newSet = new Set(prev);
|
|
512
670
|
newSet.delete(originalPath);
|
|
@@ -517,47 +675,37 @@ const AmityPostComposerPage = ({
|
|
|
517
675
|
return newData;
|
|
518
676
|
});
|
|
519
677
|
}, []);
|
|
520
|
-
const handleOnFinishImage = (0, _react.useCallback)((fileId, fileUrl, fileName,
|
|
678
|
+
const handleOnFinishImage = (0, _react.useCallback)((fileId, fileUrl, fileName, _index, originalPath) => {
|
|
521
679
|
setHasChangedAttachment(true);
|
|
522
|
-
|
|
680
|
+
setDisplayImages(prevData => (0, _mediaAttachments.applyFinishedUpload)(prevData, originalPath, {
|
|
523
681
|
url: fileUrl,
|
|
524
682
|
fileId: fileId,
|
|
525
683
|
fileName: fileName,
|
|
526
684
|
isUploaded: true
|
|
527
|
-
};
|
|
528
|
-
setDisplayImages(prevData => {
|
|
529
|
-
const newData = [...prevData];
|
|
530
|
-
newData[index] = imageObject;
|
|
531
|
-
return newData;
|
|
532
|
-
});
|
|
685
|
+
}));
|
|
533
686
|
}, []);
|
|
534
|
-
const handleOnFinishVideo = (0, _react.useCallback)((fileId, fileUrl, fileName,
|
|
687
|
+
const handleOnFinishVideo = (0, _react.useCallback)((fileId, fileUrl, fileName, _index, originalPath, thumbnail) => {
|
|
535
688
|
setHasChangedAttachment(true);
|
|
536
|
-
|
|
689
|
+
setDisplayVideos(prevData => (0, _mediaAttachments.applyFinishedUpload)(prevData, originalPath, {
|
|
537
690
|
url: fileUrl,
|
|
538
691
|
fileId: fileId,
|
|
539
692
|
fileName: fileName,
|
|
540
693
|
isUploaded: true,
|
|
541
694
|
thumbNail: thumbnail
|
|
542
|
-
};
|
|
543
|
-
setDisplayVideos(prevData => {
|
|
544
|
-
const newData = [...prevData];
|
|
545
|
-
newData[index] = imageObject;
|
|
546
|
-
return newData;
|
|
547
|
-
});
|
|
695
|
+
}));
|
|
548
696
|
}, []);
|
|
549
697
|
|
|
550
698
|
// PDT-4310 / PDT-4312: at the 10-attachment cap the camera + gallery icons
|
|
551
699
|
// are disabled until the user removes a frame.
|
|
552
|
-
const isMediaCapReached = displayImages.length >=
|
|
700
|
+
const isMediaCapReached = displayImages.length >= _mediaAttachments.MAX_MEDIA_ATTACHMENTS || displayVideos.length >= _mediaAttachments.MAX_MEDIA_ATTACHMENTS;
|
|
553
701
|
const renderDetailedAttachment = (0, _react.useCallback)(() => {
|
|
554
|
-
if (
|
|
702
|
+
if (editChildrenIncomplete) return null;
|
|
555
703
|
if (shouldShowDetailAttachment) {
|
|
556
704
|
return /*#__PURE__*/React.createElement(_DetailedMediaAttachment.default, {
|
|
557
705
|
onPressCamera: onPressCamera,
|
|
558
706
|
onPressImage: onPressImage,
|
|
559
707
|
onPressVideo: onPressVideo,
|
|
560
|
-
chosenMediaType:
|
|
708
|
+
chosenMediaType: activeMediaType,
|
|
561
709
|
onHeightChange: setAttachmentBarHeight,
|
|
562
710
|
disabled: isMediaCapReached
|
|
563
711
|
});
|
|
@@ -566,11 +714,11 @@ const AmityPostComposerPage = ({
|
|
|
566
714
|
onPressCamera: onPressCamera,
|
|
567
715
|
onPressImage: onPressImage,
|
|
568
716
|
onPressVideo: onPressVideo,
|
|
569
|
-
chosenMediaType:
|
|
717
|
+
chosenMediaType: activeMediaType,
|
|
570
718
|
onHeightChange: setAttachmentBarHeight,
|
|
571
719
|
disabled: isMediaCapReached
|
|
572
720
|
});
|
|
573
|
-
}, [
|
|
721
|
+
}, [activeMediaType, editChildrenIncomplete, isMediaCapReached, onPressCamera, onPressImage, onPressVideo, shouldShowDetailAttachment]);
|
|
574
722
|
if (isExcluded) return null;
|
|
575
723
|
return /*#__PURE__*/React.createElement(_reactNativeSafeAreaContext.SafeAreaView, {
|
|
576
724
|
testID: accessibilityId,
|
|
@@ -617,7 +765,7 @@ const AmityPostComposerPage = ({
|
|
|
617
765
|
onLoadFinish: handleOnFinishImage,
|
|
618
766
|
onUploadError: handleImageUploadError,
|
|
619
767
|
isEditMode: isEditMode,
|
|
620
|
-
|
|
768
|
+
onUploadingChange: handleUploadingChange
|
|
621
769
|
}), displayVideos.length > 0 && /*#__PURE__*/React.createElement(_SelectedMediaComponent.SelectedMediaComponent, {
|
|
622
770
|
mediaType: "video",
|
|
623
771
|
media: displayVideos,
|
|
@@ -625,7 +773,7 @@ const AmityPostComposerPage = ({
|
|
|
625
773
|
onLoadFinish: handleOnFinishVideo,
|
|
626
774
|
onUploadError: handleVideoUploadError,
|
|
627
775
|
isEditMode: isEditMode,
|
|
628
|
-
|
|
776
|
+
onUploadingChange: handleUploadingChange
|
|
629
777
|
}))), renderSuggestions({
|
|
630
778
|
type: 'post'
|
|
631
779
|
}), /*#__PURE__*/React.createElement(_reactNative.View, {
|