@droppii-org/chat-sdk 0.6.37 → 0.6.39

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 (66) hide show
  1. package/dist/assets/images/stickerInvalid.png +0 -0
  2. package/dist/components/conversation-detail/ConversationDetailInput.d.ts +3 -1
  3. package/dist/components/conversation-detail/ConversationDetailInput.d.ts.map +1 -1
  4. package/dist/components/conversation-detail/ConversationDetailInput.js +32 -5
  5. package/dist/components/conversation-detail/TypingIndicatorBar.d.ts +10 -0
  6. package/dist/components/conversation-detail/TypingIndicatorBar.d.ts.map +1 -0
  7. package/dist/components/conversation-detail/TypingIndicatorBar.js +24 -0
  8. package/dist/components/conversation-detail/sticker-picker/StickerCategoryTabs.d.ts +9 -0
  9. package/dist/components/conversation-detail/sticker-picker/StickerCategoryTabs.d.ts.map +1 -0
  10. package/dist/components/conversation-detail/sticker-picker/StickerCategoryTabs.js +38 -0
  11. package/dist/components/conversation-detail/sticker-picker/StickerPicker.d.ts +7 -0
  12. package/dist/components/conversation-detail/sticker-picker/StickerPicker.d.ts.map +1 -0
  13. package/dist/components/conversation-detail/sticker-picker/StickerPicker.js +113 -0
  14. package/dist/components/conversation-detail/sticker-picker/StickerSection.d.ts +12 -0
  15. package/dist/components/conversation-detail/sticker-picker/StickerSection.d.ts.map +1 -0
  16. package/dist/components/conversation-detail/sticker-picker/StickerSection.js +46 -0
  17. package/dist/components/conversation-detail/sticker-picker/constants.d.ts +8 -0
  18. package/dist/components/conversation-detail/sticker-picker/constants.d.ts.map +1 -0
  19. package/dist/components/conversation-detail/sticker-picker/constants.js +1 -0
  20. package/dist/components/message/item/StickerMessage.d.ts.map +1 -1
  21. package/dist/components/message/item/StickerMessage.js +3 -5
  22. package/dist/components/message/reply/ReplyPreviewMedia.d.ts.map +1 -1
  23. package/dist/components/message/reply/ReplyPreviewMedia.js +8 -2
  24. package/dist/constants/images.d.ts +1 -0
  25. package/dist/constants/images.d.ts.map +1 -1
  26. package/dist/constants/images.js +2 -0
  27. package/dist/context/ChatContext.d.ts.map +1 -1
  28. package/dist/context/ChatContext.js +1 -1
  29. package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
  30. package/dist/hooks/global/useGlobalEvent.js +10 -1
  31. package/dist/hooks/message/useReportTypingState.d.ts +10 -0
  32. package/dist/hooks/message/useReportTypingState.d.ts.map +1 -0
  33. package/dist/hooks/message/useReportTypingState.js +73 -0
  34. package/dist/hooks/message/useSendMessage.d.ts +7 -1
  35. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  36. package/dist/hooks/message/useSendMessage.js +45 -2
  37. package/dist/hooks/message/useTypingStatus.d.ts +11 -0
  38. package/dist/hooks/message/useTypingStatus.d.ts.map +1 -0
  39. package/dist/hooks/message/useTypingStatus.js +120 -0
  40. package/dist/hooks/sticker/useStickers.d.ts +5 -0
  41. package/dist/hooks/sticker/useStickers.d.ts.map +1 -0
  42. package/dist/hooks/sticker/useStickers.js +39 -0
  43. package/dist/hooks/user/useAuth.d.ts.map +1 -1
  44. package/dist/hooks/user/useAuth.js +2 -0
  45. package/dist/locales/vi/conversation-inbox.json +11 -0
  46. package/dist/screens/conversation-detail/index.d.ts.map +1 -1
  47. package/dist/screens/conversation-detail/index.js +2 -1
  48. package/dist/screens/conversation-inbox/index.d.ts +2 -0
  49. package/dist/screens/conversation-inbox/index.d.ts.map +1 -1
  50. package/dist/screens/conversation-inbox/index.js +2 -2
  51. package/dist/services/query.d.ts +3 -0
  52. package/dist/services/query.d.ts.map +1 -1
  53. package/dist/services/query.js +3 -0
  54. package/dist/services/routes.d.ts +3 -0
  55. package/dist/services/routes.d.ts.map +1 -1
  56. package/dist/services/routes.js +3 -0
  57. package/dist/store/typing.d.ts +13 -0
  58. package/dist/store/typing.d.ts.map +1 -0
  59. package/dist/store/typing.js +63 -0
  60. package/dist/styles/global.css +1 -1
  61. package/dist/types/chat.d.ts +1 -0
  62. package/dist/types/chat.d.ts.map +1 -1
  63. package/dist/types/sticker.d.ts +20 -0
  64. package/dist/types/sticker.d.ts.map +1 -0
  65. package/dist/types/sticker.js +1 -0
  66. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useReportTypingState.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useReportTypingState.ts"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,gBAAgB,MAAM;oCAoCvC,MAAM;;CA6BzB,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { useCallback, useEffect, useRef } from "react";
