@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.
Files changed (205) hide show
  1. package/README.md +7 -0
  2. package/lib/commonjs/core/assets/icons/post.js +1 -1
  3. package/lib/commonjs/core/assets/icons/post.js.map +1 -1
  4. package/lib/commonjs/core/assets/icons/story.js +1 -1
  5. package/lib/commonjs/core/assets/icons/story.js.map +1 -1
  6. package/lib/commonjs/core/assets/icons/xml.js +16 -2
  7. package/lib/commonjs/core/assets/icons/xml.js.map +1 -1
  8. package/lib/commonjs/core/legacy/file.js +145 -62
  9. package/lib/commonjs/core/legacy/file.js.map +1 -1
  10. package/lib/commonjs/core/stores/slices/feedSlice.js +3 -0
  11. package/lib/commonjs/core/stores/slices/feedSlice.js.map +1 -1
  12. package/lib/commonjs/core/stores/slices/globalfeedSlice.js +5 -0
  13. package/lib/commonjs/core/stores/slices/globalfeedSlice.js.map +1 -1
  14. package/lib/commonjs/core/stores/slices/localVideoThumbnailSlice.js +46 -0
  15. package/lib/commonjs/core/stores/slices/localVideoThumbnailSlice.js.map +1 -0
  16. package/lib/commonjs/core/stores/slices/uiSlice.js +5 -1
  17. package/lib/commonjs/core/stores/slices/uiSlice.js.map +1 -1
  18. package/lib/commonjs/core/stores/store/index.js +3 -1
  19. package/lib/commonjs/core/stores/store/index.js.map +1 -1
  20. package/lib/commonjs/social/components/LoadingImage/index.js +73 -12
  21. package/lib/commonjs/social/components/LoadingImage/index.js.map +1 -1
  22. package/lib/commonjs/social/components/LoadingImage/styles.js +8 -3
  23. package/lib/commonjs/social/components/LoadingImage/styles.js.map +1 -1
  24. package/lib/commonjs/social/components/LoadingVideo/index.js +90 -16
  25. package/lib/commonjs/social/components/LoadingVideo/index.js.map +1 -1
  26. package/lib/commonjs/social/components/LoadingVideo/styles.js +14 -5
  27. package/lib/commonjs/social/components/LoadingVideo/styles.js.map +1 -1
  28. package/lib/commonjs/social/components/MediaViewer/MediaViewer.js +13 -13
  29. package/lib/commonjs/social/components/MediaViewer/MediaViewer.js.map +1 -1
  30. package/lib/commonjs/social/components/MediaViewer/styles.js +11 -1
  31. package/lib/commonjs/social/components/MediaViewer/styles.js.map +1 -1
  32. package/lib/commonjs/social/components/PostContent/index.js +8 -29
  33. package/lib/commonjs/social/components/PostContent/index.js.map +1 -1
  34. package/lib/commonjs/social/components/SelectedMediaComponent/SelectedMediaComponent.js +109 -17
  35. package/lib/commonjs/social/components/SelectedMediaComponent/SelectedMediaComponent.js.map +1 -1
  36. package/lib/commonjs/social/components/legacy/LoadingVideo/index.js +17 -6
  37. package/lib/commonjs/social/components/legacy/LoadingVideo/index.js.map +1 -1
  38. package/lib/commonjs/social/elements/PostMediaElement/PostMediaElement.js +121 -23
  39. package/lib/commonjs/social/elements/PostMediaElement/PostMediaElement.js.map +1 -1
  40. package/lib/commonjs/social/elements/PostMediaElement/styles.js +7 -0
  41. package/lib/commonjs/social/elements/PostMediaElement/styles.js.map +1 -1
  42. package/lib/commonjs/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.js +11 -5
  43. package/lib/commonjs/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.js.map +1 -1
  44. package/lib/commonjs/social/features/feed/components/NewsFeed/NewsFeed.js +14 -0
  45. package/lib/commonjs/social/features/feed/components/NewsFeed/NewsFeed.js.map +1 -1
  46. package/lib/commonjs/social/features/post/Composer/PostComposer.js +234 -86
  47. package/lib/commonjs/social/features/post/Composer/PostComposer.js.map +1 -1
  48. package/lib/commonjs/social/hooks/index.js +11 -0
  49. package/lib/commonjs/social/hooks/index.js.map +1 -1
  50. package/lib/commonjs/social/hooks/useCommunities.js +5 -3
  51. package/lib/commonjs/social/hooks/useCommunities.js.map +1 -1
  52. package/lib/commonjs/social/hooks/useCommunityPermission.js +43 -0
  53. package/lib/commonjs/social/hooks/useCommunityPermission.js.map +1 -0
  54. package/lib/commonjs/social/hooks/useCustomRankingGlobalFeed.js +10 -1
  55. package/lib/commonjs/social/hooks/useCustomRankingGlobalFeed.js.map +1 -1
  56. package/lib/commonjs/social/hooks/useFile.js +11 -0
  57. package/lib/commonjs/social/hooks/useFile.js.map +1 -1
  58. package/lib/commonjs/social/screens/CommunityProfile/components/Header/Header.js +18 -7
  59. package/lib/commonjs/social/screens/CommunityProfile/components/Header/Header.js.map +1 -1
  60. package/lib/commonjs/social/utils/mediaAttachments.js +150 -0
  61. package/lib/commonjs/social/utils/mediaAttachments.js.map +1 -0
  62. package/lib/commonjs/social/utils/postChildren.js +72 -0
  63. package/lib/commonjs/social/utils/postChildren.js.map +1 -0
  64. package/lib/module/core/assets/icons/post.js +1 -1
  65. package/lib/module/core/assets/icons/post.js.map +1 -1
  66. package/lib/module/core/assets/icons/story.js +1 -1
  67. package/lib/module/core/assets/icons/story.js.map +1 -1
  68. package/lib/module/core/assets/icons/xml.js +13 -0
  69. package/lib/module/core/assets/icons/xml.js.map +1 -1
  70. package/lib/module/core/legacy/file.js +145 -62
  71. package/lib/module/core/legacy/file.js.map +1 -1
  72. package/lib/module/core/stores/slices/feedSlice.js +3 -0
  73. package/lib/module/core/stores/slices/feedSlice.js.map +1 -1
  74. package/lib/module/core/stores/slices/globalfeedSlice.js +5 -0
  75. package/lib/module/core/stores/slices/globalfeedSlice.js.map +1 -1
  76. package/lib/module/core/stores/slices/localVideoThumbnailSlice.js +41 -0
  77. package/lib/module/core/stores/slices/localVideoThumbnailSlice.js.map +1 -0
  78. package/lib/module/core/stores/slices/uiSlice.js +5 -1
  79. package/lib/module/core/stores/slices/uiSlice.js.map +1 -1
  80. package/lib/module/core/stores/store/index.js +3 -1
  81. package/lib/module/core/stores/store/index.js.map +1 -1
  82. package/lib/module/social/components/LoadingImage/index.js +74 -14
  83. package/lib/module/social/components/LoadingImage/index.js.map +1 -1
  84. package/lib/module/social/components/LoadingImage/styles.js +8 -3
  85. package/lib/module/social/components/LoadingImage/styles.js.map +1 -1
  86. package/lib/module/social/components/LoadingVideo/index.js +91 -17
  87. package/lib/module/social/components/LoadingVideo/index.js.map +1 -1
  88. package/lib/module/social/components/LoadingVideo/styles.js +14 -5
  89. package/lib/module/social/components/LoadingVideo/styles.js.map +1 -1
  90. package/lib/module/social/components/MediaViewer/MediaViewer.js +13 -13
  91. package/lib/module/social/components/MediaViewer/MediaViewer.js.map +1 -1
  92. package/lib/module/social/components/MediaViewer/styles.js +11 -1
  93. package/lib/module/social/components/MediaViewer/styles.js.map +1 -1
  94. package/lib/module/social/components/PostContent/index.js +8 -29
  95. package/lib/module/social/components/PostContent/index.js.map +1 -1
  96. package/lib/module/social/components/SelectedMediaComponent/SelectedMediaComponent.js +110 -18
  97. package/lib/module/social/components/SelectedMediaComponent/SelectedMediaComponent.js.map +1 -1
  98. package/lib/module/social/components/legacy/LoadingVideo/index.js +17 -6
  99. package/lib/module/social/components/legacy/LoadingVideo/index.js.map +1 -1
  100. package/lib/module/social/elements/PostMediaElement/PostMediaElement.js +123 -25
  101. package/lib/module/social/elements/PostMediaElement/PostMediaElement.js.map +1 -1
  102. package/lib/module/social/elements/PostMediaElement/styles.js +7 -0
  103. package/lib/module/social/elements/PostMediaElement/styles.js.map +1 -1
  104. package/lib/module/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.js +12 -6
  105. package/lib/module/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.js.map +1 -1
  106. package/lib/module/social/features/feed/components/NewsFeed/NewsFeed.js +15 -1
  107. package/lib/module/social/features/feed/components/NewsFeed/NewsFeed.js.map +1 -1
  108. package/lib/module/social/features/post/Composer/PostComposer.js +236 -89
  109. package/lib/module/social/features/post/Composer/PostComposer.js.map +1 -1
  110. package/lib/module/social/hooks/index.js +1 -0
  111. package/lib/module/social/hooks/index.js.map +1 -1
  112. package/lib/module/social/hooks/useCommunities.js +5 -3
  113. package/lib/module/social/hooks/useCommunities.js.map +1 -1
  114. package/lib/module/social/hooks/useCommunityPermission.js +37 -0
  115. package/lib/module/social/hooks/useCommunityPermission.js.map +1 -0
  116. package/lib/module/social/hooks/useCustomRankingGlobalFeed.js +10 -1
  117. package/lib/module/social/hooks/useCustomRankingGlobalFeed.js.map +1 -1
  118. package/lib/module/social/hooks/useFile.js +11 -0
  119. package/lib/module/social/hooks/useFile.js.map +1 -1
  120. package/lib/module/social/screens/CommunityProfile/components/Header/Header.js +19 -8
  121. package/lib/module/social/screens/CommunityProfile/components/Header/Header.js.map +1 -1
  122. package/lib/module/social/utils/mediaAttachments.js +139 -0
  123. package/lib/module/social/utils/mediaAttachments.js.map +1 -0
  124. package/lib/module/social/utils/postChildren.js +66 -0
  125. package/lib/module/social/utils/postChildren.js.map +1 -0
  126. package/lib/typescript/core/assets/icons/xml.d.ts +1 -0
  127. package/lib/typescript/core/assets/icons/xml.d.ts.map +1 -1
  128. package/lib/typescript/core/legacy/file.d.ts +32 -0
  129. package/lib/typescript/core/legacy/file.d.ts.map +1 -1
  130. package/lib/typescript/core/stores/slices/feedSlice.d.ts.map +1 -1
  131. package/lib/typescript/core/stores/slices/globalfeedSlice.d.ts.map +1 -1
  132. package/lib/typescript/core/stores/slices/localVideoThumbnailSlice.d.ts +35 -0
  133. package/lib/typescript/core/stores/slices/localVideoThumbnailSlice.d.ts.map +1 -0
  134. package/lib/typescript/core/stores/slices/uiSlice.d.ts +5 -0
  135. package/lib/typescript/core/stores/slices/uiSlice.d.ts.map +1 -1
  136. package/lib/typescript/core/stores/store/index.d.ts.map +1 -1
  137. package/lib/typescript/core/types/mention.d.ts +34 -0
  138. package/lib/typescript/core/types/mention.d.ts.map +1 -1
  139. package/lib/typescript/social/components/LoadingImage/index.d.ts +8 -2
  140. package/lib/typescript/social/components/LoadingImage/index.d.ts.map +1 -1
  141. package/lib/typescript/social/components/LoadingImage/styles.d.ts.map +1 -1
  142. package/lib/typescript/social/components/LoadingVideo/index.d.ts +9 -2
  143. package/lib/typescript/social/components/LoadingVideo/index.d.ts.map +1 -1
  144. package/lib/typescript/social/components/LoadingVideo/styles.d.ts +1 -0
  145. package/lib/typescript/social/components/LoadingVideo/styles.d.ts.map +1 -1
  146. package/lib/typescript/social/components/MediaViewer/MediaViewer.d.ts.map +1 -1
  147. package/lib/typescript/social/components/MediaViewer/styles.d.ts +1 -0
  148. package/lib/typescript/social/components/MediaViewer/styles.d.ts.map +1 -1
  149. package/lib/typescript/social/components/PostContent/index.d.ts.map +1 -1
  150. package/lib/typescript/social/components/SelectedMediaComponent/SelectedMediaComponent.d.ts +2 -2
  151. package/lib/typescript/social/components/SelectedMediaComponent/SelectedMediaComponent.d.ts.map +1 -1
  152. package/lib/typescript/social/components/legacy/LoadingVideo/index.d.ts.map +1 -1
  153. package/lib/typescript/social/elements/PostMediaElement/PostMediaElement.d.ts.map +1 -1
  154. package/lib/typescript/social/elements/PostMediaElement/styles.d.ts +4 -0
  155. package/lib/typescript/social/elements/PostMediaElement/styles.d.ts.map +1 -1
  156. package/lib/typescript/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.d.ts.map +1 -1
  157. package/lib/typescript/social/features/feed/components/NewsFeed/NewsFeed.d.ts.map +1 -1
  158. package/lib/typescript/social/features/post/Composer/PostComposer.d.ts.map +1 -1
  159. package/lib/typescript/social/hooks/index.d.ts +1 -0
  160. package/lib/typescript/social/hooks/index.d.ts.map +1 -1
  161. package/lib/typescript/social/hooks/useCommunities.d.ts +2 -1
  162. package/lib/typescript/social/hooks/useCommunities.d.ts.map +1 -1
  163. package/lib/typescript/social/hooks/useCommunityPermission.d.ts +14 -0
  164. package/lib/typescript/social/hooks/useCommunityPermission.d.ts.map +1 -0
  165. package/lib/typescript/social/hooks/useCustomRankingGlobalFeed.d.ts.map +1 -1
  166. package/lib/typescript/social/hooks/useFile.d.ts +11 -0
  167. package/lib/typescript/social/hooks/useFile.d.ts.map +1 -1
  168. package/lib/typescript/social/screens/CommunityProfile/components/Header/Header.d.ts.map +1 -1
  169. package/lib/typescript/social/utils/mediaAttachments.d.ts +91 -0
  170. package/lib/typescript/social/utils/mediaAttachments.d.ts.map +1 -0
  171. package/lib/typescript/social/utils/postChildren.d.ts +38 -0
  172. package/lib/typescript/social/utils/postChildren.d.ts.map +1 -0
  173. package/package.json +3 -3
  174. package/src/core/assets/icons/post.tsx +1 -1
  175. package/src/core/assets/icons/story.tsx +1 -1
  176. package/src/core/assets/icons/xml.ts +15 -0
  177. package/src/core/legacy/file.tsx +169 -84
  178. package/src/core/stores/slices/feedSlice.ts +3 -0
  179. package/src/core/stores/slices/globalfeedSlice.ts +5 -0
  180. package/src/core/stores/slices/localVideoThumbnailSlice.ts +58 -0
  181. package/src/core/stores/slices/uiSlice.ts +8 -0
  182. package/src/core/stores/store/index.ts +2 -0
  183. package/src/core/types/mention.ts +34 -0
  184. package/src/social/components/LoadingImage/index.tsx +81 -12
  185. package/src/social/components/LoadingImage/styles.ts +8 -3
  186. package/src/social/components/LoadingVideo/index.tsx +111 -21
  187. package/src/social/components/LoadingVideo/styles.ts +13 -4
  188. package/src/social/components/MediaViewer/MediaViewer.tsx +11 -12
  189. package/src/social/components/MediaViewer/styles.ts +9 -1
  190. package/src/social/components/PostContent/index.tsx +3 -35
  191. package/src/social/components/SelectedMediaComponent/SelectedMediaComponent.tsx +103 -13
  192. package/src/social/components/legacy/LoadingVideo/index.tsx +26 -15
  193. package/src/social/elements/PostMediaElement/PostMediaElement.tsx +139 -29
  194. package/src/social/elements/PostMediaElement/styles.ts +7 -0
  195. package/src/social/features/community/PendingRequest/components/PendingPostList/PendingPostList.tsx +11 -6
  196. package/src/social/features/feed/components/NewsFeed/NewsFeed.tsx +16 -1
  197. package/src/social/features/post/Composer/PostComposer.tsx +304 -91
  198. package/src/social/hooks/index.ts +1 -0
  199. package/src/social/hooks/useCommunities.ts +4 -2
  200. package/src/social/hooks/useCommunityPermission.ts +40 -0
  201. package/src/social/hooks/useCustomRankingGlobalFeed.ts +8 -1
  202. package/src/social/hooks/useFile.ts +11 -0
  203. package/src/social/screens/CommunityProfile/components/Header/Header.tsx +19 -10
  204. package/src/social/utils/mediaAttachments.ts +158 -0
  205. 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
