@planningcenter/chat-react-native 2.0.1-rc.0 → 2.1.0-rc.1

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 (138) hide show
  1. package/build/components/conversation/message.d.ts.map +1 -1
  2. package/build/components/conversation/message.js +7 -2
  3. package/build/components/conversation/message.js.map +1 -1
  4. package/build/components/conversation/message_reaction.d.ts +1 -1
  5. package/build/components/conversation/message_reaction.d.ts.map +1 -1
  6. package/build/components/conversation/message_reaction.js +1 -1
  7. package/build/components/conversation/message_reaction.js.map +1 -1
  8. package/build/components/conversations.d.ts.map +1 -1
  9. package/build/components/conversations.js +76 -30
  10. package/build/components/conversations.js.map +1 -1
  11. package/build/components/display/badge.d.ts +2 -6
  12. package/build/components/display/badge.d.ts.map +1 -1
  13. package/build/components/display/badge.js +1 -5
  14. package/build/components/display/badge.js.map +1 -1
  15. package/build/components/display/tabs.d.ts +17 -0
  16. package/build/components/display/tabs.d.ts.map +1 -0
  17. package/build/components/display/tabs.js +97 -0
  18. package/build/components/display/tabs.js.map +1 -0
  19. package/build/components/index.d.ts +1 -1
  20. package/build/components/index.d.ts.map +1 -1
  21. package/build/components/index.js +1 -1
  22. package/build/components/index.js.map +1 -1
  23. package/build/components/{error_boundary.d.ts → page/error_boundary.d.ts} +6 -4
  24. package/build/components/page/error_boundary.d.ts.map +1 -0
  25. package/build/components/page/error_boundary.js +115 -0
  26. package/build/components/page/error_boundary.js.map +1 -0
  27. package/build/components/page/loading.d.ts +3 -0
  28. package/build/components/page/loading.d.ts.map +1 -0
  29. package/build/components/page/loading.js +24 -0
  30. package/build/components/page/loading.js.map +1 -0
  31. package/build/contexts/api_provider.js +2 -2
  32. package/build/contexts/api_provider.js.map +1 -1
  33. package/build/hooks/use_conversation_jolt_events.d.ts +2 -0
  34. package/build/hooks/use_conversation_jolt_events.d.ts.map +1 -0
  35. package/build/hooks/use_conversation_jolt_events.js +47 -0
  36. package/build/hooks/use_conversation_jolt_events.js.map +1 -0
  37. package/build/hooks/use_conversation_messages.d.ts +2 -18
  38. package/build/hooks/use_conversation_messages.d.ts.map +1 -1
  39. package/build/hooks/use_conversation_messages.js +2 -2
  40. package/build/hooks/use_conversation_messages.js.map +1 -1
  41. package/build/hooks/use_conversations.d.ts +37 -0
  42. package/build/hooks/use_conversations.d.ts.map +1 -0
  43. package/build/hooks/use_conversations.js +48 -0
  44. package/build/hooks/use_conversations.js.map +1 -0
  45. package/build/hooks/use_jolt.d.ts +9 -0
  46. package/build/hooks/use_jolt.d.ts.map +1 -0
  47. package/build/hooks/use_jolt.js +71 -0
  48. package/build/hooks/use_jolt.js.map +1 -0
  49. package/build/hooks/use_suspense_api.d.ts +7 -2
  50. package/build/hooks/use_suspense_api.d.ts.map +1 -1
  51. package/build/hooks/use_suspense_api.js +7 -2
  52. package/build/hooks/use_suspense_api.js.map +1 -1
  53. package/build/navigation/index.d.ts +11 -2
  54. package/build/navigation/index.d.ts.map +1 -1
  55. package/build/navigation/index.js +14 -6
  56. package/build/navigation/index.js.map +1 -1
  57. package/build/navigation/screenLayout.d.ts.map +1 -1
  58. package/build/navigation/screenLayout.js +5 -8
  59. package/build/navigation/screenLayout.js.map +1 -1
  60. package/build/screens/message_actions_screen.d.ts +1 -1
  61. package/build/screens/message_actions_screen.d.ts.map +1 -1
  62. package/build/screens/message_actions_screen.js +1 -1
  63. package/build/screens/message_actions_screen.js.map +1 -1
  64. package/build/screens/reactions_screen.d.ts +11 -0
  65. package/build/screens/reactions_screen.d.ts.map +1 -0
  66. package/build/screens/reactions_screen.js +83 -0
  67. package/build/screens/reactions_screen.js.map +1 -0
  68. package/build/types/resources/app_name.d.ts +2 -0
  69. package/build/types/resources/app_name.d.ts.map +1 -0
  70. package/build/types/resources/app_name.js +2 -0
  71. package/build/types/resources/app_name.js.map +1 -0
  72. package/build/types/resources/conversation.d.ts +18 -10
  73. package/build/types/resources/conversation.d.ts.map +1 -1
  74. package/build/types/resources/conversation.js.map +1 -1
  75. package/build/types/resources/conversation_badge.d.ts +12 -0
  76. package/build/types/resources/conversation_badge.d.ts.map +1 -0
  77. package/build/types/resources/conversation_badge.js +2 -0
  78. package/build/types/resources/conversation_badge.js.map +1 -0
  79. package/build/types/resources/group_resource.d.ts +12 -0
  80. package/build/types/resources/group_resource.d.ts.map +1 -0
  81. package/build/types/resources/group_resource.js +2 -0
  82. package/build/types/resources/group_resource.js.map +1 -0
  83. package/build/types/resources/index.d.ts +2 -1
  84. package/build/types/resources/index.d.ts.map +1 -1
  85. package/build/types/resources/index.js +2 -1
  86. package/build/types/resources/index.js.map +1 -1
  87. package/build/types/resources/member.d.ts +23 -0
  88. package/build/types/resources/member.d.ts.map +1 -0
  89. package/build/types/resources/member.js +2 -0
  90. package/build/types/resources/member.js.map +1 -0
  91. package/build/types/resources/member_ability.d.ts +6 -0
  92. package/build/types/resources/member_ability.d.ts.map +1 -0
  93. package/build/types/resources/member_ability.js +2 -0
  94. package/build/types/resources/member_ability.js.map +1 -0
  95. package/build/types/resources/reaction.d.ts +1 -1
  96. package/build/types/resources/reaction.js.map +1 -1
  97. package/build/utils/cache/page_mutations.d.ts +19 -2
  98. package/build/utils/cache/page_mutations.d.ts.map +1 -1
  99. package/build/utils/cache/page_mutations.js +21 -7
  100. package/build/utils/cache/page_mutations.js.map +1 -1
  101. package/build/utils/date.d.ts +4 -0
  102. package/build/utils/date.d.ts.map +1 -0
  103. package/build/utils/date.js +23 -0
  104. package/build/utils/date.js.map +1 -0
  105. package/package.json +7 -3
  106. package/src/__tests__/utils/cache/page_mutations.ts +7 -46
  107. package/src/components/conversation/message.tsx +8 -3
  108. package/src/components/conversation/message_reaction.tsx +6 -2
  109. package/src/components/conversations.tsx +95 -32
  110. package/src/components/display/badge.tsx +3 -8
  111. package/src/components/display/tabs.tsx +142 -0
  112. package/src/components/index.tsx +1 -1
  113. package/src/components/page/error_boundary.tsx +135 -0
  114. package/src/components/page/loading.tsx +28 -0
  115. package/src/contexts/api_provider.tsx +3 -3
  116. package/src/hooks/use_conversation_jolt_events.ts +67 -0
  117. package/src/hooks/use_conversation_messages.ts +6 -2
  118. package/src/hooks/use_conversations.ts +53 -0
  119. package/src/hooks/use_jolt.ts +101 -0
  120. package/src/hooks/use_suspense_api.ts +10 -3
  121. package/src/navigation/index.tsx +23 -7
  122. package/src/navigation/screenLayout.tsx +5 -10
  123. package/src/screens/message_actions_screen.tsx +1 -1
  124. package/src/screens/reactions_screen.tsx +131 -0
  125. package/src/types/resources/app_name.ts +1 -0
  126. package/src/types/resources/conversation.ts +18 -10
  127. package/src/types/resources/conversation_badge.ts +10 -0
  128. package/src/types/resources/group_resource.ts +10 -0
  129. package/src/types/resources/index.ts +2 -1
  130. package/src/types/resources/member.ts +24 -0
  131. package/src/types/resources/member_ability.ts +5 -0
  132. package/src/types/resources/reaction.ts +1 -1
  133. package/src/utils/cache/page_mutations.ts +32 -9
  134. package/src/utils/date.ts +25 -0
  135. package/build/components/error_boundary.d.ts.map +0 -1
  136. package/build/components/error_boundary.js +0 -24
  137. package/build/components/error_boundary.js.map +0 -1
  138. package/src/components/error_boundary.tsx +0 -27