2
+ import { DChatSDK } from "../../constants/sdk";
3
+ // Keep-alive rate while typing; receivers drop a typer after 3s of silence.
4
+ const TYPING_REPORT_INTERVAL_MS = 2000;
5
+ // Business rule: no activity for 3s means the participant stopped typing.
6
+ const TYPING_IDLE_TIMEOUT_MS = 3000;
7
+ // Core forwards one "typing" per window and drops a "stop" once it lapses;
8
+ // assuming 2s is safe with the old 10s core too, it only adds a deduped resend.
9
+ const CORE_SEND_WINDOW_MS = 2000;
10
+ const changeInputState = (conversationId, focus) => {
11
+ var _a;
12
+ return ((_a = DChatSDK === null || DChatSDK === void 0 ? void 0 : DChatSDK.changeInputStates({ conversationID: conversationId, focus })) !== null && _a !== void 0 ? _a : Promise.resolve()).catch((error) => console.error("changeInputStates", error));
13
+ };
14
+ /**
15
+ * Reports the viewer's own composer activity for `conversationId` so other
16
+ * participants see a typing indicator. `reportActivity` on every edit,
17
+ * `stopTyping` when the composer is cleared, blurred or the message is sent.
18
+ */
19
+ export const useReportTypingState = (conversationId) => {
20
+ const isTypingRef = useRef(false);
21
+ // When core last actually forwarded "typing" (not deduped by it).
22
+ const coreSentAtRef = useRef(0);
23
+ const lastReportAtRef = useRef(0);
24
+ const idleTimerRef = useRef();
25
+ const conversationIdRef = useRef(conversationId);
26
+ const sendTyping = useCallback((targetId) => {
27
+ const now = Date.now();
28
+ if (now - coreSentAtRef.current >= CORE_SEND_WINDOW_MS) {
29
+ coreSentAtRef.current = now;
30
+ }
31
+ lastReportAtRef.current = now;
32
+ void changeInputState(targetId, true);
33
+ }, []);
34
+ const stopTyping = useCallback(() => {
35
+ clearTimeout(idleTimerRef.current);
36
+ if (!isTypingRef.current)
37
+ return;
38
+ isTypingRef.current = false;
39
+ lastReportAtRef.current = 0;
40
+ const targetId = conversationIdRef.current;
41
+ if (!targetId)
42
+ return;
43
+ const windowLapsed = Date.now() - coreSentAtRef.current >= CORE_SEND_WINDOW_MS;
44
+ coreSentAtRef.current = 0;
45
+ // Re-open core's window first so the stop is actually sent.
46
+ const reopen = windowLapsed
47
+ ? changeInputState(targetId, true)
48
+ : Promise.resolve();
49
+ void reopen.then(() => changeInputState(targetId, false));
50
+ }, []);
51
+ const reportActivity = useCallback((composerValue) => {
52
+ const targetId = conversationIdRef.current;
53
+ if (!targetId)
54
+ return;
55
+ if (!composerValue.trim()) {
56
+ stopTyping();
57
+ return;
58
+ }
59
+ if (!isTypingRef.current ||
60
+ Date.now() - lastReportAtRef.current >= TYPING_REPORT_INTERVAL_MS) {
61
+ isTypingRef.current = true;
62
+ sendTyping(targetId);
63
+ }
64
+ clearTimeout(idleTimerRef.current);
65
+ idleTimerRef.current = setTimeout(stopTyping, TYPING_IDLE_TIMEOUT_MS);
66
+ }, [sendTyping, stopTyping]);
67
+ // Switching threads or unmounting ends typing in the thread being left.
68
+ useEffect(() => {
69
+ conversationIdRef.current = conversationId;
70
+ return stopTyping;
71
+ }, [conversationId, stopTyping]);
72
+ return { reportActivity, stopTyping };
73
+ };
@@ -3,6 +3,7 @@ import { type CustomMessagePayload } from "../../types/customMessage";
3
3
  import { BotFlowMeta, ExtendMessageInfo, FileMsgParamsByFile, ImageMsgParamsByFile, MentionOffset, MentionSendPayload, UrlMetadataInfo, VideoMsgParamsByFile } from "../../types/chat";
4
4
  import { UploadFile } from "antd";
5
5
  import { ISessionResponse } from "../../types/dto";
