@ethora/chat-component-rn 26.7.7 → 26.7.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/MainComponents/ChatRoom.js +17 -8
- package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MediaMessage.js +8 -5
- package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MessageContainer.js +5 -1
- package/lib/commonjs/components/MainComponents/MessageContainer.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MessageList.js +2 -1
- package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +51 -12
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js +12 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js +13 -2
- package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js +13 -2
- package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
- package/lib/commonjs/components/styled/MessageImage.js +3 -1
- package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
- package/lib/commonjs/components/styled/UnsupportedType.js +14 -4
- package/lib/commonjs/components/styled/UnsupportedType.js.map +1 -1
- package/lib/commonjs/components/styled/VideoMessage.js +3 -1
- package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +46 -31
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/helpers/createMessageFromXml.js +19 -1
- package/lib/commonjs/helpers/createMessageFromXml.js.map +1 -1
- package/lib/commonjs/helpers/getDisplayFileName.js +79 -0
- package/lib/commonjs/helpers/getDisplayFileName.js.map +1 -0
- package/lib/commonjs/helpers/getServerReadTimestamp.js +125 -0
- package/lib/commonjs/helpers/getServerReadTimestamp.js.map +1 -0
- package/lib/commonjs/helpers/initXmppRooms.js +9 -7
- package/lib/commonjs/helpers/initXmppRooms.js.map +1 -1
- package/lib/commonjs/helpers/isOwnMessage.js +75 -0
- package/lib/commonjs/helpers/isOwnMessage.js.map +1 -0
- package/lib/commonjs/helpers/mimeToExtension.js +6 -14
- package/lib/commonjs/helpers/mimeToExtension.js.map +1 -1
- package/lib/commonjs/hooks/useChatRoomFocus.js +17 -8
- package/lib/commonjs/hooks/useChatRoomFocus.js.map +1 -1
- package/lib/commonjs/hooks/useChatWrapperInit.js +12 -7
- package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/commonjs/networking/stanzaHandlers.js +20 -2
- package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
- package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js +45 -11
- package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
- package/lib/commonjs/networking/xmppClient.js +36 -2
- package/lib/commonjs/networking/xmppClient.js.map +1 -1
- package/lib/commonjs/roomStore/index.js +35 -2
- package/lib/commonjs/roomStore/index.js.map +1 -1
- package/lib/commonjs/roomStore/persistence.js +200 -11
- package/lib/commonjs/roomStore/persistence.js.map +1 -1
- package/lib/commonjs/roomStore/roomsSlice.js +83 -6
- package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
- package/lib/module/components/MainComponents/ChatRoom.js +17 -8
- package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/module/components/MainComponents/MediaMessage.js +9 -6
- package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/module/components/MainComponents/MessageContainer.js +5 -1
- package/lib/module/components/MainComponents/MessageContainer.js.map +1 -1
- package/lib/module/components/MainComponents/MessageList.js +2 -1
- package/lib/module/components/MainComponents/MessageList.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +52 -13
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessageFile.js +12 -1
- package/lib/module/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js +13 -2
- package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessageVideo.js +13 -2
- package/lib/module/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
- package/lib/module/components/styled/MessageImage.js +3 -1
- package/lib/module/components/styled/MessageImage.js.map +1 -1
- package/lib/module/components/styled/UnsupportedType.js +14 -4
- package/lib/module/components/styled/UnsupportedType.js.map +1 -1
- package/lib/module/components/styled/VideoMessage.js +3 -1
- package/lib/module/components/styled/VideoMessage.js.map +1 -1
- package/lib/module/context/xmppProvider.js +47 -32
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/helpers/createMessageFromXml.js +19 -1
- package/lib/module/helpers/createMessageFromXml.js.map +1 -1
- package/lib/module/helpers/getDisplayFileName.js +73 -0
- package/lib/module/helpers/getDisplayFileName.js.map +1 -0
- package/lib/module/helpers/getServerReadTimestamp.js +120 -0
- package/lib/module/helpers/getServerReadTimestamp.js.map +1 -0
- package/lib/module/helpers/initXmppRooms.js +9 -7
- package/lib/module/helpers/initXmppRooms.js.map +1 -1
- package/lib/module/helpers/isOwnMessage.js +70 -0
- package/lib/module/helpers/isOwnMessage.js.map +1 -0
- package/lib/module/helpers/mimeToExtension.js +6 -13
- package/lib/module/helpers/mimeToExtension.js.map +1 -1
- package/lib/module/hooks/useChatRoomFocus.js +17 -8
- package/lib/module/hooks/useChatRoomFocus.js.map +1 -1
- package/lib/module/hooks/useChatWrapperInit.js +12 -7
- package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/module/networking/stanzaHandlers.js +20 -2
- package/lib/module/networking/stanzaHandlers.js.map +1 -1
- package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js +45 -11
- package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
- package/lib/module/networking/xmppClient.js +36 -2
- package/lib/module/networking/xmppClient.js.map +1 -1
- package/lib/module/roomStore/index.js +37 -4
- package/lib/module/roomStore/index.js.map +1 -1
- package/lib/module/roomStore/persistence.js +197 -11
- package/lib/module/roomStore/persistence.js.map +1 -1
- package/lib/module/roomStore/roomsSlice.js +83 -6
- package/lib/module/roomStore/roomsSlice.js.map +1 -1
- package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MessageContainer.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MessageList.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageFile.d.ts +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageFile.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessagePhoto.d.ts +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessagePhoto.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageVideo.d.ts +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageVideo.d.ts.map +1 -1
- package/lib/typescript/components/styled/MessageImage.d.ts +6 -0
- package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
- package/lib/typescript/components/styled/UnsupportedType.d.ts.map +1 -1
- package/lib/typescript/components/styled/VideoMessage.d.ts +6 -0
- package/lib/typescript/components/styled/VideoMessage.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/helpers/createMessageFromXml.d.ts.map +1 -1
- package/lib/typescript/helpers/getDisplayFileName.d.ts +49 -0
- package/lib/typescript/helpers/getDisplayFileName.d.ts.map +1 -0
- package/lib/typescript/helpers/getServerReadTimestamp.d.ts +69 -0
- package/lib/typescript/helpers/getServerReadTimestamp.d.ts.map +1 -0
- package/lib/typescript/helpers/initXmppRooms.d.ts.map +1 -1
- package/lib/typescript/helpers/isOwnMessage.d.ts +36 -0
- package/lib/typescript/helpers/isOwnMessage.d.ts.map +1 -0
- package/lib/typescript/helpers/mimeToExtension.d.ts +0 -15
- package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -1
- package/lib/typescript/hooks/useChatRoomFocus.d.ts +4 -1
- package/lib/typescript/hooks/useChatRoomFocus.d.ts.map +1 -1
- package/lib/typescript/hooks/useChatWrapperInit.d.ts.map +1 -1
- package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts +9 -2
- package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts.map +1 -1
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/roomStore/index.d.ts.map +1 -1
- package/lib/typescript/roomStore/persistence.d.ts +18 -1
- package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts +13 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/MainComponents/ChatRoom.tsx +21 -10
- package/src/components/MainComponents/MediaMessage.tsx +8 -5
- package/src/components/MainComponents/MessageContainer.tsx +5 -1
- package/src/components/MainComponents/MessageList.tsx +2 -1
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +63 -26
- package/src/components/RoomComponents/styled/LastMessageFile.tsx +14 -2
- package/src/components/RoomComponents/styled/LastMessagePhoto.tsx +14 -2
- package/src/components/RoomComponents/styled/LastMessageVideo.tsx +14 -2
- package/src/components/styled/MessageImage.tsx +8 -1
- package/src/components/styled/UnsupportedType.tsx +14 -4
- package/src/components/styled/VideoMessage.tsx +8 -1
- package/src/context/xmppProvider.tsx +47 -24
- package/src/helpers/createMessageFromXml.ts +20 -1
- package/src/helpers/getDisplayFileName.ts +87 -0
- package/src/helpers/getServerReadTimestamp.ts +120 -0
- package/src/helpers/initXmppRooms.tsx +9 -9
- package/src/helpers/isOwnMessage.ts +90 -0
- package/src/helpers/mimeToExtension.ts +6 -22
- package/src/hooks/useChatRoomFocus.ts +14 -5
- package/src/hooks/useChatWrapperInit.ts +12 -7
- package/src/networking/stanzaHandlers.ts +20 -6
- package/src/networking/xmpp/flushLastViewedToPrivateStore.ts +65 -18
- package/src/networking/xmppClient.ts +41 -2
- package/src/roomStore/index.ts +40 -4
- package/src/roomStore/persistence.ts +197 -7
- package/src/roomStore/roomsSlice.ts +84 -6
|
@@ -21,7 +21,10 @@ interface UseChatRoomFocusOptions {
|
|
|
21
21
|
* Without this hook, `useUnread()` will always return 0 for the chat
|
|
22
22
|
* room because the SDK assumes "mounted == active". With this hook,
|
|
23
23
|
* focus marks the room visible (clearing the badge) and blur stamps
|
|
24
|
-
* `lastViewedTimestamp
|
|
24
|
+
* `lastViewedTimestamp` to the newest SERVER-acknowledged message so
|
|
25
|
+
* future messages count as unread. Deliberately not `Date.now()`: a
|
|
26
|
+
* device clock running ahead would write a future marker that the
|
|
27
|
+
* forward-only private-store merge could never correct again (bug #38).
|
|
25
28
|
*
|
|
26
29
|
* Usage with React Navigation:
|
|
27
30
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useChatRoomFocus.d.ts","sourceRoot":"","sources":["../../../src/hooks/useChatRoomFocus.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useChatRoomFocus.d.ts","sourceRoot":"","sources":["../../../src/hooks/useChatRoomFocus.ts"],"names":[],"mappings":"AAWA,UAAU,uBAAuB;IAC/B,wEAAwE;IACxE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACnC;;;;;;;OAOG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,gBAAgB,GAAI,yBAG9B,uBAAuB,SA4CzB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useChatWrapperInit.d.ts","sourceRoot":"","sources":["../../../src/hooks/useChatWrapperInit.ts"],"names":[],"mappings":"AAAA,cAAc;AAId,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,UAAU,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"useChatWrapperInit.d.ts","sourceRoot":"","sources":["../../../src/hooks/useChatWrapperInit.ts"],"names":[],"mappings":"AAAA,cAAc;AAId,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAgBlD,UAAU,uBAAuB;IAC/B,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACnC,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,UAAU,wBAAwB;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACzD,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5D,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC;IACnE,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,QAAA,MAAM,kBAAkB,GAAI,uCAIzB,uBAAuB,KAAG,wBAqP5B,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stanzaHandlers.d.ts","sourceRoot":"","sources":["../../../src/networking/stanzaHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AA6B9B,QAAA,MAAM,iBAAiB,GAAU,QAAQ,OAAO,oDAgH/C,CAAC;AAEF,QAAA,MAAM,eAAe,GAAU,QAAQ,OAAO,kBAiB7C,CAAC;AAEF,QAAA,MAAM,aAAa,GAAU,QAAQ,OAAO,kBAiB3C,CAAC;AAEF,QAAA,MAAM,gBAAgB,GAAU,QAAQ,GAAG,
|
|
1
|
+
{"version":3,"file":"stanzaHandlers.d.ts","sourceRoot":"","sources":["../../../src/networking/stanzaHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AA6B9B,QAAA,MAAM,iBAAiB,GAAU,QAAQ,OAAO,oDAgH/C,CAAC;AAEF,QAAA,MAAM,eAAe,GAAU,QAAQ,OAAO,kBAiB7C,CAAC;AAEF,QAAA,MAAM,aAAa,GAAU,QAAQ,OAAO,kBAiB3C,CAAC;AAEF,QAAA,MAAM,gBAAgB,GAAU,QAAQ,GAAG,kBAkG1C,CAAC;AAEF,QAAA,MAAM,eAAe,GAAU,QAAQ,OAAO,EAAE,aAAa,MAAM,kBA8ClE,CAAC;AAEF,QAAA,MAAM,gBAAgB,GAAI,QAAQ,OAAO,GAAG,GAAG,SAU9C,CAAC;AAEF,QAAA,MAAM,YAAY,GAAU,QAAQ,OAAO,EAAE,QAAQ,GAAG,kBAoBvD,CAAC;AAEF,QAAA,MAAM,YAAY,GAAI,QAAQ,OAAO,SAgEpC,CAAC;AAEF,QAAA,MAAM,aAAa,GAAI,QAAQ,OAAO,SAGrC,CAAC;AAEF,QAAA,MAAM,uBAAuB,GAAI,QAAQ,OAAO,EAAE,OAAO,GAAG,SAG3D,CAAC;AAEF,QAAA,MAAM,gBAAgB,GAAI,QAAQ,OAAO,EAAE,MAAM,GAAG,SAGnD,CAAC;AAEF,QAAA,MAAM,cAAc,GAAI,QAAQ,OAAO,EAAE,MAAM,GAAG,SAsDjD,CAAC;AAKF,QAAA,MAAM,cAAc,GAAI,SAAS,OAAO,EAAE,QAAQ,GAAG,SAAO,CAAC;AAC7D,QAAA,MAAM,iBAAiB,GAAI,SAAS,OAAO,SAAO,CAAC;AACnD,QAAA,MAAM,iBAAiB,GAAI,SAAS,OAAO,SAAO,CAAC;AACnD,QAAA,MAAM,YAAY,GAAI,SAAS,OAAO,SAAO,CAAC;AAE9C,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,GACb,CAAC"}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import { type ReadTimestampHeapState } from '../../helpers/getServerReadTimestamp';
|
|
2
|
+
import type { IMessage } from '../../types/types';
|
|
1
3
|
interface RoomLike {
|
|
2
4
|
jid: string;
|
|
3
5
|
lastViewedTimestamp?: number;
|
|
4
6
|
unreadMessages?: number;
|
|
7
|
+
messages?: IMessage[];
|
|
5
8
|
}
|
|
6
9
|
interface FlushOpts {
|
|
7
10
|
visibleRoomJID?: string | null;
|
|
8
11
|
visibleRoomTs?: number | null;
|
|
9
12
|
onlyIfNoUnread?: boolean;
|
|
13
|
+
heapState?: ReadTimestampHeapState;
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
12
16
|
* Merge in-memory lastViewedTimestamp values into the server-side
|
|
@@ -15,8 +19,11 @@ interface FlushOpts {
|
|
|
15
19
|
* each other's entries.
|
|
16
20
|
*
|
|
17
21
|
* Called on: tab change away from chat, AppState background/inactive,
|
|
18
|
-
* and (with onlyIfNoUnread=true) logout. ChatRoom's own unmount path
|
|
19
|
-
*
|
|
22
|
+
* and (with onlyIfNoUnread=true) logout. ChatRoom's own unmount path also
|
|
23
|
+
* goes through this (via flushLastViewedToPrivateStoreStanza) for the
|
|
24
|
+
* single-room case - actionSetTimestampToPrivateStore is a separate,
|
|
25
|
+
* unconditional low-level write primitive kept for direct/external use
|
|
26
|
+
* and is not part of this merge path.
|
|
20
27
|
*/
|
|
21
28
|
export declare function flushLastViewedToPrivateStore(client: any, rooms: Record<string, RoomLike> | null | undefined, opts?: FlushOpts): Promise<boolean>;
|
|
22
29
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flushLastViewedToPrivateStore.d.ts","sourceRoot":"","sources":["../../../../src/networking/xmpp/flushLastViewedToPrivateStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"flushLastViewedToPrivateStore.d.ts","sourceRoot":"","sources":["../../../../src/networking/xmpp/flushLastViewedToPrivateStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,sBAAsB,EAC5B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD,UAAU,QAAQ;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,UAAU,SAAS;IAKjB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAQ/B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAI9B,cAAc,CAAC,EAAE,OAAO,CAAC;IAMzB,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,GAAG,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,IAAI,GAAG,SAAS,EAClD,IAAI,GAAE,SAAc,GACnB,OAAO,CAAC,OAAO,CAAC,CAmFlB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xmppClient.d.ts","sourceRoot":"","sources":["../../../src/networking/xmppClient.ts"],"names":[],"mappings":"AAAA,OAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"xmppClient.d.ts","sourceRoot":"","sources":["../../../src/networking/xmppClient.ts"],"names":[],"mappings":"AAAA,OAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAoFvD,KAAK,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC;AAEtE,UAAU,cAAc;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAeD,MAAM,MAAM,uBAAuB,GAAG,MAAM,OAAO,CAAC;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,qBAAa,UAAU;IACrB,MAAM,EAAG,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,qBAAqB,CAAC;IACrC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAM;IAClB,OAAO,EAAE,MAAM,CAAM;IACrB,UAAU,EAAE,MAAM,CAAM;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAa;IAElE,QAAQ,SAAM;IACd,iBAAiB,SAAK;IACtB,oBAAoB,SAAK;IACzB,cAAc,SAAQ;IAItB,iBAAiB,SAAS;IAI1B,OAAO,CAAC,eAAe,CAAK;IAC5B,iBAAiB,UAAS;IAI1B,OAAO,CAAC,cAAc,CAA8C;IAMpE,OAAO,CAAC,YAAY,CAAS;IAK7B,aAAa,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IAC9C;;;;OAIG;IACH,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,mBAAmB,CAAwC;IACnE,OAAO,CAAC,0BAA0B,CAA8B;IAGhE,OAAO,CAAC,gBAAgB,CAA6B;IAGrD,cAAc,UAAS;IACvB,eAAe,UAAS;IACxB,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,iBAAiB,CAA4C;IACrE,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,oBAAoB,CAAO;IACnC,OAAO,CAAC,oBAAoB,CAAQ;IACpC,OAAO,CAAC,0BAA0B,CAAQ;IAI1C,WAAW;IAIX;;;;;;OAMG;IACH,sBAAsB,CAAC,QAAQ,EAAE,uBAAuB,GAAG,IAAI;IAI/D;yDACqD;IACrD,WAAW,CAAC,EAAE,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI;IAInC,2DAA2D;IAC3D,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;gBAMlD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,qBAAqB,GAAG,MAAM;IAgC/C,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKvC,kBAAkB,CAAC,QAAQ,EAAE,MAAM;IAInC;;;OAGG;IACH,oBAAoB,IAAI,OAAO;IAM/B,uEAAuE;IACvE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;IAKnD;;;;OAIG;IACG,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS/D;;;;OAIG;IACG,kBAAkB,CAAC,MAAM,EAAE;QAC/B,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,MAAM,CAAC,EAAE,aAAa,CAAC;KACxB,GAAG,OAAO,CAAC,GAAG,CAAC;IA8ChB,OAAO,CAAC,UAAU;IAalB,gBAAgB;IAqHhB,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,CAAa;IAC9B,OAAO,CAAC,OAAO,CAAC,CAAuB;IACvC,OAAO,CAAC,QAAQ,CAAC,CAAwB;IAEzC,oBAAoB;IAkGpB,oBAAoB;IAepB;;;OAGG;IACH,aAAa,CAAC,SAAS,GAAE,MAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBvD,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,iBAAiB;IA4BjB;;;;;OAKG;IACH,cAAc;IAkBd,OAAO,CAAC,gBAAgB,CAAiC;IAEzD,iBAAiB,CAAC,SAAS,GAAE,MAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAgCvD,iBAAiB,CAAC,SAAS,GAAE,MAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1D,SAAS;IA8Ef;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IA0B9B,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,oBAAoB,CAAS;YAEvB,iBAAiB;IAuBzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAY9B,4EAA4E;IAC5E,MAAM,IAAI,IAAI;IAOd,IAAI,WAAW,IAAI,OAAO,CAEzB;IAEK,UAAU,CAAC,OAAO,CAAC,EAAE;QAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpE,KAAK;IAgCX,cAAc,sBAEZ;IAII,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM;IAIhE,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAIzD,kBAAkB,GAAI,SAAS,MAAM,UAEnC;IAEF,oBAAoB,GAAI,SAAS,MAAM,UAErC;IAEF,gBAAgB,GACd,SAAS,MAAM,EACf,KAAK,MAAM,EACX,SAAS,MAAM,EACf,KAAK,MAAM,EACX,UAAU,cAAc,kBAYxB;IAEF,2BAA2B,CAAC,OAAO,EAAE,MAAM;IAI3C,kBAAkB,GAChB,SAAS,MAAM,EACf,eAAe,MAAM,EACrB,MAAM,MAAM,EACZ,iBAAiB,MAAM,UAGvB;IAEF,iBAAiB,GAAI,SAAS,MAAM,UAElC;IAEF,oBAAoB,GAAI,SAAS,MAAM,UAErC;IAEF;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAIrB,wEAAwE;IACxE,iBAAiB;IAKjB,WAAW,GACT,SAAS,MAAM,EACf,WAAW,MAAM,EACjB,UAAU,MAAM,EAChB,OAAO,MAAM,EACb,eAAe,MAAM,EACrB,aAAa,MAAM,EACnB,oBAAoB,MAAM,EAC1B,UAAU,OAAO,EACjB,gBAAgB,OAAO,EACvB,cAAc,MAAM,EACpB,WAAW,MAAM,KAChB,OAAO,CA0CR;IAEF,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAI/C,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAI3D,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAIxE,iCAAiC,yBAyD/B;IAEI,sCAAsC,CAC1C,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,MAAM,EAAE;IAgBZ,mCAAmC,CACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,EAC7C,IAAI,GAAE;QACJ,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;KACrB;IAiBR,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,MAAM;IA8BpE,cAAc,CAAC,aAAa,EAAE,MAAM;IAI9B,uBAAuB,CAC3B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,MAAM,CAAC;IAMlB,yBAAyB,CACvB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EAAE,EACxB,eAAe,CAAC,EAAE,MAAM;IAY1B,qCAAqC,CACnC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,iBAAiB,CAAC,EAAE,MAAM,EAC1B,OAAO,CAAC,EAAE,OAAO,EACjB,aAAa,CAAC,EAAE,OAAO,EACvB,WAAW,CAAC,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO;CA4CX;AAED,eAAe,UAAU,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/roomStore/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/roomStore/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAWvC,QAAA,MAAM,WAAW;;;;;;;;;;GAQf,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAmCvD,eAAO,MAAM,KAAK;;;;;;;;;;;;oCAEgC,CAAC;AAEnD,MAAM,MAAM,WAAW,GAAG,OAAO,KAAK,CAAC,QAAQ,CAAC;AAEhD,eAAO,MAAM,aAAa,GAAI,OAAO,SAAS,KAAG,KAAK,GAAG,IAKxD,CAAC;AAIF,eAAO,MAAM,cAAc,eA4CpB,CAAC"}
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import { Middleware } from '@reduxjs/toolkit';
|
|
2
|
-
import { IRoom, User } from '../types/types';
|
|
2
|
+
import { IMessage, IRoom, User } from '../types/types';
|
|
3
|
+
import type { FailedMessagePayload } from './roomHeapSlice';
|
|
3
4
|
export declare const MESSAGE_LIMIT = 100;
|
|
5
|
+
export declare const MAX_PERSISTED_FAILED_MESSAGES = 200;
|
|
4
6
|
interface PersistedChatState {
|
|
5
7
|
user: User;
|
|
6
8
|
}
|
|
7
9
|
interface PersistedRoomsState {
|
|
8
10
|
rooms: Record<string, IRoom>;
|
|
9
11
|
}
|
|
12
|
+
interface PersistedHeapState {
|
|
13
|
+
failedMessages: Record<string, FailedMessagePayload>;
|
|
14
|
+
}
|
|
15
|
+
export declare function reconstructFailedMessagePayload(message: IMessage, roomJID: string): FailedMessagePayload | null;
|
|
16
|
+
/**
|
|
17
|
+
* Scan every persisted room for messages still `pending: true` that
|
|
18
|
+
* DON'T already have a restored failure entry (those are handled by the
|
|
19
|
+
* caller separately, from the persisted `failedMessages` map), and
|
|
20
|
+
* synthesize a retry-able FailedMessagePayload for each. Pure function:
|
|
21
|
+
* the caller (roomStore/index.ts) is responsible for dispatching
|
|
22
|
+
* `markMessageFailed` for the results.
|
|
23
|
+
*/
|
|
24
|
+
export declare function computeBootTimeFailures(rooms: Record<string, IRoom> | null | undefined, alreadyFailed: Record<string, FailedMessagePayload> | null | undefined): FailedMessagePayload[];
|
|
10
25
|
/**
|
|
11
26
|
* Middleware that debounces writes of the persisted slices to AsyncStorage.
|
|
12
27
|
* Only triggers on actions that actually mutate persisted state.
|
|
@@ -20,11 +35,13 @@ export declare const persistenceMiddleware: Middleware;
|
|
|
20
35
|
export declare function readPersistedState(): Promise<{
|
|
21
36
|
chat: PersistedChatState | null;
|
|
22
37
|
rooms: PersistedRoomsState | null;
|
|
38
|
+
heap: PersistedHeapState | null;
|
|
23
39
|
}>;
|
|
24
40
|
export declare function clearPersistedState(): Promise<void>;
|
|
25
41
|
export declare const PERSIST_KEYS: {
|
|
26
42
|
KEY_CHAT: string;
|
|
27
43
|
KEY_ROOMS: string;
|
|
44
|
+
KEY_HEAP: string;
|
|
28
45
|
};
|
|
29
46
|
export {};
|
|
30
47
|
//# sourceMappingURL=persistence.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../../src/roomStore/persistence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../../src/roomStore/persistence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AA8B5D,eAAO,MAAM,aAAa,MAAM,CAAC;AAMjC,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD,UAAU,kBAAkB;IAC1B,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,UAAU,mBAAmB;IAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAE9B;AAED,UAAU,kBAAkB;IAC1B,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;CAKtD;AA4OD,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,MAAM,GACd,oBAAoB,GAAG,IAAI,CAkC7B;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,IAAI,GAAG,SAAS,EAC/C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,GAAG,IAAI,GAAG,SAAS,GACrE,oBAAoB,EAAE,CAaxB;AAID;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,UAkDnC,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC;IAClD,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAClC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACjC,CAAC,CA6BD;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAMzD;AAED,eAAO,MAAM,YAAY;;;;CAAoC,CAAC"}
|
|
@@ -87,7 +87,19 @@ declare const reducers: {
|
|
|
87
87
|
*
|
|
88
88
|
* Monotonic: a marker only ever moves a baseline FORWARD, so a stale
|
|
89
89
|
* server value can't resurrect already-read messages and a more-recent
|
|
90
|
-
* local read (tab blur stamping
|
|
90
|
+
* local read (tab blur stamping the server read timestamp) always wins.
|
|
91
|
+
*
|
|
92
|
+
* Bug #38 self-heal: forward-only is correct for a SANE existing
|
|
93
|
+
* value, but a device whose clock was ahead may have already stamped
|
|
94
|
+
* a marker in the future (either persisted locally from a previous
|
|
95
|
+
* session, or fetched from the server before this fix existed). To
|
|
96
|
+
* "only ever move forward" from a corrupt future value is to never
|
|
97
|
+
* move at all - the room would stay stuck at `unreadMessages: 0`
|
|
98
|
+
* forever. When the room is loaded, detect a marker that sits well
|
|
99
|
+
* past the newest message we actually know it has and waive the
|
|
100
|
+
* forward-only check for that one comparison, so the incoming
|
|
101
|
+
* (already-clamped, see xmppClient.getChatsPrivateStoreRequestStanza)
|
|
102
|
+
* value can correct it even though it's numerically smaller.
|
|
91
103
|
*/
|
|
92
104
|
applyPrivateStoreMarkers: (state: WritableDraft<RoomMessagesState>, action: PayloadAction<Record<string, number>>) => void;
|
|
93
105
|
setRoomRole: (state: WritableDraft<RoomMessagesState>, action: PayloadAction<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roomsSlice.d.ts","sourceRoot":"","sources":["../../../src/roomStore/roomsSlice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,aAAa,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAElF,OAAO,KAAK,UAAU,MAAM,0BAA0B,CAAC;AAWvD,eAAO,MAAM,qBAAqB,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"roomsSlice.d.ts","sourceRoot":"","sources":["../../../src/roomStore/roomsSlice.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -28,6 +28,7 @@ import { IConfig } from '../../types/models/config.model';
|
|
|
28
28
|
import { useRoomInitialization } from '../../hooks/useRoomInitialization';
|
|
29
29
|
import { useRoomState } from '../../hooks/useRoomState';
|
|
30
30
|
import { useChatSettingState } from '../../hooks/useChatSettingState';
|
|
31
|
+
import { isOwnMessage } from '../../helpers/isOwnMessage';
|
|
31
32
|
import CustomTypingIndicator from '../styled/StyledInputComponents/CustomTypingIndicator';
|
|
32
33
|
// import {PanGestureHandler} from 'react-native-gesture-handler';
|
|
33
34
|
import { FlatList } from 'react-native';
|
|
@@ -46,6 +47,7 @@ import {
|
|
|
46
47
|
} from 'react-native-keyboard-controller';
|
|
47
48
|
import useComposing from '../../hooks/useComposing';
|
|
48
49
|
import { store } from '../../roomStore';
|
|
50
|
+
import { getServerReadTimestamp } from '../../helpers/getServerReadTimestamp';
|
|
49
51
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
50
52
|
import {
|
|
51
53
|
getInputDockPaddingBottom,
|
|
@@ -234,21 +236,30 @@ const ChatRoom: React.FC<ChatRoomProps> = React.memo(
|
|
|
234
236
|
dispatch(setVisibleRoom({ roomJID: activeRoomJID }));
|
|
235
237
|
setIsLoadingMore(false);
|
|
236
238
|
return () => {
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
239
|
+
const rooms = store.getState().rooms?.rooms;
|
|
240
|
+
const heapState = store.getState().roomHeapSlice;
|
|
241
|
+
// Fall back to the newest server-acked message, never the device
|
|
242
|
+
// clock: a fast device clock would write a future marker that the
|
|
243
|
+
// forward-only private-store merge could never correct (bug #38).
|
|
244
|
+
const timestamp =
|
|
245
|
+
readBoundaryRef.current ??
|
|
246
|
+
getServerReadTimestamp(rooms?.[activeRoomJID], heapState);
|
|
247
|
+
if (timestamp > 0) {
|
|
248
|
+
dispatch(
|
|
249
|
+
setLastViewedTimestamp({
|
|
250
|
+
chatJID: activeRoomJID,
|
|
251
|
+
timestamp,
|
|
252
|
+
}),
|
|
253
|
+
);
|
|
254
|
+
}
|
|
244
255
|
dispatch(clearVisibleRoom());
|
|
245
256
|
const liveClient = clientRef.current;
|
|
246
257
|
if (liveClient) {
|
|
247
258
|
liveClient
|
|
248
|
-
.flushLastViewedToPrivateStoreStanza(
|
|
259
|
+
.flushLastViewedToPrivateStoreStanza(rooms, {
|
|
249
260
|
visibleRoomJID: activeRoomJID,
|
|
250
261
|
// Carry the same boundary to the SERVER marker. Without
|
|
251
|
-
// this the flush defaults to
|
|
262
|
+
// this the flush defaults to "everything" for the visible
|
|
252
263
|
// room, so messages the user never scrolled down to come
|
|
253
264
|
// back as read on the next login — the local count was
|
|
254
265
|
// right but the server overrode it.
|
|
@@ -440,7 +451,7 @@ const ChatRoom: React.FC<ChatRoomProps> = React.memo(
|
|
|
440
451
|
CustomMessageComponent ? (
|
|
441
452
|
<CustomMessageComponent
|
|
442
453
|
message={decorated.message}
|
|
443
|
-
isUser={decorated.message
|
|
454
|
+
isUser={isOwnMessage(decorated.message, user)}
|
|
444
455
|
isReply={false}
|
|
445
456
|
/>
|
|
446
457
|
) : null
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
Text,
|
|
12
12
|
View,
|
|
13
13
|
} from 'react-native';
|
|
14
|
-
import {
|
|
14
|
+
import { isLikelyAudio } from '../../helpers/mimeToExtension';
|
|
15
|
+
import { getDisplayFileName } from '../../helpers/getDisplayFileName';
|
|
15
16
|
import { FileIcon, PlayIcon } from '../../assets/icons';
|
|
16
17
|
import { defaultMediaDims } from '../../helpers/mediaDimensions';
|
|
17
18
|
import { useChatSettingState } from '../../hooks/useChatSettingState';
|
|
@@ -148,11 +149,11 @@ const MediaMessage: React.FC<MediaMessageProps> = ({
|
|
|
148
149
|
const messageText = appendFileToken(rawMessageText, fileToken);
|
|
149
150
|
|
|
150
151
|
if (mimeType) {
|
|
151
|
-
const displayName =
|
|
152
|
-
fileName: message?.fileName,
|
|
152
|
+
const displayName = getDisplayFileName({
|
|
153
153
|
originalName: (message as any)?.originalName,
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
fileName: message?.fileName,
|
|
155
|
+
location: rawLocation || rawMessageText,
|
|
156
|
+
mimetype: mimeType,
|
|
156
157
|
});
|
|
157
158
|
const isAudioPayload = isLikelyAudio(
|
|
158
159
|
mimeType,
|
|
@@ -180,6 +181,7 @@ const MediaMessage: React.FC<MediaMessageProps> = ({
|
|
|
180
181
|
return (
|
|
181
182
|
<CustomMessageImage
|
|
182
183
|
fileName={displayName}
|
|
184
|
+
originalName={(message as any)?.originalName}
|
|
183
185
|
// Full-size original: opened by the preview modal.
|
|
184
186
|
fileURL={location || messageText || ''}
|
|
185
187
|
// ...and the thumbnail the bubble actually renders, which is
|
|
@@ -192,6 +194,7 @@ const MediaMessage: React.FC<MediaMessageProps> = ({
|
|
|
192
194
|
return (
|
|
193
195
|
<CustomMessageVideo
|
|
194
196
|
fileName={displayName}
|
|
197
|
+
originalName={(message as any)?.originalName}
|
|
195
198
|
fileURL={location || ''}
|
|
196
199
|
// `messageText` is the message's locationPreview — the poster
|
|
197
200
|
// frame the backend generated for this video. Handing it over
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { FC, Fragment, useMemo } from 'react';
|
|
2
2
|
import { IConfig, IMessage } from '../../types/types';
|
|
3
|
+
import { isOwnMessage } from '../../helpers/isOwnMessage';
|
|
3
4
|
import DateLabel from '../styled/DateLabel';
|
|
4
5
|
import SystemMessage from './SystemMessage';
|
|
5
6
|
import NewMessageLabel from '../styled/NewMessageLabel';
|
|
@@ -46,7 +47,10 @@ export const MessageContainer: FC<MessageContainerProps> = ({
|
|
|
46
47
|
isReply,
|
|
47
48
|
className,
|
|
48
49
|
}) => {
|
|
49
|
-
|
|
50
|
+
// Bug #41: `message.user.id === walletAddress` used to treat two
|
|
51
|
+
// empty/undefined ids as a match, briefly rendering catch-up messages
|
|
52
|
+
// from other users as our own. isOwnMessage() never does that.
|
|
53
|
+
const isUser = isOwnMessage(message, walletAddress);
|
|
50
54
|
|
|
51
55
|
const messageDate = new Date(message.date);
|
|
52
56
|
|
|
@@ -29,6 +29,7 @@ import { ArowDownIcon } from '../../assets/icons';
|
|
|
29
29
|
import CustomTypingIndicator from '../styled/StyledInputComponents/CustomTypingIndicator';
|
|
30
30
|
import { getIconColor } from '../../helpers/getIconColor';
|
|
31
31
|
import { getChatBackgroundColor } from '../../helpers/getChatBackground';
|
|
32
|
+
import { isOwnMessage } from '../../helpers/isOwnMessage';
|
|
32
33
|
|
|
33
34
|
interface MessageListProps<TMessage extends IMessage> {
|
|
34
35
|
CustomMessage?: React.ComponentType<{
|
|
@@ -438,7 +439,7 @@ const MessageList = <TMessage extends IMessage>({
|
|
|
438
439
|
{CustomMessage && (
|
|
439
440
|
<CustomMessage
|
|
440
441
|
message={activeMessage}
|
|
441
|
-
isUser={activeMessage
|
|
442
|
+
isUser={isOwnMessage(activeMessage, user)}
|
|
442
443
|
isReply={isReply}
|
|
443
444
|
/>
|
|
444
445
|
)}
|
|
@@ -31,7 +31,12 @@ import { useToast } from '../../../context/ToastContext';
|
|
|
31
31
|
import PdfViewer from './PdfView';
|
|
32
32
|
import DocumentViewer from './DocumentViewer';
|
|
33
33
|
import AudioMessage from '../../styled/AudioMessage';
|
|
34
|
-
import {
|
|
34
|
+
import { isLikelyAudio } from '../../../helpers/mimeToExtension';
|
|
35
|
+
import {
|
|
36
|
+
getDisplayFileName,
|
|
37
|
+
getUniqueFileName,
|
|
38
|
+
sanitizeFileNameForPath,
|
|
39
|
+
} from '../../../helpers/getDisplayFileName';
|
|
35
40
|
|
|
36
41
|
// MIME types Google's gview embed renders reliably. Everything else
|
|
37
42
|
// falls through to the info-card so the user can still download.
|
|
@@ -128,6 +133,19 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
128
133
|
|
|
129
134
|
if (!activeFile) {return null;}
|
|
130
135
|
|
|
136
|
+
// Bug #40: prefer the sender's original name over the server's stored
|
|
137
|
+
// hash name everywhere this modal shows or saves/shares the file.
|
|
138
|
+
// Sanitized so it's safe to use as an actual path segment (strips path
|
|
139
|
+
// separators/control chars) when writing to disk below.
|
|
140
|
+
const displayFileName = sanitizeFileNameForPath(
|
|
141
|
+
getDisplayFileName({
|
|
142
|
+
originalName: activeFile.originalName,
|
|
143
|
+
fileName: activeFile.fileName,
|
|
144
|
+
location: activeFile.fileURL,
|
|
145
|
+
mimetype: activeFile.mimetype,
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
|
|
131
149
|
const requestStoragePermission = async () => {
|
|
132
150
|
const MediaLibrary = getMediaLibrary();
|
|
133
151
|
if (!MediaLibrary?.requestPermissionsAsync) {return false;}
|
|
@@ -151,12 +169,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
151
169
|
}
|
|
152
170
|
|
|
153
171
|
try {
|
|
154
|
-
const
|
|
155
|
-
activeFile.fileName,
|
|
156
|
-
activeFile.mimetype
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
const filePath = FileSystem.cacheDirectory + fileName;
|
|
172
|
+
const filePath = FileSystem.cacheDirectory + displayFileName;
|
|
160
173
|
const download = await FileSystem.downloadAsync(withFileToken(activeFile.fileURL), filePath);
|
|
161
174
|
|
|
162
175
|
if (download.status === 200) {
|
|
@@ -171,17 +184,39 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
171
184
|
Alert.alert('Error', 'Failed to save the file.');
|
|
172
185
|
}
|
|
173
186
|
} catch (err) {
|
|
174
|
-
Alert.alert('Error', `Failed to save the file: ${
|
|
187
|
+
Alert.alert('Error', `Failed to save the file: ${displayFileName}`);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Best-effort existing-name lookup for the Android SAF branch below: SAF
|
|
192
|
+
// hands back content URIs, not plain names, so this decodes each one and
|
|
193
|
+
// takes its last path segment. If the provider's URI shape doesn't match
|
|
194
|
+
// what we expect, we fail open (empty set) rather than block the save -
|
|
195
|
+
// worst case we skip de-duplication, we never crash the save flow.
|
|
196
|
+
const listSafDirectoryNames = async (
|
|
197
|
+
saf: any,
|
|
198
|
+
dirUri: string
|
|
199
|
+
): Promise<Set<string>> => {
|
|
200
|
+
try {
|
|
201
|
+
const uris: string[] = await saf.readDirectoryAsync(dirUri);
|
|
202
|
+
const names = uris.map((uri) => {
|
|
203
|
+
try {
|
|
204
|
+
const decoded = decodeURIComponent(uri);
|
|
205
|
+
const last = decoded.split('/').pop() || '';
|
|
206
|
+
return last.includes(':') ? last.split(':').pop() || '' : last;
|
|
207
|
+
} catch {
|
|
208
|
+
return '';
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
return new Set(names.filter(Boolean));
|
|
212
|
+
} catch {
|
|
213
|
+
return new Set<string>();
|
|
175
214
|
}
|
|
176
215
|
};
|
|
177
216
|
|
|
178
217
|
const saveFileToDownloads = async () => {
|
|
179
218
|
try {
|
|
180
|
-
const
|
|
181
|
-
activeFile.fileName,
|
|
182
|
-
activeFile.mimetype
|
|
183
|
-
);
|
|
184
|
-
const filePath = FileSystem.cacheDirectory + fileName;
|
|
219
|
+
const filePath = FileSystem.cacheDirectory + displayFileName;
|
|
185
220
|
const download = await FileSystem.downloadAsync(withFileToken(activeFile.fileURL), filePath);
|
|
186
221
|
|
|
187
222
|
if (download.status !== 200) {
|
|
@@ -205,7 +240,16 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
205
240
|
safDirUriRef.current = dirUri;
|
|
206
241
|
}
|
|
207
242
|
|
|
208
|
-
|
|
243
|
+
// Avoid silently overwriting a different attachment that happens
|
|
244
|
+
// to share the same display name (common now that we prefer the
|
|
245
|
+
// human-picked name over the unique server hash - bug #40) by
|
|
246
|
+
// suffixing " (1)", " (2)", ... on a collision.
|
|
247
|
+
const existingNames = await listSafDirectoryNames(saf, dirUri as string);
|
|
248
|
+
const uniqueName = await getUniqueFileName(
|
|
249
|
+
displayFileName,
|
|
250
|
+
async (candidate) => existingNames.has(candidate)
|
|
251
|
+
);
|
|
252
|
+
const baseName = uniqueName.replace(/\.[^/.]+$/, '');
|
|
209
253
|
const mime = activeFile.mimetype || 'application/octet-stream';
|
|
210
254
|
const base64 = await FileSystem.readAsStringAsync(download.uri, {
|
|
211
255
|
encoding: FileSystem.EncodingType.Base64,
|
|
@@ -232,7 +276,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
232
276
|
// user may cancel), so we don't show a "Save successful" toast here.
|
|
233
277
|
await Share.share({
|
|
234
278
|
url: download.uri,
|
|
235
|
-
title:
|
|
279
|
+
title: displayFileName,
|
|
236
280
|
});
|
|
237
281
|
} catch (err) {
|
|
238
282
|
console.error('Error saving file:', err);
|
|
@@ -279,7 +323,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
279
323
|
'https://as2.ftcdn.net/v2/jpg/02/51/95/53/1000_F_251955356_FAQH0U1y1TZw3ZcdPGybwUkH90a3VAhb.jpg',
|
|
280
324
|
}}
|
|
281
325
|
resizeMode="contain"
|
|
282
|
-
accessibilityLabel={
|
|
326
|
+
accessibilityLabel={displayFileName}
|
|
283
327
|
/>
|
|
284
328
|
);
|
|
285
329
|
case activeFile.mimetype.startsWith('video/'):
|
|
@@ -333,10 +377,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
333
377
|
}}
|
|
334
378
|
>
|
|
335
379
|
<Text style={{ fontSize: 16, fontWeight: '600' }}>
|
|
336
|
-
{
|
|
337
|
-
activeFile.originalName || activeFile.fileName,
|
|
338
|
-
activeFile.mimetype
|
|
339
|
-
)}
|
|
380
|
+
{displayFileName}
|
|
340
381
|
</Text>
|
|
341
382
|
<AudioMessage
|
|
342
383
|
src={withFileToken(activeFile.fileURL)}
|
|
@@ -351,10 +392,6 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
351
392
|
case activeFile.mimetype === 'application/pdf':
|
|
352
393
|
return <PdfViewer pdfUrl={withFileToken(activeFile.fileURL)} />;
|
|
353
394
|
default: {
|
|
354
|
-
const displayName = ensureFilenameHasExtension(
|
|
355
|
-
activeFile.fileName,
|
|
356
|
-
activeFile.mimetype
|
|
357
|
-
);
|
|
358
395
|
// Office docs (.docx / .xlsx / .pptx / .doc / .xls / .ppt /
|
|
359
396
|
// .txt / .csv / .rtf) → render inline via Google's gview embed
|
|
360
397
|
// — fixes the "blank preview" complaint for docs (bug #9).
|
|
@@ -362,7 +399,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
362
399
|
return (
|
|
363
400
|
<DocumentViewer
|
|
364
401
|
url={activeFile.fileURL}
|
|
365
|
-
fileName={
|
|
402
|
+
fileName={displayFileName}
|
|
366
403
|
/>
|
|
367
404
|
);
|
|
368
405
|
}
|
|
@@ -379,7 +416,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
379
416
|
}}
|
|
380
417
|
>
|
|
381
418
|
<Text style={{ fontSize: 16, fontWeight: '600' }}>
|
|
382
|
-
{
|
|
419
|
+
{displayFileName}
|
|
383
420
|
</Text>
|
|
384
421
|
<Text style={{ color: '#666' }}>
|
|
385
422
|
{activeFile.mimetype || 'unknown type'}
|
|
@@ -14,9 +14,13 @@ import {
|
|
|
14
14
|
isSecureFileUrl,
|
|
15
15
|
requestFileTokenRecovery,
|
|
16
16
|
} from '../../../helpers/secureFileUrl';
|
|
17
|
+
import { getDisplayFileName } from '../../../helpers/getDisplayFileName';
|
|
17
18
|
|
|
18
19
|
interface LastMessageFileProps
|
|
19
|
-
extends Pick<
|
|
20
|
+
extends Pick<
|
|
21
|
+
LastMessage,
|
|
22
|
+
'user' | 'originalName' | 'fileName' | 'mimetype' | 'locationPreview'
|
|
23
|
+
> {}
|
|
20
24
|
|
|
21
25
|
const fallbackImage =
|
|
22
26
|
'https://as2.ftcdn.net/v2/jpg/02/51/95/53/1000_F_251955356_FAQH0U1y1TZw3ZcdPGybwUkH90a3VAhb.jpg';
|
|
@@ -24,10 +28,18 @@ const fallbackImage =
|
|
|
24
28
|
const LastMessageFile: FC<LastMessageFileProps> = ({
|
|
25
29
|
user,
|
|
26
30
|
originalName,
|
|
31
|
+
fileName,
|
|
32
|
+
mimetype,
|
|
27
33
|
locationPreview,
|
|
28
34
|
}) => {
|
|
29
35
|
const [imgSrc, setImgSrc] = useState(locationPreview);
|
|
30
36
|
const fileToken = useFileToken();
|
|
37
|
+
// Bug #40: prefer the sender's original name over the stored hash
|
|
38
|
+
// name; only fall back to the generic "file" label when neither is
|
|
39
|
+
// present at all (avoids a synthetic "media_<timestamp>.bin" here).
|
|
40
|
+
const displayName = originalName || fileName
|
|
41
|
+
? getDisplayFileName({ originalName, fileName, mimetype })
|
|
42
|
+
: 'file';
|
|
31
43
|
|
|
32
44
|
return (
|
|
33
45
|
<LastRoomMessageContainer>
|
|
@@ -57,7 +69,7 @@ const LastMessageFile: FC<LastMessageFileProps> = ({
|
|
|
57
69
|
) : (
|
|
58
70
|
<FileIcon style={{ width: '20px', height: '20px' }} />
|
|
59
71
|
)}
|
|
60
|
-
<LastRoomMessageText>{
|
|
72
|
+
<LastRoomMessageText>{displayName}</LastRoomMessageText>
|
|
61
73
|
</View>
|
|
62
74
|
</LastRoomMessageContainer>
|
|
63
75
|
);
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
ShadeWrapper,
|
|
12
12
|
} from './StyledRoomComponents';
|
|
13
13
|
import { View } from 'react-native';
|
|
14
|
+
import { getDisplayFileName } from '../../../helpers/getDisplayFileName';
|
|
14
15
|
|
|
15
16
|
const PhotoContainer = styled.View`
|
|
16
17
|
position: relative;
|
|
@@ -25,14 +26,25 @@ const PhotoContainer = styled.View`
|
|
|
25
26
|
`;
|
|
26
27
|
|
|
27
28
|
interface LastMessagePhotoProps
|
|
28
|
-
extends Pick<
|
|
29
|
+
extends Pick<
|
|
30
|
+
LastMessage,
|
|
31
|
+
'user' | 'originalName' | 'fileName' | 'mimetype' | 'locationPreview'
|
|
32
|
+
> {}
|
|
29
33
|
|
|
30
34
|
const LastMessagePhoto: FC<LastMessagePhotoProps> = ({
|
|
31
35
|
user,
|
|
32
36
|
locationPreview,
|
|
33
37
|
originalName,
|
|
38
|
+
fileName,
|
|
39
|
+
mimetype,
|
|
34
40
|
}) => {
|
|
35
41
|
const fileToken = useFileToken();
|
|
42
|
+
// Bug #40: prefer the sender's original name over the stored hash
|
|
43
|
+
// name; only fall back to the generic "file" label when neither is
|
|
44
|
+
// present at all (avoids a synthetic "media_<timestamp>.bin" here).
|
|
45
|
+
const displayName = originalName || fileName
|
|
46
|
+
? getDisplayFileName({ originalName, fileName, mimetype })
|
|
47
|
+
: 'file';
|
|
36
48
|
return (
|
|
37
49
|
<LastRoomMessageContainer>
|
|
38
50
|
<LastRoomMessageName>{user?.name || ''}:</LastRoomMessageName>
|
|
@@ -48,7 +60,7 @@ const LastMessagePhoto: FC<LastMessagePhotoProps> = ({
|
|
|
48
60
|
<LastMessageImg src={appendFileToken(locationPreview, fileToken)} />
|
|
49
61
|
</ShadeWrapper>
|
|
50
62
|
</PhotoContainer>
|
|
51
|
-
<LastRoomMessageText>{
|
|
63
|
+
<LastRoomMessageText>{displayName}</LastRoomMessageText>
|
|
52
64
|
</View>
|
|
53
65
|
</LastRoomMessageContainer>
|
|
54
66
|
);
|