@@ -5,6 +5,6 @@ export interface ReactionCountResource {
5
5
  count: number;
6
6
  mine: number;
7
7
  messageId: string;
8
- authorIds: number[];
8
+ authorIds: string[];
9
9
  }
10
10
  //# sourceMappingURL=reaction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reaction.js","sourceRoot":"","sources":["../../../src/types/resources/reaction.ts"],"names":[],"mappings":"","sourcesContent":["export interface ReactionCountResource {\n type: 'ReactionCount'\n id: string\n value: 'thumbs_up' | 'thumbs_down' | 'pray' | 'laugh' | 'heart'\n count: number\n mine: number\n messageId: string\n authorIds: number[]\n}\n"]}
1
+ {"version":3,"file":"reaction.js","sourceRoot":"","sources":["../../../src/types/resources/reaction.ts"],"names":[],"mappings":"","sourcesContent":["export interface ReactionCountResource {\n type: 'ReactionCount'\n id: string\n value: 'thumbs_up' | 'thumbs_down' | 'pray' | 'laugh' | 'heart'\n count: number\n mine: number\n messageId: string\n authorIds: string[]\n}\n"]}
@@ -14,7 +14,7 @@ export declare function updateRecordInPagesData<T extends ResourceObject>({ data
14
14
  pageParams: any;
15
15
  };
16
16
  record: T;
17
- processRecord?: (_record: T) => T;
17
+ processRecord?: (_next: T, _prev?: T) => T;
18
18
  }): {
19
19
  pages: {
20
20
  data: T[];
@@ -29,9 +29,26 @@ export declare function addRecordInPagesData<T extends ResourceObject>({ data, r
29
29
  pageParams: any;
30
30
  };
31
31
  record: T;
32
- processRecord?: (_record: T) => T;
32
+ processRecord?: (_next: T, _prev?: T) => T;
33
33
  }): {
34
34
  pages: ApiCollection<T>[];
35
35
  pageParams: any;
36
36
  } | undefined;
37
+ /**
38
+ * deleteRecordInPagesData
39
+ */
40
+ export declare function deleteRecordInPagesData<T extends ResourceObject>({ data, record, }: {
41
+ data?: {
42
+ pages: ApiCollection<T>[];
43
+ pageParams: any;
44
+ };
45
+ record: T;
46
+ }): {
47
+ pages: {
48
+ data: T[];
49
+ links: Record<string, string>;
50
+ meta: import("../../types").CollectionMeta;
51
+ }[];
52
+ pageParams: any;
53
+ } | undefined;
37
54
  //# sourceMappingURL=page_mutations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"page_mutations.d.ts","sourceRoot":"","sources":["../../../src/utils/cache/page_mutations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE3D;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,cAAc,EAAE,EAChE,IAAI,EACJ,MAAM,EACN,aAAsB,GACvB,EAAE;IACD,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QAAC,UAAU,EAAE,GAAG,CAAA;KAAE,CAAA;IACrD,MAAM,EAAE,CAAC,CAAA;IACT,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAA;CAClC;;;;;;gBAHiD,GAAG;cA4BpD;AAED,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,cAAc,EAAE,EAC7D,IAAI,EACJ,MAAM,EACN,aAAsB,GACvB,EAAE;IACD,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QAAC,UAAU,EAAE,GAAG,CAAA;KAAE,CAAA;IACrD,MAAM,EAAE,CAAC,CAAA;IACT,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAA;CAClC;;gBAHiD,GAAG;cAapD"}
1
+ {"version":3,"file":"page_mutations.d.ts","sourceRoot":"","sources":["../../../src/utils/cache/page_mutations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE3D;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,cAAc,EAAE,EAChE,IAAI,EACJ,MAAM,EACN,aAAsB,GACvB,EAAE;IACD,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QAAC,UAAU,EAAE,GAAG,CAAA;KAAE,CAAA;IACrD,MAAM,EAAE,CAAC,CAAA;IACT,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;CAC3C;;;;;;gBAHiD,GAAG;cA4BpD;AAED,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,cAAc,EAAE,EAC7D,IAAI,EACJ,MAAM,EACN,aAAsB,GACvB,EAAE;IACD,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QAAC,UAAU,EAAE,GAAG,CAAA;KAAE,CAAA;IACrD,MAAM,EAAE,CAAC,CAAA;IACT,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;CAC3C;;gBAHiD,GAAG;cAapD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,cAAc,EAAE,EAChE,IAAI,EACJ,MAAM,GACP,EAAE;IACD,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QAAC,UAAU,EAAE,GAAG,CAAA;KAAE,CAAA;IACrD,MAAM,EAAE,CAAC,CAAA;CACV;;;;;;gBAFiD,GAAG;cAcpD"}
@@ -15,7 +15,7 @@ export function updateRecordInPagesData({ data, record, processRecord = r => r,
15
15
  const newData = page.data.map(message => {
16
16
  if (message.id === record.id) {
17
17
  foundRecord = true;
18
- return processRecord(record);
18
+ return processRecord(record, message);
19
19
  }
20
20
  return message;
21
21
  });
@@ -24,9 +24,9 @@ export function updateRecordInPagesData({ data, record, processRecord = r => r,
24
24
  if (!foundRecord) {
25
25
  // Can be used to add as well but it's not at all efficient. It's better to use addRecordInPagesData.
26
26
  // This is a fallback for when the record is not found in the cache.
27
- const lastPage = { ...newPages[newPages.length - 1] };
28
- lastPage.data = [...lastPage.data, processRecord(record)];
29
- newPages[newPages.length - 1] = lastPage;
27
+ const firstPage = { ...newPages[0] };
28
+ firstPage.data = [processRecord(record), ...firstPage.data];
29
+ newPages[0] = firstPage;
30
30
  }
31
31
  return { ...data, pages: newPages };
32
32
  }
@@ -34,9 +34,23 @@ export function addRecordInPagesData({ data, record, processRecord = r => r, })
34
34
  if (!data)
35
35
  return data;
36
36
  const newPages = [...data.pages];
37
- const lastPage = { ...newPages[newPages.length - 1] };
38
- lastPage.data = [...lastPage.data, processRecord(record)];
39
- newPages[newPages.length - 1] = lastPage;
37
+ const firstPage = { ...newPages[0] };
38
+ firstPage.data = [processRecord(record), ...firstPage.data];
39
+ newPages[0] = firstPage;
40
+ return { ...data, pages: newPages };
41
+ }
42
+ /**
43
+ * deleteRecordInPagesData
44
+ */
45
+ export function deleteRecordInPagesData({ data, record, }) {
46
+ if (!data)
47
+ return data;
48
+ const newPages = data.pages.map(page => {
49
+ const newData = page.data.filter(message => {
50
+ return message.id !== record.id;
51
+ });
52
+ return { ...page, data: newData };
53
+ });
40
54
  return { ...data, pages: newPages };
41
55
  }
42
56
  //# sourceMappingURL=page_mutations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"page_mutations.js","sourceRoot":"","sources":["../../../src/utils/cache/page_mutations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAA2B,EAChE,IAAI,EACJ,MAAM,EACN,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAKvB;IACC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,IAAI,WAAW,GAAG,KAAK,CAAA;IACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;YACtC,IAAI,OAAO,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC;gBAC7B,WAAW,GAAG,IAAI,CAAA;gBAClB,OAAO,aAAa,CAAC,MAAM,CAAC,CAAA;YAC9B,CAAC;YACD,OAAO,OAAO,CAAA;QAChB,CAAC,CAAC,CAAA;QAEF,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;IACnC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,qGAAqG;QACrG,oEAAoE;QACpE,MAAM,QAAQ,GAAG,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAA;QACrD,QAAQ,CAAC,IAAI,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;QACzD,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAA;IAC1C,CAAC;IAED,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAA2B,EAC7D,IAAI,EACJ,MAAM,EACN,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAKvB;IACC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;IAChC,MAAM,QAAQ,GAAG,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAA;IACrD,QAAQ,CAAC,IAAI,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;IAEzD,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAA;IAExC,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;AACrC,CAAC","sourcesContent":["/**\n * Helpers to handle updating messages in the cache\n * Messages are ordered by ULID on the client so we may\n * have diverging logic from other kinds of api data\n */\n\nimport { ApiCollection, ResourceObject } from '../../types'\n\n/**\n * updateRecordInPagesData\n * Can be used to add as well but it's not at all efficient. It's better to use addRecordInPagesData.\n */\nexport function updateRecordInPagesData<T extends ResourceObject>({\n data,\n record,\n processRecord = r => r,\n}: {\n data?: { pages: ApiCollection<T>[]; pageParams: any }\n record: T\n processRecord?: (_record: T) => T\n}) {\n if (!data) return data\n\n let foundRecord = false\n const newPages = data.pages.map(page => {\n const newData = page.data.map(message => {\n if (message.id === record.id) {\n foundRecord = true\n return processRecord(record)\n }\n return message\n })\n\n return { ...page, data: newData }\n })\n\n if (!foundRecord) {\n // Can be used to add as well but it's not at all efficient. It's better to use addRecordInPagesData.\n // This is a fallback for when the record is not found in the cache.\n const lastPage = { ...newPages[newPages.length - 1] }\n lastPage.data = [...lastPage.data, processRecord(record)]\n newPages[newPages.length - 1] = lastPage\n }\n\n return { ...data, pages: newPages }\n}\n\nexport function addRecordInPagesData<T extends ResourceObject>({\n data,\n record,\n processRecord = r => r,\n}: {\n data?: { pages: ApiCollection<T>[]; pageParams: any }\n record: T\n processRecord?: (_record: T) => T\n}) {\n if (!data) return data\n\n const newPages = [...data.pages]\n const lastPage = { ...newPages[newPages.length - 1] }\n lastPage.data = [...lastPage.data, processRecord(record)]\n\n newPages[newPages.length - 1] = lastPage\n\n return { ...data, pages: newPages }\n}\n"]}
1
+ {"version":3,"file":"page_mutations.js","sourceRoot":"","sources":["../../../src/utils/cache/page_mutations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAA2B,EAChE,IAAI,EACJ,MAAM,EACN,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAKvB;IACC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,IAAI,WAAW,GAAG,KAAK,CAAA;IACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;YACtC,IAAI,OAAO,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC;gBAC7B,WAAW,GAAG,IAAI,CAAA;gBAClB,OAAO,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;YACvC,CAAC;YACD,OAAO,OAAO,CAAA;QAChB,CAAC,CAAC,CAAA;QAEF,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;IACnC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,qGAAqG;QACrG,oEAAoE;QACpE,MAAM,SAAS,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAA;QACpC,SAAS,CAAC,IAAI,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;QAC3D,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;IACzB,CAAC;IAED,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAA2B,EAC7D,IAAI,EACJ,MAAM,EACN,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAKvB;IACC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;IAChC,MAAM,SAAS,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAA;IACpC,SAAS,CAAC,IAAI,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAE3D,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;IAEvB,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAA2B,EAChE,IAAI,EACJ,MAAM,GAIP;IACC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;YACzC,OAAO,OAAO,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAA;QACjC,CAAC,CAAC,CAAA;QAEF,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;IACnC,CAAC,CAAC,CAAA;IAEF,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;AACrC,CAAC","sourcesContent":["/**\n * Helpers to handle updating messages in the cache\n * Messages are ordered by ULID on the client so we may\n * have diverging logic from other kinds of api data\n */\n\nimport { ApiCollection, ResourceObject } from '../../types'\n\n/**\n * updateRecordInPagesData\n * Can be used to add as well but it's not at all efficient. It's better to use addRecordInPagesData.\n */\nexport function updateRecordInPagesData<T extends ResourceObject>({\n data,\n record,\n processRecord = r => r,\n}: {\n data?: { pages: ApiCollection<T>[]; pageParams: any }\n record: T\n processRecord?: (_next: T, _prev?: T) => T\n}) {\n if (!data) return data\n\n let foundRecord = false\n const newPages = data.pages.map(page => {\n const newData = page.data.map(message => {\n if (message.id === record.id) {\n foundRecord = true\n return processRecord(record, message)\n }\n return message\n })\n\n return { ...page, data: newData }\n })\n\n if (!foundRecord) {\n // Can be used to add as well but it's not at all efficient. It's better to use addRecordInPagesData.\n // This is a fallback for when the record is not found in the cache.\n const firstPage = { ...newPages[0] }\n firstPage.data = [processRecord(record), ...firstPage.data]\n newPages[0] = firstPage\n }\n\n return { ...data, pages: newPages }\n}\n\nexport function addRecordInPagesData<T extends ResourceObject>({\n data,\n record,\n processRecord = r => r,\n}: {\n data?: { pages: ApiCollection<T>[]; pageParams: any }\n record: T\n processRecord?: (_next: T, _prev?: T) => T\n}) {\n if (!data) return data\n\n const newPages = [...data.pages]\n const firstPage = { ...newPages[0] }\n firstPage.data = [processRecord(record), ...firstPage.data]\n\n newPages[0] = firstPage\n\n return { ...data, pages: newPages }\n}\n\n/**\n * deleteRecordInPagesData\n */\nexport function deleteRecordInPagesData<T extends ResourceObject>({\n data,\n record,\n}: {\n data?: { pages: ApiCollection<T>[]; pageParams: any }\n record: T\n}) {\n if (!data) return data\n\n const newPages = data.pages.map(page => {\n const newData = page.data.filter(message => {\n return message.id !== record.id\n })\n\n return { ...page, data: newData }\n })\n\n return { ...data, pages: newPages }\n}\n"]}
@@ -0,0 +1,4 @@
1
+ type DateProps = string | number | Date;
2
+ export declare function formatDatePreview(date?: DateProps): any;
3
+ export {};
4
+ //# sourceMappingURL=date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAGA,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;AAEvC,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE,SAAS,OAcjD"}
@@ -0,0 +1,23 @@
1
+ import { date as formatDate } from '@planningcenter/datetime-fmt';
2
+ import moment from 'moment-timezone';
3
+ export function formatDatePreview(date) {
4
+ if (!date)
5
+ return '';
6
+ const now = moment();
7
+ const isToday = now.isSame(date, 'day');
8
+ const isThisWeek = now.isSame(date, 'week');
9
+ const isThisYear = now.isSame(date, 'year');
10
+ if (isToday)
11
+ return moment(date).format('h:mm a');
12
+ if (isThisWeek)
13
+ return formatDate(date, { style: 'relative-short' });
14
+ if (isThisYear)
15
+ return formatDate(date, { style: 'abbreviated' });
16
+ // TODO: Org date format
17
+ return formatShorterDate(date);
18
+ }
19
+ const formatShorterDate = (date) => {
20
+ // Drop the century from the year
21
+ return formatDate(date, { style: 'short', year: true }).replace(/20(\d{2})/, '$1');
22
+ };
23
+ //# sourceMappingURL=date.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.js","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,8BAA8B,CAAA;AACjE,OAAO,MAAM,MAAM,iBAAiB,CAAA;AAIpC,MAAM,UAAU,iBAAiB,CAAC,IAAgB;IAChD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IAEpB,MAAM,GAAG,GAAG,MAAM,EAAE,CAAA;IACpB,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACvC,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAC3C,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAE3C,IAAI,OAAO;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IACjD,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAA;IACpE,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAA;IAEjE,wBAAwB;IACxB,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAA;AAChC,CAAC;AAED,MAAM,iBAAiB,GAAG,CAAC,IAAe,EAAE,EAAE;IAC5C,iCAAiC;IACjC,OAAO,UAAU,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAA;AACpF,CAAC,CAAA","sourcesContent":["import { date as formatDate } from '@planningcenter/datetime-fmt'\nimport moment from 'moment-timezone'\n\ntype DateProps = string | number | Date\n\nexport function formatDatePreview(date?: DateProps) {\n if (!date) return ''\n\n const now = moment()\n const isToday = now.isSame(date, 'day')\n const isThisWeek = now.isSame(date, 'week')\n const isThisYear = now.isSame(date, 'year')\n\n if (isToday) return moment(date).format('h:mm a')\n if (isThisWeek) return formatDate(date, { style: 'relative-short' })\n if (isThisYear) return formatDate(date, { style: 'abbreviated' })\n\n // TODO: Org date format\n return formatShorterDate(date)\n}\n\nconst formatShorterDate = (date: DateProps) => {\n // Drop the century from the year\n return formatDate(date, { style: 'short', year: true }).replace(/20(\\d{2})/, '$1')\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "2.0.1-rc.0",
3
+ "version": "2.1.0-rc.1",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -24,13 +24,17 @@
24
24
  "react-native-url-polyfill": "^2.0.0"
25
25
  },
26
26
  "peerDependencies": {
27
- "@planningcenter/icons": "^15.17.0",
27
+ "@planningcenter/datetime-fmt": ">=1.0.0",
28
+ "@planningcenter/icons": ">=15.0.0",
29
+ "@planningcenter/jolt-client": ">=2.0.0",
28
30
  "@react-navigation/elements": "*",
29
31
  "@react-navigation/native": ">=7.0.0",
30
32
  "@react-navigation/native-stack": ">=7.0.0",
31
33
  "@tanstack/react-query": "^5.0.0",
32
34
  "color": "^3.1.2",
33
35
  "lodash": "*",
36
+ "moment": ">=2.0.0",
37
+ "moment-timezone": ">=2.0.0",
34
38
  "react": "*",
35
39
  "react-native": "*",
36
40
  "react-native-device-info": "*",
@@ -47,5 +51,5 @@
47
51
  "prettier": "^3.4.2",
48
52
  "typescript": "<5.6.0"
49
53
  },
50
- "gitHead": "06d7daa82162274562468d89e060fa707cbbd7a3"
54
+ "gitHead": "f75cb36bdd241b841ea1279d73c772ed5210bb8a"
51
55
  }
@@ -1,4 +1,4 @@
1
- import { addRecordInPagesData, updateRecordInPagesData } from '../../../utils/'
1
+ import { deleteRecordInPagesData, updateRecordInPagesData } from '../../../utils/'
2
2
 
3
3
  const data = {
4
4
  pageParams: {},
@@ -105,6 +105,7 @@ describe('updateRecordInPagesData', () => {
105
105
  pages: [
106
106
  {
107
107
  data: [
108
+ { id, type: 'Message', text: `updated example ${id}` },
108
109
  { id: '1', type: 'Message', text: 'message 1' },
109
110
  { id: '2', type: 'Message', text: 'message 2' },
110
111
  ],
@@ -116,7 +117,6 @@ describe('updateRecordInPagesData', () => {
116
117
  data: [
117
118
  { id: '3', type: 'Message', text: 'message 3' },
118
119
  { id: '4', type: 'Message', text: 'message 4' },
119
- { id, type: 'Message', text: `updated example ${id}` },
120
120
  ],
121
121
  included: [],
122
122
  links: {},
@@ -127,49 +127,14 @@ describe('updateRecordInPagesData', () => {
127
127
  })
128
128
  })
129
129
 
130
- describe('addRecordInPagesData', () => {
131
- it('should add records that do not yet exist', () => {
132
- const id = '7'
130
+ describe('deleteRecordInPagesData', () => {
131
+ it('should delete records from page', () => {
132
+ const id = '4'
133
133
  const record = createRecord({ id })
134
134
 
135
- const result = addRecordInPagesData<typeof record>({
136
- data,
137
- record,
138
- })
139
-
140
- expect(result).toEqual({
141
- pageParams: {},
142
- pages: [
143
- {
144
- data: [
145
- { id: '1', type: 'Message', text: 'message 1' },
146
- { id: '2', type: 'Message', text: 'message 2' },
147
- ],
148
- included: [],
149
- links: {},
150
- meta: { count: 2, totalCount: 2 },
151
- },
152
- {
153
- data: [
154
- { id: '3', type: 'Message', text: 'message 3' },
155
- { id: '4', type: 'Message', text: 'message 4' },
156
- record,
157
- ],
158
- included: [],
159
- links: {},
160
- meta: { count: 2, totalCount: 2 },
161
- },
162
- ],
163
- })
164
- })
165
-
166
- it('should add the record in the pages data and update with a custom processRecord function', () => {
167
- const id = '8'
168
- const record = createRecord({ id })
169
- const result = addRecordInPagesData<typeof record>({
135
+ const result = deleteRecordInPagesData<typeof record>({
170
136
  data,
171
137
  record,
172
- processRecord: r => ({ ...r, text: 'updated ' + r.text }),
173
138
  })
174
139
 
175
140
  expect(result).toEqual({
@@ -185,11 +150,7 @@ describe('addRecordInPagesData', () => {
185
150
  meta: { count: 2, totalCount: 2 },
186
151
  },
187
152
  {
188
- data: [
189
- { id: '3', type: 'Message', text: 'message 3' },
190
- { id: '4', type: 'Message', text: 'message 4' },
191
- { ...record, text: `updated example ${id}` },
192
- ],
153
+ data: [{ id: '3', type: 'Message', text: 'message 3' }],
193
154
  included: [],
194
155
  links: {},
195
156
  meta: { count: 2, totalCount: 2 },
@@ -7,6 +7,7 @@ import { MessageReaction } from '../../components/conversation/message_reaction'
7
7
  import { Avatar, Text } from '../../components/display'
8
8
  import { useTheme } from '../../hooks'
9
9
  import { MessageResource } from '../../types'
10
+ import { ReactionCountResource } from '../../types/resources/reaction'
10
11
 
11
12
  /** Message
12
13
  * Component for display of a message within a conversation list
@@ -21,9 +22,13 @@ export function Message(props: MessageResource & { conversation_id: string }) {
21
22
  conversation_id,
22
23
  })
23
24
  }
24
- // TODO: open the reaction screen to show who reacted
25
- const handleReactionPress = handleMessagePress
26
-
25
+ const handleReactionPress = (reaction: ReactionCountResource) => {
26
+ navigation.navigate('Reactions', {
27
+ message_id: props.id,
28
+ conversation_id,
29
+ reaction_value: reaction.value,
30
+ })
31
+ }
27
32
  if (!text) return null
28
33
 
29
34
  return (
@@ -19,12 +19,16 @@ export function MessageReaction({
19
19
  onPress,
20
20
  }: {
21
21
  reaction: ReactionCountResource
22
- onPress: () => void
22
+ onPress: (_reaction: ReactionCountResource) => void
23
23
  }) {
24
24
  const styles = useReactionStyles(reaction)
25
25
 
26
26
  return (
27
- <PlatformPressable key={reaction.value} style={styles.reaction} onPress={onPress}>
27
+ <PlatformPressable
28
+ key={reaction.value}
29
+ style={styles.reaction}
30
+ onPress={() => onPress(reaction)}
31
+ >
28
32
  <Text style={styles.reactionEmoji}>{REACTION_EMOJIS[reaction.value]}</Text>
29
33
  <Text style={styles.reactionText}>{reaction.count}</Text>
30
34
  </PlatformPressable>
@@ -1,45 +1,42 @@
1
1
  import { useNavigation } from '@react-navigation/native'
2
2
  import React from 'react'
3
- import { FlatList, Pressable, StyleSheet } from 'react-native'
3
+ import { FlatList, Pressable, StyleSheet, View } from 'react-native'
4
4
  import { useTheme } from '../hooks'
5
- import { useSuspensePaginator } from '../hooks/use_suspense_api'
6
- import { ConversationResource } from '../types'
7
- import { GetRequest } from '../utils/client/types'
8
- import { Heading, Text } from './display'
5
+ import { useConversationsJoltEvents } from '../hooks/use_conversation_jolt_events'
6
+ import { useConversations } from '../hooks/use_conversations'
7
+ import { formatDatePreview } from '../utils/date'
8
+ import { AvatarGroup, Badge, Heading, Text, TextButton } from './display'
9
9
 
10
10
  export const Conversations = () => {
11
11
  const styles = useStyles()
12
- const request: GetRequest = {
13
- url: '/me/conversations',
14
- data: {
15
- perPage: 20,
16
- order: '-last_message',
17
- fields: {
18
- Conversation: [
19
- 'title',
20
- 'last_message_created_at',
21
- 'last_message_author_name',
22
- 'last_message_text_preview',
23
- 'unread_count',
24
- ],
25
- },
26
- },
27
- }
28
- const { data: conversations, fetchNextPage } = useSuspensePaginator<ConversationResource>(request)
12
+
13
+ const { conversations, fetchNextPage, refetch, isRefetching } = useConversations()
29
14
 
30
15
  // TODO: Filter using the API
31
16
  const nonEmptyConversations = conversations.filter(c => c.lastMessageTextPreview) || []
32
-
33
17
  const navigation = useNavigation()
34
18
 
19
+ useConversationsJoltEvents()
20
+
35
21
  return (
36
22
  <FlatList
37
23
  data={nonEmptyConversations}
38
24
  contentContainerStyle={styles.container}
39
25
  style={styles.scrollView}
26
+ onRefresh={refetch}
27
+ refreshing={isRefetching}
28
+ ListHeaderComponent={
29
+ <View style={styles.header}>
30
+ <Heading numberOfLines={1} variant="h2">
31
+ Conversations
32
+ </Heading>
33
+ <TextButton>Mark all read</TextButton>
34
+ </View>
35
+ }
40
36
  ListEmptyComponent={<Text>No conversations found</Text>}
41
37
  renderItem={({ item }) => (
42
38
  <Pressable
39
+ style={styles.conversation}
43
40
  onPress={() =>
44
41
  navigation.navigate('Conversation', {
45
42
  conversation_id: item.id,
@@ -47,12 +44,30 @@ export const Conversations = () => {
47
44
  })
48
45
  }
49
46
  >
50
- <Heading numberOfLines={1} variant="h3">
51
- {item.title}
52
- </Heading>
53
- <Text style={styles.listItem}>
54
- {item.lastMessageAuthorName}: {item.lastMessageTextPreview}
55
- </Text>
47
+ <AvatarGroup size="lg" sourceUris={item.previewAvatarUrls || []} />
48
+ <View style={styles.conversationBody}>
49
+ <Heading numberOfLines={1} variant="h3" style={styles.conversationTitle}>
50
+ {item.title}
51
+ </Heading>
52
+ <Text style={styles.listItem} numberOfLines={2}>
53
+ {item.lastMessageAuthorName}: {item.lastMessageTextPreview}
54
+ </Text>
55
+ <View style={styles.conversationBadges}>
56
+ {item.badges?.map(badge => (
57
+ <Badge
58
+ key={badge.text}
59
+ variant="meta"
60
+ productLogoName={badge.appName}
61
+ label={badge.pcoResourceType}
62
+ metaLabel={badge.text || ''}
63
+ />
64
+ ))}
65
+ </View>
66
+ </View>
67
+ <View style={styles.conversationExtra}>
68
+ <Text variant="secondary">{formatDatePreview(item.lastMessageCreatedAt)}</Text>
69
+ <UnreadCountBadge count={item.unreadCount} />
70
+ </View>
56
71
  </Pressable>
57
72
  )}
58
73
  onEndReached={() => fetchNextPage()}
@@ -60,13 +75,61 @@ export const Conversations = () => {
60
75
  )
61
76
  }
62
77
 
78
+ const UnreadCountBadge = ({ count }: { count: number }) => {
79
+ const styles = useStyles()
80
+ const displayCount = count > 99 ? '99+' : count
81
+
82
+ if (count === 0) return null
83
+
84
+ return (
85
+ <Text variant="tertiary" style={styles.unreadCountBadge}>
86
+ {displayCount}
87
+ </Text>
88
+ )
89
+ }
90
+
63
91
  const useStyles = () => {
64
92
  const { colors } = useTheme()
65
93
 
66
94
  return StyleSheet.create({
67
- scrollView: { flex: 1, backgroundColor: colors.fillColorNeutral090 },
68
- container: { gap: 8, padding: 16 },
69
- foo: { fontSize: 24, color: colors.testColor },
95
+ header: {
96
+ flexDirection: 'row',
97
+ justifyContent: 'space-between',
98
+ paddingTop: 8,
99
+ paddingBottom: 8,
100
+ paddingHorizontal: 16,
101
+ },
102
+ scrollView: { flex: 1 },
103
+ container: { gap: 8, paddingVertical: 16 },
70
104
  listItem: { color: colors.fillColorNeutral020 },
105
+ conversation: {
106
+ flexDirection: 'row',
107
+ gap: 8,
108
+ borderBottomWidth: 1,
109
+ borderBottomColor: colors.fillColorNeutral060,
110
+ paddingTop: 4,
111
+ paddingBottom: 12,
112
+ paddingHorizontal: 16,
113
+ },
114
+ conversationTitle: {},
115
+ conversationBody: {
116
+ flex: 1,
117
+ rowGap: 2,
118
+ },
119
+ conversationExtra: {
120
+ rowGap: 2,
121
+ },
122
+ conversationBadges: {
123
+ marginTop: 4,
124
+ alignItems: 'flex-start',
125
+ },
126
+ unreadCountBadge: {
127
+ alignSelf: 'flex-end',
128
+ backgroundColor: colors.interaction,
129
+ paddingVertical: 0,
130
+ paddingHorizontal: 10,
131
+ borderRadius: 24,
132
+ color: 'white',
133
+ },
71
134
  })
72
135
  }
@@ -54,12 +54,7 @@ type VariantStyles = Record<
54
54
  }
55
55
  >
56
56
 
57
- const LOGO_NAMES = {
58
- groups: 'groups',
59
- services: 'services',
60
- } as const
61
-
62
- type PoductLogoNameUnion = (typeof LOGO_NAMES)[keyof typeof LOGO_NAMES]
57
+ type ProductLogoName = 'Groups' | 'Services' | 'groups' | 'services'
63
58
 
64
59
  const PRODUCT_LOGO_COMPONENT_MAP = {
65
60
  groups: GroupsLogo,
@@ -90,7 +85,7 @@ interface BadgeProps {
90
85
  /**
91
86
  * Adds a product logo to the left of the text.
92
87
  */
93
- productLogoName?: PoductLogoNameUnion
88
+ productLogoName?: ProductLogoName
94
89
  /**
95
90
  * Shows an icon of the user choice to the left of the text.
96
91
  */
@@ -123,7 +118,7 @@ export function Badge({
123
118
  const hasMetaLabel = Boolean(metaLabel)
124
119
 
125
120
  const showLogo = showBadgeLogo && productLogoName && isMeta
126
- const ProductLogoSvg = showLogo && PRODUCT_LOGO_COMPONENT_MAP[productLogoName]
121
+ const ProductLogoSvg = showLogo && PRODUCT_LOGO_COMPONENT_MAP[productLogoName?.toLowerCase()]
127
122
  const badgeLabel = isMetaSubtle && hasMetaLabel ? `${label}:` : label
128
123
 
129
124
  return (