6
+ import type { StickerItem } from "../../types/sticker";
6
7
  export declare const isMediaResendable: (message: {
7
8
  clientMsgID: string;
8
9
  contentType: number;
@@ -15,6 +16,7 @@ export declare const createFileMessageByFile: (file: FileMsgParamsByFile) => Pro
15
16
  export declare const AT_ALL_TAG = "AtAllTag";
16
17
  export declare const createAtTextMessage: (params: AtMsgParams) => Promise<MessageItem | null>;
17
18
  export declare const createUrlTextMessage: (text: string, urls: string[]) => Promise<MessageItem | null>;
19
+ export declare const createStickerMessage: (stickerUrl: string) => Promise<MessageItem | null>;
18
20
  export declare const createCustomMessageByParams: (params: CustomMsgParams) => Promise<MessageItem | null>;
19
21
  export declare const createCustomMessage: (payload: CustomMessagePayload) => Promise<MessageItem | null>;
20
22
  export declare const createQuoteMessage: (text: string, quotedMessage: MessageItem, urls?: string[]) => Promise<MessageItem | null>;
@@ -37,13 +39,17 @@ export declare const useSendMessage: (options?: UseSendMessageOptions) => {
37
39
  sendCustomMessage: (payload: CustomMessagePayload, sendOptions?: {
38
40
  currentSession?: ISessionResponse;
39
41
  }) => Promise<boolean>;
42
+ sendStickerMessage: (sticker: StickerItem, sendOptions?: {
43
+ currentSession?: ISessionResponse;
44
+ }) => Promise<boolean>;
40
45
  resendMessage: (failedMessage: MessageItem) => Promise<void>;
41
46
  };
42
- export declare const generateExtendMessageInfo: ({ richText, urlMetadata, currentSession, flow, mentionOffsets, }: {
47
+ export declare const generateExtendMessageInfo: ({ richText, urlMetadata, currentSession, flow, mentionOffsets, stickerId, }: {
43
48
  richText?: string;
44
49
  urlMetadata?: UrlMetadataInfo;
45
50
  currentSession?: ISessionResponse;
46
51
  flow?: BotFlowMeta;
47
52
  mentionOffsets?: MentionOffset[];
53
+ stickerId?: string;
48
54
  }) => ExtendMessageInfo;
49
55
  //# sourceMappingURL=useSendMessage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSendMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useSendMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,eAAe,EACf,eAAe,EACf,WAAW,EAIZ,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACrB,MAAM,cAAc,CAAC;AAWtB,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAQlC,OAAO,EAEL,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AAmBrB,eAAO,MAAM,iBAAiB,GAAI,SAAS;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,YAE0C,CAAC;AAE5C,eAAO,MAAM,iBAAiB,GAAU,MAAM,MAAM,gCAanD,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,MAAM,oBAAoB,gCAaxE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,iBAAiB,eAAe,gCAazE,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,MAAM,oBAAoB,gCAaxE,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAU,MAAM,mBAAmB,gCAatE,CAAC;AAGF,eAAO,MAAM,UAAU,aAAa,CAAC;AAErC,eAAO,MAAM,mBAAmB,GAAU,QAAQ,WAAW,gCAa5D,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAU,MAAM,MAAM,EAAE,MAAM,MAAM,EAAE,gCActE,CAAC;AAEF,eAAO,MAAM,2BAA2B,GAAU,QAAQ,eAAe,gCAaxE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,SAAS,oBAAoB,gCAOtE,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,EACZ,eAAe,WAAW,EAC1B,OAAO,MAAM,EAAE,gCAehB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAC9B,4BAA4B,GAC5B,iDAAiD,CAAC;AAEtD,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,sBAAsB,CAAC;CACtC;AAED,eAAO,MAAM,cAAc,GAAI,UAAU,qBAAqB;gIAwSvD;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;QACrB,cAAc,CAAC,EAAE,gBAAgB,CAAC;QAClC,UAAU,EAAE,OAAO,CAAC;QACpB,WAAW,CAAC,EAAE,eAAe,CAAC;QAC9B,IAAI,CAAC,EAAE,WAAW,CAAC;QACnB,aAAa,CAAC,EAAE,oBAAoB,CAAC;QACrC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;KAC/B,KAAG,OAAO,CAAC,OAAO,CAAC;iCA7CT,oBAAoB,gBACf;QAAE,cAAc,CAAC,EAAE,gBAAgB,CAAA;KAAE,KAClD,OAAO,CAAC,OAAO,CAAC;mCAnHG,WAAW;CAsXpC,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,kEAMvC;IACD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC,sBAkCA,CAAC"}
1
+ {"version":3,"file":"useSendMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useSendMessage.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,7 @@ import { SessionStatus, SessionTag } from "../../types/chat";
17
17
  import { updateSession } from "../../hooks/session/useUpdateSession";
18
18
  import { invalidateSessionQueries } from "../../utils/queryHelpers";
19
19
  import { resolveConversationBotId } from "../../utils/conversation";
20
+ import { QUERY_KEYS } from "../../services/query";
20
21
  const messageFileStore = new Map();
21
22
  const MEDIA_CONTENT_TYPES = new Set([
22
23
  MessageType.PictureMessage,
@@ -104,6 +105,17 @@ export const createUrlTextMessage = async (text, urls) => {
104
105
  });
105
106
  return textMessage;
106
107
  };
108
+ export const createStickerMessage = async (stickerUrl) => {
109
+ const stickerMessage = await DChatSDK.createStickerMessage(stickerUrl, uuidv4())
110
+ .then(({ data }) => {
111
+ return data;
112
+ })
113
+ .catch(({ errCode, errMsg }) => {
114
+ console.error("createStickerMessage", errCode, errMsg);
115
+ return null;
116
+ });
117
+ return stickerMessage;
118
+ };
107
119
  export const createCustomMessageByParams = async (params) => {
108
120
  const customMessage = await DChatSDK.createCustomMessage(params, new Date().getTime().toString())
109
121
  .then(({ data }) => {
@@ -242,7 +254,7 @@ export const useSendMessage = (options) => {
242
254
  }
243
255
  }, [dispatchMessage, scrollEvent]);
244
256
  const resendMessage = useCallback(async (failedMessage) => {
245
- var _a;
257
+ var _a, _b;
246
258
  const storedFiles = messageFileStore.get(failedMessage.clientMsgID);
247
259
  if (!storedFiles) {
248
260
  let newTextPayload = null;
@@ -260,6 +272,11 @@ export const useSendMessage = (options) => {
260
272
  failedMessage.urlTextElem) {
261
273
  newTextPayload = await createUrlTextMessage(textContent, failedMessage.urlTextElem.urls);
262
274
  }
275
+ else if (failedMessage.contentType === MessageType.StickerMessage) {
276
+ newTextPayload = ((_b = failedMessage.stickerElem) === null || _b === void 0 ? void 0 : _b.content)
277
+ ? await createStickerMessage(failedMessage.stickerElem.content)
278
+ : null;
279
+ }
263
280
  else {
264
281
  newTextPayload = await createTextMessage(textContent);
265
282
  }
@@ -342,6 +359,28 @@ export const useSendMessage = (options) => {
342
359
  });
343
360
  return sendMessage(Object.assign(Object.assign({}, customMessage), { ex: JSON.stringify(extendMessageInfo) || "{}", isInternal: false }), sendOptions === null || sendOptions === void 0 ? void 0 : sendOptions.currentSession);
344
361
  }, [conversationData, recvID, groupID, sendMessage]);
362
+ const sendStickerMessage = useCallback(async (sticker, sendOptions) => {
363
+ var _a;
364
+ if (!conversationData || (!recvID && !groupID))
365
+ return false;
366
+ if (!((_a = sticker.file) === null || _a === void 0 ? void 0 : _a.fullUrl))
367
+ return false;
368
+ const stickerMessage = await createStickerMessage(sticker.file.fullUrl);
369
+ if (!stickerMessage)
370
+ return false;
371
+ // BE records "recent stickers" only from ex.stickerId on the saved message.
372
+ const extendMessageInfo = generateExtendMessageInfo({
373
+ currentSession: sendOptions === null || sendOptions === void 0 ? void 0 : sendOptions.currentSession,
374
+ stickerId: sticker.id,
375
+ });
376
+ const isSent = await sendMessage(Object.assign(Object.assign({}, stickerMessage), { ex: JSON.stringify(extendMessageInfo) || "{}", isInternal: false }), sendOptions === null || sendOptions === void 0 ? void 0 : sendOptions.currentSession);
377
+ if (isSent) {
378
+ queryClient.invalidateQueries({
379
+ queryKey: [QUERY_KEYS.GET_STICKER_HISTORY],
380
+ });
381
+ }
382
+ return isSent;
383
+ }, [conversationData, recvID, groupID, sendMessage, queryClient]);
345
384
  const sendMergeMessage = useCallback(async ({ richText, plainText, files = [], currentSession, isInternal, urlMetadata, flow, customMessage, mentions, }) => {
346
385
  if (!conversationData || (!recvID && !groupID))
347
386
  return false;
@@ -535,14 +574,18 @@ export const useSendMessage = (options) => {
535
574
  return {
536
575
  sendMergeMessage,
537
576
  sendCustomMessage,
577
+ sendStickerMessage,
538
578
  resendMessage,
539
579
  };
540
580
  };
541
- export const generateExtendMessageInfo = ({ richText, urlMetadata, currentSession, flow, mentionOffsets, }) => {
581
+ export const generateExtendMessageInfo = ({ richText, urlMetadata, currentSession, flow, mentionOffsets, stickerId, }) => {
542
582
  const extendMessageInfo = {
543
583
  sessionId: (currentSession === null || currentSession === void 0 ? void 0 : currentSession.id) || "",
544
584
  applicationType: useAuthStore.getState().applicationType,
545
585
  };
586
+ if (stickerId) {
587
+ extendMessageInfo.stickerId = stickerId;
588
+ }
546
589
  if (flow) {
547
590
  extendMessageInfo.flow = flow;
548
591
  }
@@ -0,0 +1,11 @@
1
+ import { ConversationInputStatus, MessageItem } from "@droppii-org/wasm-client-sdk";
2
+ export declare const handleInputStatusChanged: (payload: ConversationInputStatus | string) => void;
3
+ /** A typer's own message landing ends their typing state right away. */
4
+ export declare const removeTyperOnNewMessage: (message: MessageItem) => void;
5
+ export interface TypingState {
6
+ /** Resolved names of the first typers, only filled when count <= 2. */
7
+ names: string[];
8
+ count: number;
9
+ }
10
+ export declare const useTypingState: (conversationId?: string) => TypingState;
11
+ //# sourceMappingURL=useTypingStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTypingStatus.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useTypingStatus.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,uBAAuB,EACvB,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAuEtC,eAAO,MAAM,wBAAwB,GACnC,SAAS,uBAAuB,GAAG,MAAM,SA0B1C,CAAC;AAUF,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,GAAI,SAAS,WAAW,SAK3D,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,cAAc,GAAI,iBAAiB,MAAM,KAAG,WAkBxD,CAAC"}
@@ -0,0 +1,120 @@
1
+ import { useTranslation } from "react-i18next";
2
+ import { useShallow } from "zustand/react/shallow";
3
+ import { SessionType, } from "@droppii-org/wasm-client-sdk";
4
+ import { DChatSDK } from "../../constants/sdk";
5
+ import useAuthStore from "../../store/auth";
6
+ import useConversationStore from "../../store/conversation";
7
+ import useTypingStore from "../../store/typing";
8
+ import { getGroupIdFromConversationId } from "../../utils/conversation";
9
+ // Two names fit the "<A> và <B>" rule; from three on the text drops names.
10
+ const MAX_NAMED_TYPERS = 2;
11
+ const pendingNameLookups = new Map();
12
+ // Typers whose latest event was "start"; a stop during a name lookup wins.
13
+ const activeTypers = new Set();
14
+ // Names are per conversation: group nicknames and 1:1 aliases differ by thread.
15
+ const nameKey = (conversationId, userId) => `${conversationId}:${userId}`;
16
+ const pickNickname = (list) => { var _a, _b; return ((_b = (_a = list === null || list === void 0 ? void 0 : list[0]) === null || _a === void 0 ? void 0 : _a.nickname) === null || _b === void 0 ? void 0 : _b.trim()) || ""; };
17
+ const lookupTyperName = async (key, conversationId, userId) => {
18
+ var _a;
19
+ const conversation = useConversationStore
20
+ .getState()
21
+ .conversationList.find((item) => item.conversationID === conversationId);
22
+ const peerName = (_a = conversation === null || conversation === void 0 ? void 0 : conversation.showName) === null || _a === void 0 ? void 0 : _a.trim();
23
+ const { setName } = useTypingStore.getState();
24
+ if (conversation && !conversation.groupID && peerName) {
25
+ setName(key, peerName);
26
+ return;
27
+ }
28
+ try {
29
+ const groupId = (conversation === null || conversation === void 0 ? void 0 : conversation.groupID) || getGroupIdFromConversationId(conversationId);
30
+ let nickname = "";
31
+ if (groupId) {
32
+ const { data } = await DChatSDK.getSpecifiedGroupMembersInfo({
33
+ groupID: groupId,
34
+ userIDList: [userId],
35
+ });
36
+ nickname = pickNickname(data);
37
+ }
38
+ if (!nickname) {
39
+ const { data } = await DChatSDK.getUsersInfo([userId]);
40
+ nickname = pickNickname(data);
41
+ }
42
+ if (nickname)
43
+ setName(key, nickname);
44
+ }
45
+ catch (error) {
46
+ console.error("resolveTyperName", error);
47
+ }
48
+ };
49
+ /** Settles once the name is cached or the lookup gave up (never rejects). */
50
+ const resolveTyperName = (conversationId, userId) => {
51
+ const key = nameKey(conversationId, userId);
52
+ if (useTypingStore.getState().names[key])
53
+ return Promise.resolve();
54
+ let pending = pendingNameLookups.get(key);
55
+ if (!pending) {
56
+ pending = lookupTyperName(key, conversationId, userId).finally(() => pendingNameLookups.delete(key));
57
+ pendingNameLookups.set(key, pending);
58
+ }
59
+ return pending;
60
+ };
61
+ export const handleInputStatusChanged = (payload) => {
62
+ var _a;
63
+ let data;
64
+ try {
65
+ data = typeof payload === "string" ? JSON.parse(payload) : payload;
66
+ }
67
+ catch (error) {
68
+ console.error("handleInputStatusChanged", error);
69
+ return;
70
+ }
71
+ if (!(data === null || data === void 0 ? void 0 : data.conversationID) || !data.userID)
72
+ return;
73
+ if (data.userID === useAuthStore.getState().userID)
74
+ return;
75
+ const { conversationID, userID } = data;
76
+ const key = nameKey(conversationID, userID);
77
+ if ((_a = data.platformIDs) === null || _a === void 0 ? void 0 : _a.length) {
78
+ activeTypers.add(key);
79
+ // Shown only once named, so a placeholder name never flashes first.
80
+ void resolveTyperName(conversationID, userID).then(() => {
81
+ if (activeTypers.has(key)) {
82
+ useTypingStore.getState().addTyper(conversationID, userID);
83
+ }
84
+ });
85
+ }
86
+ else {
87
+ activeTypers.delete(key);
88
+ useTypingStore.getState().removeTyper(conversationID, userID);
89
+ }
90
+ };
91
+ // Mirrors OpenIM's conversationID scheme: si_<sorted user ids> / sg_<groupID>.
92
+ const getConversationIdFromMessage = (message) => {
93
+ if (message.sessionType === SessionType.Single) {
94
+ return `si_${[message.sendID, message.recvID].sort().join("_")}`;
95
+ }
96
+ return message.groupID ? `sg_${message.groupID}` : "";
97
+ };
98
+ /** A typer's own message landing ends their typing state right away. */
99
+ export const removeTyperOnNewMessage = (message) => {
100
+ const conversationId = getConversationIdFromMessage(message);
101
+ if (!conversationId || !message.sendID)
102
+ return;
103
+ activeTypers.delete(nameKey(conversationId, message.sendID));
104
+ useTypingStore.getState().removeTyper(conversationId, message.sendID);
105
+ };
106
+ export const useTypingState = (conversationId) => {
107
+ const { t } = useTranslation("conversation-inbox");
108
+ const count = useTypingStore((state) => { var _a, _b; return conversationId ? ((_b = (_a = state.typers[conversationId]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) : 0; });
109
+ // Only the named typers' entries, so unrelated name lookups don't re-render.
110
+ const resolvedNames = useTypingStore(useShallow((state) => {
111
+ const typers = conversationId ? state.typers[conversationId] : undefined;
112
+ if (!typers || typers.length > MAX_NAMED_TYPERS)
113
+ return [];
114
+ return typers.map((userId) => state.names[nameKey(conversationId, userId)]);
115
+ }));
116
+ return {
117
+ names: resolvedNames.map((name) => name || t("thread_card.fallback_name")),
118
+ count,
119
+ };
120
+ };
@@ -0,0 +1,5 @@
1
+ import type { StickerCategory, StickerItem } from "../../types/sticker";
2
+ export declare const useStickerCategories: () => import("@tanstack/react-query").UseQueryResult<StickerCategory[], Error>;
3
+ export declare const useStickersByCategory: (categoryId: string, enabled: boolean) => import("@tanstack/react-query").UseQueryResult<StickerItem[], Error>;
4
+ export declare const useStickerHistory: () => import("@tanstack/react-query").UseQueryResult<StickerItem[], Error>;
5
+ //# sourceMappingURL=useStickers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStickers.d.ts","sourceRoot":"","sources":["../../../src/hooks/sticker/useStickers.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA2BpE,eAAO,MAAM,oBAAoB,gFAK7B,CAAC;AAEL,eAAO,MAAM,qBAAqB,GAAI,YAAY,MAAM,EAAE,SAAS,OAAO,yEAMtE,CAAC;AAIL,eAAO,MAAM,iBAAiB,4EAM1B,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+ import { apiInstance } from "../../services/api";
3
+ import { ENDPOINTS } from "../../services/routes";
4
+ import { QUERY_KEYS } from "../../services/query";
5
+ const STICKER_LIST_STALE_TIME = 5 * 60 * 1000;
6
+ const fetchStickerCategories = async () => {
7
+ var _a, _b;
8
+ const response = await apiInstance.get(ENDPOINTS.chatService.getStickerCategories);
9
+ return (_b = (_a = response.data) === null || _a === void 0 ? void 0 : _a.data) !== null && _b !== void 0 ? _b : [];
10
+ };
11
+ const fetchStickersByCategory = async (categoryId) => {
12
+ var _a, _b;
13
+ const response = await apiInstance.get(ENDPOINTS.chatService.getStickersByCategory(categoryId));
14
+ return (_b = (_a = response.data) === null || _a === void 0 ? void 0 : _a.data) !== null && _b !== void 0 ? _b : [];
15
+ };
16
+ const fetchStickerHistory = async () => {
17
+ var _a, _b;
18
+ const response = await apiInstance.get(ENDPOINTS.chatService.getStickerHistory);
19
+ return (_b = (_a = response.data) === null || _a === void 0 ? void 0 : _a.data) !== null && _b !== void 0 ? _b : [];
20
+ };
21
+ export const useStickerCategories = () => useQuery({
22
+ queryKey: [QUERY_KEYS.GET_STICKER_CATEGORIES],
23
+ queryFn: fetchStickerCategories,
24
+ staleTime: STICKER_LIST_STALE_TIME,
25
+ });
26
+ export const useStickersByCategory = (categoryId, enabled) => useQuery({
27
+ queryKey: [QUERY_KEYS.GET_STICKERS_BY_CATEGORY, categoryId],
28
+ queryFn: () => fetchStickersByCategory(categoryId),
29
+ enabled: enabled && !!categoryId,
30
+ staleTime: STICKER_LIST_STALE_TIME,
31
+ });
32
+ // BE writes history from a message webhook after the send settles, so a
33
+ // cached copy goes stale on every send — always refetch when the picker opens.
34
+ export const useStickerHistory = () => useQuery({
35
+ queryKey: [QUERY_KEYS.GET_STICKER_HISTORY],
36
+ queryFn: fetchStickerHistory,
37
+ staleTime: 0,
38
+ gcTime: 0,
39
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../../src/hooks/user/useAuth.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,YAAY;;CA4BxB,CAAC"}
1
+ {"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../../src/hooks/user/useAuth.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY;;CA6BxB,CAAC"}
@@ -3,6 +3,7 @@ import { DChatSDK } from "../../constants/sdk";
3
3
  import useAuthStore from "../../store/auth";
4
4
  import useConversationStore from "../../store/conversation";
5
5
  import useMessageDraftStore from "../../store/messageDraft";
6
+ import useTypingStore from "../../store/typing";
6
7
  export const useDChatAuth = () => {
7
8
  const initAuthStore = useAuthStore((state) => state.initAuthStore);
8
9
  const resetConversationStore = useConversationStore((state) => state.resetConversationStore);
@@ -12,6 +13,7 @@ export const useDChatAuth = () => {
12
13
  const applicationType = useAuthStore.getState().applicationType;
13
14
  resetConversationStore();
14
15
  clearAllDrafts();
16
+ useTypingStore.getState().clearTypers();
15
17
  initAuthStore({
16
18
  chatToken: "",
17
19
  accessToken: "",
@@ -83,6 +83,12 @@
83
83
  "product": "Gửi sản phẩm",
84
84
  "appointment": "Tạo lịch hẹn"
85
85
  },
86
+ "sticker": {
87
+ "toggle": "Nhãn dán",
88
+ "recent": "Gần đây",
89
+ "empty": "Chưa có nhãn dán",
90
+ "load_failed": "Không tải được nhãn dán"
91
+ },
86
92
  "bot": {
87
93
  "start": "Bắt đầu",
88
94
  "restart": "Bắt đầu lại",
@@ -144,5 +150,10 @@
144
150
  "public_group_fallback": "Nhóm công khai",
145
151
  "group_join": "Tham gia",
146
152
  "group_access": "Truy cập"
153
+ },
154
+ "typing": {
155
+ "many_label": "Nhiều người đang nhập",
156
+ "and": "và",
157
+ "suffix": "đang nhập"
147
158
  }
148
159
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-detail/index.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAWrF,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,6CAA6C,CAAC;AAGrD,MAAM,WAAW,4BACf,SAAQ,4BAA4B,EAClC,4BAA4B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,QAAA,MAAM,uBAAuB,GAAI,8IAc9B,4BAA4B,mDAqO9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-detail/index.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAWrF,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,6CAA6C,CAAC;AAGrD,MAAM,WAAW,4BACf,SAAQ,4BAA4B,EAClC,4BAA4B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,QAAA,MAAM,uBAAuB,GAAI,8IAc9B,4BAA4B,mDAsO9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -24,6 +24,7 @@ import ThreadProfilePanel from "../../components/conversation-detail/ThreadProfi
24
24
  import MediaResourcePanel from "../../components/media-resources/MediaResourcePanel";
25
25
  import { MediaResourceTabKey } from "../../components/media-resources/constants";
26
26
  import { ConversationMessageList, ConversationDetailHeader, ConversationDetailInput, PinnedMessageBar, } from "../../components/conversation-detail";
27
+ import TypingIndicatorBar from "../../components/conversation-detail/TypingIndicatorBar";
27
28
  import { BotButtonMessage, BotInactiveNotice, BotMenuSheet, BotStartButton, BotSuggest, CsSessionConfirmModal, } from "../../components/conversation-detail/bot";
28
29
  import { useBotFlow } from "../../hooks/conversation/useBotFlow";
29
30
  import { BOT_TYPE } from "../../constants/botFlow";
@@ -113,6 +114,6 @@ const DChatConversationDetail = (_a) => {
113
114
  !botFlow.flowIsEnd &&
114
115
  botFlow.shouldShowButtons &&
115
116
  botFlow.currentButtonsBlock ? (_jsx(BotButtonMessage, { buttons: botFlow.currentButtonsBlock.data.buttons, disabled: botFlow.isProcessing, onSelect: botFlow.selectButton, onRestart: botFlow.restart })) : null;
116
- return (_jsxs("div", { "data-testid": "conversation-detail-root", className: clsx("relative flex h-full w-full flex-col overflow-hidden bg-white", className), children: [_jsx(ConversationDetailHeader, { conversation: conversation, onBack: handleBack, onProfileClick: openProfile }), _jsx(PinnedMessageBar, {}), _jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: children !== null && children !== void 0 ? children : (_jsx(ConversationMessageList, { bottomSlot: botButtonsSlot, onCopyMessageLink: onCopyMessageLink, onSearchMessageRevoked: onSearchMessageRevoked })) }), footer, isBotThread && (_jsxs(_Fragment, { children: [_jsx(BotMenuSheet, { open: isMenuOpen, items: botFlow.menuItems, disabled: botFlow.isProcessing, onClose: closeMenu, onSelect: botFlow.selectMenuOption }), _jsx(CsSessionConfirmModal, { open: botFlow.isCsConfirmOpen, loading: botFlow.isProcessing, onCancel: botFlow.cancelMenuOption, onConfirm: botFlow.confirmEndCsSession })] })), _jsx(ThreadProfilePanel, { open: isProfileOpen, conversation: conversation, onClose: closeProfile, onSelectResource: setResourceTab }), _jsx(MediaResourcePanel, { open: resourceTab !== null, initialTab: resourceTab !== null && resourceTab !== void 0 ? resourceTab : MediaResourceTabKey.Image, onClose: () => setResourceTab(null) })] }));
117
+ return (_jsxs("div", { "data-testid": "conversation-detail-root", className: clsx("relative flex h-full w-full flex-col overflow-hidden bg-white", className), children: [_jsx(ConversationDetailHeader, { conversation: conversation, onBack: handleBack, onProfileClick: openProfile }), _jsx(PinnedMessageBar, {}), _jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [children !== null && children !== void 0 ? children : (_jsx(ConversationMessageList, { bottomSlot: botButtonsSlot, onCopyMessageLink: onCopyMessageLink, onSearchMessageRevoked: onSearchMessageRevoked })), _jsx(TypingIndicatorBar, { conversationId: conversation.conversationID })] }), footer, isBotThread && (_jsxs(_Fragment, { children: [_jsx(BotMenuSheet, { open: isMenuOpen, items: botFlow.menuItems, disabled: botFlow.isProcessing, onClose: closeMenu, onSelect: botFlow.selectMenuOption }), _jsx(CsSessionConfirmModal, { open: botFlow.isCsConfirmOpen, loading: botFlow.isProcessing, onCancel: botFlow.cancelMenuOption, onConfirm: botFlow.confirmEndCsSession })] })), _jsx(ThreadProfilePanel, { open: isProfileOpen, conversation: conversation, onClose: closeProfile, onSelectResource: setResourceTab }), _jsx(MediaResourcePanel, { open: resourceTab !== null, initialTab: resourceTab !== null && resourceTab !== void 0 ? resourceTab : MediaResourceTabKey.Image, onClose: () => setResourceTab(null) })] }));
117
118
  };
118
119
  export default DChatConversationDetail;
@@ -18,6 +18,8 @@ export interface DChatConversationInboxProps {
18
18
  onCopyMessageLink?: (conversationId: string, clientMsgID: string) => void;
19
19
  /** Called instead of the SDK's default toast when a jumped-to message turns out to be revoked. */
20
20
  onSearchMessageRevoked?: () => void;
21
+ /** Shows the sticker picker entry in the thread composer. Defaults to true. */
22
+ showSticker?: boolean;
21
23
  }
22
24
  declare const DChatConversationInbox: (props: DChatConversationInboxProps) => import("react/jsx-runtime").JSX.Element;
23
25
  export default DChatConversationInbox;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-inbox/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAM/D,MAAM,WAAW,2BAA2B;IAC1C,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,6FAA6F;IAC7F,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,yEAAyE;IACzE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAClC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAoOD,QAAA,MAAM,sBAAsB,GAAI,OAAO,2BAA2B,4CAQjE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-inbox/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAM/D,MAAM,WAAW,2BAA2B;IAC1C,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,6FAA6F;IAC7F,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,yEAAyE;IACzE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAClC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IACpC,+EAA+E;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAsOD,QAAA,MAAM,sBAAsB,GAAI,OAAO,2BAA2B,4CAQjE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -77,7 +77,7 @@ const useInboxConversationList = (chatCategory) => {
77
77
  const isApplicationTypeValid = Object.values(DChatApplicationType).includes(applicationType);
78
78
  return Object.assign(Object.assign({}, useConversationList({ enabled: isApplicationTypeValid, chatCategory })), { isApplicationTypeValid });
79
79
  };
80
- const DChatConversationInboxBody = ({ onSelectThread, className, moreOptions, showSearch = true, headerTitle, headerRightSlot, onCopyMessageLink, onSearchMessageRevoked, target, conversationList, }) => {
80
+ const DChatConversationInboxBody = ({ onSelectThread, className, moreOptions, showSearch = true, headerTitle, headerRightSlot, onCopyMessageLink, onSearchMessageRevoked, showSticker, target, conversationList, }) => {
81
81
  const { t } = useTranslation("conversation-inbox");
82
82
  const selectedConversationId = useConversationStore((state) => state.selectedConversationId);
83
83
  const removedFromGroupModalOpen = useConversationStore((state) => state.removedFromGroupModalOpen);
@@ -99,7 +99,7 @@ const DChatConversationInboxBody = ({ onSelectThread, className, moreOptions, sh
99
99
  groupId ||
100
100
  botId);
101
101
  if (hasConversationTarget) {
102
- return (_jsx(DChatConversationDetail, { className: className, moreOptions: moreOptions, conversationId: threadId !== null && threadId !== void 0 ? threadId : undefined, userId: userId, groupId: groupId, botId: botId, onCopyMessageLink: onCopyMessageLink, onSearchMessageRevoked: onSearchMessageRevoked, messageId: messageId }));
102
+ return (_jsx(DChatConversationDetail, { className: className, moreOptions: moreOptions, conversationId: threadId !== null && threadId !== void 0 ? threadId : undefined, userId: userId, groupId: groupId, botId: botId, onCopyMessageLink: onCopyMessageLink, onSearchMessageRevoked: onSearchMessageRevoked, messageId: messageId, showSticker: showSticker }));
103
103
  }
104
104
  return (_jsxs("div", { "data-testid": "conversation-inbox-root", className: clsx(
105
105
  // `relative` + `overflow-hidden` keep ContactSearchPanel's Drawer
@@ -46,5 +46,8 @@ export declare const QUERY_KEYS: {
46
46
  DELETE_CONVERSATION: string;
47
47
  GET_JOIN_LINK: string;
48
48
  GET_JOIN_LINK_INFO: string;
49
+ GET_STICKER_CATEGORIES: string;
50
+ GET_STICKERS_BY_CATEGORY: string;
51
+ GET_STICKER_HISTORY: string;
49
52
  };
50
53
  //# sourceMappingURL=query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/services/query.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDtB,CAAC"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/services/query.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmDtB,CAAC"}
@@ -46,4 +46,7 @@ export const QUERY_KEYS = {
46
46
  DELETE_CONVERSATION: "DELETE_CONVERSATION",
47
47
  GET_JOIN_LINK: "GET_JOIN_LINK",
48
48
  GET_JOIN_LINK_INFO: "GET_JOIN_LINK_INFO",
49
+ GET_STICKER_CATEGORIES: "GET_STICKER_CATEGORIES",
50
+ GET_STICKERS_BY_CATEGORY: "GET_STICKERS_BY_CATEGORY",
51
+ GET_STICKER_HISTORY: "GET_STICKER_HISTORY",
49
52
  };
@@ -43,6 +43,9 @@ export declare const ENDPOINTS: {
43
43
  getUrlMetadata: string;
44
44
  getMessageResources: string;
45
45
  searchContacts: string;
46
+ getStickerCategories: string;
47
+ getStickersByCategory: (categoryId: string) => string;
48
+ getStickerHistory: string;
46
49
  };
47
50
  identityService: {
48
51
  getToken: string;
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/services/routes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS;;;;;;mCAMS,MAAM;;0CAGC,MAAM,UAAU,MAAM;6BAEnC,MAAM;;oDAEiB,MAAM;uCAEnB,MAAM;kCAEX,MAAM;;oCAGJ,MAAM;mCAEP,MAAM;uCAEF,MAAM;;;;;;;;;iCAUZ,MAAM;4CAEK,MAAM;mCAEf,MAAM;yCAEA,MAAM;gCAEf,MAAM;;;;;gDAOU,MAAM;qCAEjB,MAAM;uCAEJ,MAAM;0CAEH,MAAM;yCAEP,MAAM;8BAEjB,MAAM;wCACI,MAAM;;;;;;;;;;;;;;CAezC,CAAC"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/services/routes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS;;;;;;mCAMS,MAAM;;0CAGC,MAAM,UAAU,MAAM;6BAEnC,MAAM;;oDAEiB,MAAM;uCAEnB,MAAM;kCAEX,MAAM;;oCAGJ,MAAM;mCAEP,MAAM;uCAEF,MAAM;;;;;;;;;iCAUZ,MAAM;4CAEK,MAAM;mCAEf,MAAM;yCAEA,MAAM;gCAEf,MAAM;;;;;gDAOU,MAAM;qCAEjB,MAAM;uCAEJ,MAAM;0CAEH,MAAM;yCAEP,MAAM;8BAEjB,MAAM;wCACI,MAAM;;;;;4CAMF,MAAM;;;;;;;;;;;;CAa7C,CAAC"}
@@ -43,6 +43,9 @@ export const ENDPOINTS = {
43
43
  getUrlMetadata: "/chat-service/v1/utils/url-metadata",
44
44
  getMessageResources: "/chat-service/v1/app/messages/resources",
45
45
  searchContacts: "/chat-service/v1/app/contacts/search",
46
+ getStickerCategories: "/chat-service/v1/app/sticker-categories",
47
+ getStickersByCategory: (categoryId) => `/chat-service/v1/app/sticker-categories/${categoryId}/stickers`,
48
+ getStickerHistory: "/chat-service/v1/app/stickers/history",
46
49
  },
47
50
  identityService: {
48
51
  getToken: "/identity-service/v1/identity/get-token",
@@ -0,0 +1,13 @@
1
+ interface TypingStore {
2
+ /** conversationID -> userIDs currently typing, in the order they started. */
3
+ typers: Record<string, string[]>;
4
+ /** "<conversationID>:<userID>" -> display name, resolved once and reused. */
5
+ names: Record<string, string>;
6
+ addTyper: (conversationId: string, userId: string) => void;
7
+ removeTyper: (conversationId: string, userId: string) => void;
8
+ setName: (key: string, name: string) => void;
9
+ clearTypers: () => void;
10
+ }
11
+ declare const useTypingStore: import("zustand").UseBoundStore<import("zustand").StoreApi<TypingStore>>;
12
+ export default useTypingStore;
13
+ //# sourceMappingURL=typing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typing.d.ts","sourceRoot":"","sources":["../../src/store/typing.ts"],"names":[],"mappings":"AAeA,UAAU,WAAW;IACnB,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACjC,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,QAAQ,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,WAAW,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9D,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,QAAA,MAAM,cAAc,0EA4CjB,CAAC;AAEJ,eAAe,cAAc,CAAC"}