- const [isUploading, setIsUploading] = (0, _react.useState)(false);
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 <= 10 || displayVideos.length <= 10);
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
- // Load the edited post's child media the same way the web UIKit does
154
- // (usePostByIds → batch getPostByIds), instead of the racy per-id fetch.
155
- const childrenPosts = (0, _usePostByIds.usePostByIds)((post === null || post === void 0 ? void 0 : post.children) ?? []);
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
- const url = await getImage({
165
- fileId: fileId,
166
- imageSize: _enums.ImageSizeState.full
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, _fileUrls$, _fileUrls$2;
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
- const fileUrls = await Promise.allSettled([fileId, thumbnailFileId].map(async id => {
181
- return await getImage({
182
- fileId: id,
183
- imageSize: _enums.ImageSizeState.full
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
- //@ts-ignore
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
- //@ts-ignore
193
- thumbNail: (_fileUrls$2 = fileUrls[1]) === null || _fileUrls$2 === void 0 ? void 0 : _fileUrls$2.value,
194
- postId: item.postId
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
- }, [getImage]);
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 (childrenPosts.length > 0) getPostInfo(childrenPosts);
214
- }, [getPostInfo, childrenPosts]);
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 === 10) return _reactNative.Alert.alert('Maximum upload limit reached', "You've reached the upload limit of 10 images. Any additional images will not be saved.");
393
- if (mediaType === 'video' && displayVideos.length === 10) return _reactNative.Alert.alert('Maximum upload limit reached', "You've reached the upload limit of 10 videos. Any additional videos will not be saved.");
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
- if (displayImages.length > 0) return pickCamera('photo');
423
- if (displayVideos.length > 0) return pickCamera('video');
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
- }, [displayImages.length, displayVideos.length, pickCamera]);
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 >= 10) 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.`);
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: 10 - current
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
- const setter = isPhoto ? setDisplayImages : setDisplayVideos;
448
- setter(prev => {
449
- // Silently drop duplicates, matching the web UIKit (which dedups by
450
- // file name). The picker copies each pick to a fresh temp uri, so uri
451
- // is not stable across picks — the original `fileName` is.
452
- const seen = new Set(prev.map(m => m.fileName));
453
- const additions = [];
454
- for (const asset of result.assets ?? []) {
455
- if (!asset.uri) continue;
456
- const fileName = asset.fileName ?? asset.uri.substring(asset.uri.lastIndexOf('/') + 1);
457
- if (seen.has(fileName)) continue; // duplicate → silently drop
458
- seen.add(fileName);
459
- additions.push({
460
- url: asset.uri,
461
- fileName,
462
- fileId: '',
463
- isUploaded: false
464
- });
465
- }
466
- const updated = [...prev, ...additions];
467
- return updated.length > 10 ? updated.slice(0, 10) : updated;
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
- }, [displayImages.length, displayVideos.length]);
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, index) => {
678
+ const handleOnFinishImage = (0, _react.useCallback)((fileId, fileUrl, fileName, _index, originalPath) => {
521
679
  setHasChangedAttachment(true);
522
- const imageObject = {
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, index, _, thumbnail) => {
687
+ const handleOnFinishVideo = (0, _react.useCallback)((fileId, fileUrl, fileName, _index, originalPath, thumbnail) => {
535
688
  setHasChangedAttachment(true);
536
- const imageObject = {
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 >= 10 || displayVideos.length >= 10;
700
+ const isMediaCapReached = displayImages.length >= _mediaAttachments.MAX_MEDIA_ATTACHMENTS || displayVideos.length >= _mediaAttachments.MAX_MEDIA_ATTACHMENTS;
553
701
  const renderDetailedAttachment = (0, _react.useCallback)(() => {
554
- if (isEditMode) return null;
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: 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: chosenMediaType,
717
+ chosenMediaType: activeMediaType,
570
718
  onHeightChange: setAttachmentBarHeight,
571
719
  disabled: isMediaCapReached
572
720
  });
573
- }, [chosenMediaType, isEditMode, isMediaCapReached, onPressCamera, onPressImage, onPressVideo, shouldShowDetailAttachment]);
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
- setIsUploading: setIsUploading
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
- setIsUploading: setIsUploading
776
+ onUploadingChange: handleUploadingChange
629
777
  }))), renderSuggestions({
630
778
  type: 'post'
631
779
  }), /*#__PURE__*/React.createElement(_reactNative.View, {