@planningcenter/chat-react-native 4.4.1-qa-staging.25 → 4.4.1-qa-staging.27

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 (80) hide show
  1. package/build/components/conversations/conversations.d.ts.map +1 -1
  2. package/build/components/conversations/conversations.js +3 -2
  3. package/build/components/conversations/conversations.js.map +1 -1
  4. package/build/components/display/filter_dropdown.d.ts +6 -1
  5. package/build/components/display/filter_dropdown.d.ts.map +1 -1
  6. package/build/components/display/filter_dropdown.js +57 -35
  7. package/build/components/display/filter_dropdown.js.map +1 -1
  8. package/build/components/display/index.d.ts +1 -0
  9. package/build/components/display/index.d.ts.map +1 -1
  10. package/build/components/display/index.js +1 -0
  11. package/build/components/display/index.js.map +1 -1
  12. package/build/components/display/popover_menu_view.d.ts +8 -0
  13. package/build/components/display/popover_menu_view.d.ts.map +1 -0
  14. package/build/components/display/popover_menu_view.js +482 -0
  15. package/build/components/display/popover_menu_view.js.map +1 -0
  16. package/build/contexts/conversations_context.d.ts.map +1 -1
  17. package/build/contexts/conversations_context.js +20 -17
  18. package/build/contexts/conversations_context.js.map +1 -1
  19. package/build/hooks/use_clear_conversation_filters.d.ts.map +1 -1
  20. package/build/hooks/use_clear_conversation_filters.js +2 -1
  21. package/build/hooks/use_clear_conversation_filters.js.map +1 -1
  22. package/build/hooks/use_conversations_cache.d.ts +1 -1
  23. package/build/hooks/use_conversations_cache.d.ts.map +1 -1
  24. package/build/hooks/use_conversations_cache.js +64 -55
  25. package/build/hooks/use_conversations_cache.js.map +1 -1
  26. package/build/hooks/use_conversations_jolt_events.js +2 -2
  27. package/build/hooks/use_conversations_jolt_events.js.map +1 -1
  28. package/build/hooks/use_jolt.d.ts.map +1 -1
  29. package/build/hooks/use_jolt.js +37 -9
  30. package/build/hooks/use_jolt.js.map +1 -1
  31. package/build/screens/conversation_filters/context/conversation_filter_context.d.ts.map +1 -1
  32. package/build/screens/conversation_filters/context/conversation_filter_context.js +8 -4
  33. package/build/screens/conversation_filters/context/conversation_filter_context.js.map +1 -1
  34. package/build/screens/conversation_filters/screen_props.d.ts +1 -0
  35. package/build/screens/conversation_filters/screen_props.d.ts.map +1 -1
  36. package/build/screens/conversation_filters/screen_props.js.map +1 -1
  37. package/build/screens/conversations/components/filter_dropdown_list_header_component.d.ts.map +1 -1
  38. package/build/screens/conversations/components/filter_dropdown_list_header_component.js +251 -185
  39. package/build/screens/conversations/components/filter_dropdown_list_header_component.js.map +1 -1
  40. package/build/screens/conversations/conversations_screen.d.ts +1 -0
  41. package/build/screens/conversations/conversations_screen.d.ts.map +1 -1
  42. package/build/screens/conversations/conversations_screen.js +1 -2
  43. package/build/screens/conversations/conversations_screen.js.map +1 -1
  44. package/build/screens/design_system_screen.d.ts.map +1 -1
  45. package/build/screens/design_system_screen.js +289 -201
  46. package/build/screens/design_system_screen.js.map +1 -1
  47. package/build/utils/native_adapters/menu.d.ts +1 -2
  48. package/build/utils/native_adapters/menu.d.ts.map +1 -1
  49. package/build/utils/native_adapters/menu.js +2 -41
  50. package/build/utils/native_adapters/menu.js.map +1 -1
  51. package/build/utils/request/conversation.d.ts +2 -1
  52. package/build/utils/request/conversation.d.ts.map +1 -1
  53. package/build/utils/request/conversation.js +4 -1
  54. package/build/utils/request/conversation.js.map +1 -1
  55. package/package.json +3 -3
  56. package/src/components/conversations/__tests__/conversations_blank_state.test.tsx +1 -0
  57. package/src/components/conversations/conversations.tsx +12 -2
  58. package/src/components/display/__tests__/filter_dropdown.test.tsx +18 -2
  59. package/src/components/display/__tests__/popover_menu_view.test.tsx +63 -0
  60. package/src/components/display/filter_dropdown.tsx +11 -3
  61. package/src/components/display/index.ts +1 -0
  62. package/src/components/display/popover_menu_view.tsx +239 -0
  63. package/src/contexts/conversations_context.tsx +8 -1
  64. package/src/hooks/__tests__/use_conversations_jolt_events.test.tsx +135 -0
  65. package/src/hooks/__tests__/use_jolt.test.tsx +76 -1
  66. package/src/hooks/use_clear_conversation_filters.ts +1 -0
  67. package/src/hooks/use_conversations_cache.ts +29 -24
  68. package/src/hooks/use_conversations_jolt_events.ts +2 -2
  69. package/src/hooks/use_jolt.ts +26 -2
  70. package/src/screens/conversation_filters/context/conversation_filter_context.tsx +4 -0
  71. package/src/screens/conversation_filters/screen_props.ts +1 -0
  72. package/src/screens/conversations/__tests__/conversations_screen.test.tsx +2 -12
  73. package/src/screens/conversations/components/__tests__/filter_dropdown_list_header_component.test.tsx +74 -7
  74. package/src/screens/conversations/components/filter_dropdown_list_header_component.tsx +55 -14
  75. package/src/screens/conversations/conversations_screen.tsx +2 -2
  76. package/src/screens/design_system_screen.tsx +34 -1
  77. package/src/utils/__tests__/conversation.test.ts +1 -0
  78. package/src/utils/native_adapters/menu.tsx +4 -18
  79. package/src/utils/request/conversation.ts +4 -0
  80. package/src/utils/native_adapters/__tests__/menu.test.tsx +0 -46
@@ -32,8 +32,7 @@ export interface MenuViewProps {
32
32
  }
33
33
  export type MenuView = React.ComponentType<MenuViewProps>;
34
34
  export declare class MenuAdapter {
35
- View: MenuView;
36
- isConfigured: boolean;
35
+ View?: MenuView;
37
36
  constructor(methods?: {
38
37
  View: MenuView;
39
38
  });
@@ -1 +1 @@
1
- {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/menu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAA;AACxC,OAAO,EAAE,SAAS,EAAQ,SAAS,EAAE,MAAM,cAAc,CAAA;AASzD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,KAAK,CAAA;AAE1C,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,UAAU,CAAC,EAAE,oBAAoB,CAAA;IACjC,UAAU,CAAC,EAAE,UAAU,EAAE,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IAChD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,CAAA;AAkBzD,qBAAa,WAAW;IACtB,IAAI,EAAE,QAAQ,CAAA;IACd,YAAY,EAAE,OAAO,CAAA;gBAET,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE;CAIzC"}
1
+ {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/menu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AASnD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,KAAK,CAAA;AAE1C,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,UAAU,CAAC,EAAE,oBAAoB,CAAA;IACjC,UAAU,CAAC,EAAE,UAAU,EAAE,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IAChD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,CAAA;AAMzD,qBAAa,WAAW;IACtB,IAAI,CAAC,EAAE,QAAQ,CAAA;gBAEH,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE;CAGzC"}
@@ -1,56 +1,17 @@
1
- import { c as _c } from "react/compiler-runtime";
2
- import React, { useEffect } from 'react';
3
- import { View } from 'react-native';
4
-
5
1
  // =================================
6
2
  // ====== Types ====================
7
3
  // =================================
8
4
 
9
5
  // Mirrors the props shared by `@expo/ui/community/menu` and
10
6
  // `@react-native-menu/menu`, so a host can pass either `MenuView` straight in.
11
- import { jsx as _jsx } from "react/jsx-runtime";
7
+
12
8
  // =================================
13
9
  // ====== Adapter ==================
14
10
  // =================================
15
11
 
16
- function UnconfiguredMenuView(t0) {
17
- const $ = _c(5);
18
- const {
19
- children,
20
- style,
21
- testID
22
- } = t0;
23
- let t1;
24
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
25
- t1 = [];
26
- $[0] = t1;
27
- } else {
28
- t1 = $[0];
29
- }
30
- useEffect(_temp, t1);
31
- let t2;
32
- if ($[1] !== children || $[2] !== style || $[3] !== testID) {
33
- t2 = /*#__PURE__*/_jsx(View, {
34
- style: style,
35
- testID: testID,
36
- children: children
37
- });
38
- $[1] = children;
39
- $[2] = style;
40
- $[3] = testID;
41
- $[4] = t2;
42
- } else {
43
- t2 = $[4];
44
- }
45
- return t2;
46
- }
47
- function _temp() {
48
- console.warn("ChatAdapters.configure() must include a menu adapter before rendering a menu");
49
- }
50
12
  export class MenuAdapter {
51
13
  constructor(methods) {
52
- this.View = methods?.View ?? UnconfiguredMenuView;
53
- this.isConfigured = !!methods?.View;
14
+ this.View = methods?.View;
54
15
  }
55
16
  }
56
17
  //# sourceMappingURL=menu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"menu.js","names":["React","useEffect","View","jsx","_jsx","UnconfiguredMenuView","t0","$","_c","children","style","testID","t1","Symbol","for","_temp","t2","console","warn","MenuAdapter","constructor","methods","isConfigured"],"sources":["../../../src/utils/native_adapters/menu.tsx"],"sourcesContent":["import React, { useEffect } from 'react'\nimport { StyleProp, View, ViewStyle } from 'react-native'\n\n// =================================\n// ====== Types ====================\n// =================================\n\n// Mirrors the props shared by `@expo/ui/community/menu` and\n// `@react-native-menu/menu`, so a host can pass either `MenuView` straight in.\n\nexport type MenuActionState = 'on' | 'off'\n\nexport interface MenuActionAttributes {\n destructive?: boolean\n disabled?: boolean\n hidden?: boolean\n}\n\nexport interface MenuAction {\n id?: string\n title: string\n state?: MenuActionState\n attributes?: MenuActionAttributes\n subactions?: MenuAction[]\n displayInline?: boolean\n}\n\nexport interface MenuActionEvent {\n nativeEvent: { event: string }\n}\n\nexport interface MenuViewProps {\n actions: MenuAction[]\n onPressAction?: (event: MenuActionEvent) => void\n onOpenMenu?: () => void\n onCloseMenu?: () => void\n shouldOpenOnLongPress?: boolean\n title?: string\n style?: StyleProp<ViewStyle>\n testID?: string\n children?: React.ReactNode\n}\n\nexport type MenuView = React.ComponentType<MenuViewProps>\n\n// =================================\n// ====== Adapter ==================\n// =================================\n\nfunction UnconfiguredMenuView({ children, style, testID }: MenuViewProps) {\n useEffect(() => {\n console.warn('ChatAdapters.configure() must include a menu adapter before rendering a menu')\n }, [])\n\n return (\n <View style={style} testID={testID}>\n {children}\n </View>\n )\n}\n\nexport class MenuAdapter {\n View: MenuView\n isConfigured: boolean\n\n constructor(methods?: { View: MenuView }) {\n this.View = methods?.View ?? UnconfiguredMenuView\n this.isConfigured = !!methods?.View\n }\n}\n"],"mappings":";AAAA,OAAOA,KAAK,IAAIC,SAAS,QAAQ,OAAO;AACxC,SAAoBC,IAAI,QAAmB,cAAc;;AAEzD;AACA;AACA;;AAEA;AACA;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAqCA;AACA;AACA;;AAEA,SAAAC,qBAAAC,EAAA;EAAA,MAAAC,CAAA,GAAAC,EAAA;EAA8B;IAAAC,QAAA;IAAAC,KAAA;IAAAC;EAAA,IAAAL,EAA0C;EAAA,IAAAM,EAAA;EAAA,IAAAL,CAAA,QAAAM,MAAA,CAAAC,GAAA;IAGnEF,EAAA,KAAE;IAAAL,CAAA,MAAAK,EAAA;EAAA;IAAAA,EAAA,GAAAL,CAAA;EAAA;EAFLN,SAAS,CAACc,KAET,EAAEH,EAAE,CAAC;EAAA,IAAAI,EAAA;EAAA,IAAAT,CAAA,QAAAE,QAAA,IAAAF,CAAA,QAAAG,KAAA,IAAAH,CAAA,QAAAI,MAAA;IAGJK,EAAA,gBAAAZ,IAAA,CAACF,IAAI;MAAQQ,KAAK,EAALA,KAAK;MAAUC,MAAM,EAANA,MAAM;MAAAF,QAAA,EAC/BA;IAAQ,CACL,CAAC;IAAAF,CAAA,MAAAE,QAAA;IAAAF,CAAA,MAAAG,KAAA;IAAAH,CAAA,MAAAI,MAAA;IAAAJ,CAAA,MAAAS,EAAA;EAAA;IAAAA,EAAA,GAAAT,CAAA;EAAA;EAAA,OAFPS,EAEO;AAAA;AARX,SAAAD,MAAA;EAEIE,OAAO,CAAAC,IAAK,CAAC,8EAA8E,CAAC;AAAA;AAUhG,OAAO,MAAMC,WAAW,CAAC;EAIvBC,WAAWA,CAACC,OAA4B,EAAE;IACxC,IAAI,CAACnB,IAAI,GAAGmB,OAAO,EAAEnB,IAAI,IAAIG,oBAAoB;IACjD,IAAI,CAACiB,YAAY,GAAG,CAAC,CAACD,OAAO,EAAEnB,IAAI;EACrC;AACF","ignoreList":[]}
1
+ {"version":3,"file":"menu.js","names":["MenuAdapter","constructor","methods","View"],"sources":["../../../src/utils/native_adapters/menu.tsx"],"sourcesContent":["import React from 'react'\nimport { StyleProp, ViewStyle } from 'react-native'\n\n// =================================\n// ====== Types ====================\n// =================================\n\n// Mirrors the props shared by `@expo/ui/community/menu` and\n// `@react-native-menu/menu`, so a host can pass either `MenuView` straight in.\n\nexport type MenuActionState = 'on' | 'off'\n\nexport interface MenuActionAttributes {\n destructive?: boolean\n disabled?: boolean\n hidden?: boolean\n}\n\nexport interface MenuAction {\n id?: string\n title: string\n state?: MenuActionState\n attributes?: MenuActionAttributes\n subactions?: MenuAction[]\n displayInline?: boolean\n}\n\nexport interface MenuActionEvent {\n nativeEvent: { event: string }\n}\n\nexport interface MenuViewProps {\n actions: MenuAction[]\n onPressAction?: (event: MenuActionEvent) => void\n onOpenMenu?: () => void\n onCloseMenu?: () => void\n shouldOpenOnLongPress?: boolean\n title?: string\n style?: StyleProp<ViewStyle>\n testID?: string\n children?: React.ReactNode\n}\n\nexport type MenuView = React.ComponentType<MenuViewProps>\n\n// =================================\n// ====== Adapter ==================\n// =================================\n\nexport class MenuAdapter {\n View?: MenuView\n\n constructor(methods?: { View: MenuView }) {\n this.View = methods?.View\n }\n}\n"],"mappings":"AAGA;AACA;AACA;;AAEA;AACA;;AAqCA;AACA;AACA;;AAEA,OAAO,MAAMA,WAAW,CAAC;EAGvBC,WAAWA,CAACC,OAA4B,EAAE;IACxC,IAAI,CAACC,IAAI,GAAGD,OAAO,EAAEC,IAAI;EAC3B;AACF","ignoreList":[]}
@@ -6,6 +6,7 @@ export interface ConversationRequestArgs {
6
6
  group_source_app_name?: string;
7
7
  show_direct_messages?: boolean;
8
8
  show_archived?: boolean;
9
+ show_unread?: boolean;
9
10
  }
10
- export declare const getConversationsRequestArgs: ({ gids, chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived, }?: Partial<ConversationRequestArgs>) => GetRequest;
11
+ export declare const getConversationsRequestArgs: ({ gids, chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived, show_unread, }?: Partial<ConversationRequestArgs>) => GetRequest;
11
12
  //# sourceMappingURL=conversation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../../src/utils/request/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAA;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,eAAO,MAAM,2BAA2B,GAAI,6FAMzC,OAAO,CAAC,uBAAuB,CAAM,KAAG,UAuD1C,CAAA"}
1
+ {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../../src/utils/request/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAA;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,eAAO,MAAM,2BAA2B,GAAI,0GAOzC,OAAO,CAAC,uBAAuB,CAAM,KAAG,UAyD1C,CAAA"}
@@ -3,13 +3,16 @@ export const getConversationsRequestArgs = ({
3
3
  chat_group_graph_id,
4
4
  group_source_app_name,
5
5
  show_direct_messages,
6
- show_archived
6
+ show_archived,
7
+ show_unread
7
8
  } = {}) => {
8
9
  let filter;
9
10
  if (chat_group_graph_id) {
10
11
  filter = 'mine_or_not_empty,group';
11
12
  } else if (show_archived) {
12
13
  filter = 'mine_or_not_empty,archived';
14
+ } else if (show_unread) {
15
+ filter = 'mine_or_not_empty,unread';
13
16
  } else if (show_direct_messages) {
14
17
  filter = 'mine_or_not_empty,direct_message';
15
18
  } else if (group_source_app_name) {
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.js","names":["getConversationsRequestArgs","gids","chat_group_graph_id","group_source_app_name","show_direct_messages","show_archived","filter","url","data","perPage","order","group","fields","Conversation","AnalyticsMetadata","ConversationBadge","include"],"sources":["../../../src/utils/request/conversation.ts"],"sourcesContent":["import { GraphId } from '../../types/resources/group_resource'\nimport { GetRequest } from '../client/types'\n\nexport interface ConversationRequestArgs {\n gids: string\n chat_group_graph_id?: GraphId\n group_source_app_name?: string\n show_direct_messages?: boolean\n show_archived?: boolean\n}\n\nexport const getConversationsRequestArgs = ({\n gids,\n chat_group_graph_id,\n group_source_app_name,\n show_direct_messages,\n show_archived,\n}: Partial<ConversationRequestArgs> = {}): GetRequest => {\n let filter: string\n if (chat_group_graph_id) {\n filter = 'mine_or_not_empty,group'\n } else if (show_archived) {\n filter = 'mine_or_not_empty,archived'\n } else if (show_direct_messages) {\n filter = 'mine_or_not_empty,direct_message'\n } else if (group_source_app_name) {\n filter = 'mine_or_not_empty,group_source_app_name'\n } else {\n filter = 'mine_or_not_empty'\n }\n\n return {\n url: '/me/conversations',\n data: {\n perPage: 20,\n order: '-last_message',\n filter,\n gids,\n group: chat_group_graph_id,\n group_source_app_name,\n fields: {\n Conversation: [\n 'created_at',\n 'badges',\n 'conversation_preview',\n 'disabled',\n 'disabled_reason',\n 'disabled_message',\n 'groups',\n 'last_message_author_id',\n 'last_message_author_name',\n 'last_message_created_at',\n 'last_message_text_preview',\n 'preview_avatar_urls',\n 'custom_avatar_type',\n 'custom_avatar_key',\n 'custom_avatar_color',\n 'custom_avatar_image_url',\n 'member_ability',\n 'muted',\n 'replies_disabled',\n 'title',\n 'unread_count',\n 'unread_reaction',\n 'updated_at',\n ],\n AnalyticsMetadata: ['metadata'],\n ConversationBadge: ['app_name', 'pco_resource_type', 'text'],\n },\n include: ['badges', 'analytics_metadata'],\n },\n }\n}\n"],"mappings":"AAWA,OAAO,MAAMA,2BAA2B,GAAGA,CAAC;EAC1CC,IAAI;EACJC,mBAAmB;EACnBC,qBAAqB;EACrBC,oBAAoB;EACpBC;AACgC,CAAC,GAAG,CAAC,CAAC,KAAiB;EACvD,IAAIC,MAAc;EAClB,IAAIJ,mBAAmB,EAAE;IACvBI,MAAM,GAAG,yBAAyB;EACpC,CAAC,MAAM,IAAID,aAAa,EAAE;IACxBC,MAAM,GAAG,4BAA4B;EACvC,CAAC,MAAM,IAAIF,oBAAoB,EAAE;IAC/BE,MAAM,GAAG,kCAAkC;EAC7C,CAAC,MAAM,IAAIH,qBAAqB,EAAE;IAChCG,MAAM,GAAG,yCAAyC;EACpD,CAAC,MAAM;IACLA,MAAM,GAAG,mBAAmB;EAC9B;EAEA,OAAO;IACLC,GAAG,EAAE,mBAAmB;IACxBC,IAAI,EAAE;MACJC,OAAO,EAAE,EAAE;MACXC,KAAK,EAAE,eAAe;MACtBJ,MAAM;MACNL,IAAI;MACJU,KAAK,EAAET,mBAAmB;MAC1BC,qBAAqB;MACrBS,MAAM,EAAE;QACNC,YAAY,EAAE,CACZ,YAAY,EACZ,QAAQ,EACR,sBAAsB,EACtB,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,wBAAwB,EACxB,0BAA0B,EAC1B,yBAAyB,EACzB,2BAA2B,EAC3B,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,gBAAgB,EAChB,OAAO,EACP,kBAAkB,EAClB,OAAO,EACP,cAAc,EACd,iBAAiB,EACjB,YAAY,CACb;QACDC,iBAAiB,EAAE,CAAC,UAAU,CAAC;QAC/BC,iBAAiB,EAAE,CAAC,UAAU,EAAE,mBAAmB,EAAE,MAAM;MAC7D,CAAC;MACDC,OAAO,EAAE,CAAC,QAAQ,EAAE,oBAAoB;IAC1C;EACF,CAAC;AACH,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"conversation.js","names":["getConversationsRequestArgs","gids","chat_group_graph_id","group_source_app_name","show_direct_messages","show_archived","show_unread","filter","url","data","perPage","order","group","fields","Conversation","AnalyticsMetadata","ConversationBadge","include"],"sources":["../../../src/utils/request/conversation.ts"],"sourcesContent":["import { GraphId } from '../../types/resources/group_resource'\nimport { GetRequest } from '../client/types'\n\nexport interface ConversationRequestArgs {\n gids: string\n chat_group_graph_id?: GraphId\n group_source_app_name?: string\n show_direct_messages?: boolean\n show_archived?: boolean\n show_unread?: boolean\n}\n\nexport const getConversationsRequestArgs = ({\n gids,\n chat_group_graph_id,\n group_source_app_name,\n show_direct_messages,\n show_archived,\n show_unread,\n}: Partial<ConversationRequestArgs> = {}): GetRequest => {\n let filter: string\n if (chat_group_graph_id) {\n filter = 'mine_or_not_empty,group'\n } else if (show_archived) {\n filter = 'mine_or_not_empty,archived'\n } else if (show_unread) {\n filter = 'mine_or_not_empty,unread'\n } else if (show_direct_messages) {\n filter = 'mine_or_not_empty,direct_message'\n } else if (group_source_app_name) {\n filter = 'mine_or_not_empty,group_source_app_name'\n } else {\n filter = 'mine_or_not_empty'\n }\n\n return {\n url: '/me/conversations',\n data: {\n perPage: 20,\n order: '-last_message',\n filter,\n gids,\n group: chat_group_graph_id,\n group_source_app_name,\n fields: {\n Conversation: [\n 'created_at',\n 'badges',\n 'conversation_preview',\n 'disabled',\n 'disabled_reason',\n 'disabled_message',\n 'groups',\n 'last_message_author_id',\n 'last_message_author_name',\n 'last_message_created_at',\n 'last_message_text_preview',\n 'preview_avatar_urls',\n 'custom_avatar_type',\n 'custom_avatar_key',\n 'custom_avatar_color',\n 'custom_avatar_image_url',\n 'member_ability',\n 'muted',\n 'replies_disabled',\n 'title',\n 'unread_count',\n 'unread_reaction',\n 'updated_at',\n ],\n AnalyticsMetadata: ['metadata'],\n ConversationBadge: ['app_name', 'pco_resource_type', 'text'],\n },\n include: ['badges', 'analytics_metadata'],\n },\n }\n}\n"],"mappings":"AAYA,OAAO,MAAMA,2BAA2B,GAAGA,CAAC;EAC1CC,IAAI;EACJC,mBAAmB;EACnBC,qBAAqB;EACrBC,oBAAoB;EACpBC,aAAa;EACbC;AACgC,CAAC,GAAG,CAAC,CAAC,KAAiB;EACvD,IAAIC,MAAc;EAClB,IAAIL,mBAAmB,EAAE;IACvBK,MAAM,GAAG,yBAAyB;EACpC,CAAC,MAAM,IAAIF,aAAa,EAAE;IACxBE,MAAM,GAAG,4BAA4B;EACvC,CAAC,MAAM,IAAID,WAAW,EAAE;IACtBC,MAAM,GAAG,0BAA0B;EACrC,CAAC,MAAM,IAAIH,oBAAoB,EAAE;IAC/BG,MAAM,GAAG,kCAAkC;EAC7C,CAAC,MAAM,IAAIJ,qBAAqB,EAAE;IAChCI,MAAM,GAAG,yCAAyC;EACpD,CAAC,MAAM;IACLA,MAAM,GAAG,mBAAmB;EAC9B;EAEA,OAAO;IACLC,GAAG,EAAE,mBAAmB;IACxBC,IAAI,EAAE;MACJC,OAAO,EAAE,EAAE;MACXC,KAAK,EAAE,eAAe;MACtBJ,MAAM;MACNN,IAAI;MACJW,KAAK,EAAEV,mBAAmB;MAC1BC,qBAAqB;MACrBU,MAAM,EAAE;QACNC,YAAY,EAAE,CACZ,YAAY,EACZ,QAAQ,EACR,sBAAsB,EACtB,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,wBAAwB,EACxB,0BAA0B,EAC1B,yBAAyB,EACzB,2BAA2B,EAC3B,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,gBAAgB,EAChB,OAAO,EACP,kBAAkB,EAClB,OAAO,EACP,cAAc,EACd,iBAAiB,EACjB,YAAY,CACb;QACDC,iBAAiB,EAAE,CAAC,UAAU,CAAC;QAC/BC,iBAAiB,EAAE,CAAC,UAAU,EAAE,mBAAmB,EAAE,MAAM;MAC7D,CAAC;MACDC,OAAO,EAAE,CAAC,QAAQ,EAAE,oBAAoB;IAC1C;EACF,CAAC;AACH,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "4.4.1-qa-staging.25",
3
+ "version": "4.4.1-qa-staging.27",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,7 +33,7 @@
33
33
  "dependencies": {
34
34
  "@fortawesome/fontawesome-svg-core": "^7.2.0",
35
35
  "@fortawesome/react-native-fontawesome": "^0.3.2",
36
- "@planningcenter/emoji-keyboard": "4.4.1-qa-staging.25",
36
+ "@planningcenter/emoji-keyboard": "4.4.1-qa-staging.27",
37
37
  "lodash-inflection": "^1.5.0"
38
38
  },
39
39
  "peerDependencies": {
@@ -84,5 +84,5 @@
84
84
  "sf-symbols-typescript": "^2.2.0",
85
85
  "typescript": "~6.0.3"
86
86
  },
87
- "gitHead": "7648dba9a61c653d510be77823f6209cccdd59f5"
87
+ "gitHead": "692d0f5368988b4d99e2e79c94417a33f8cf8f55"
88
88
  }
@@ -41,6 +41,7 @@ describe('ConversationsBlankState', () => {
41
41
  group_source_app_name: undefined,
42
42
  show_direct_messages: undefined,
43
43
  show_archived: undefined,
44
+ show_unread: undefined,
44
45
  })
45
46
  })
46
47
 
@@ -31,14 +31,24 @@ export const Conversations = ({ ListHeaderComponent }: ConversationsProps) => {
31
31
  isRefetching,
32
32
  isFetched,
33
33
  hasNextPage,
34
- args: { chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived },
34
+ args: {
35
+ chat_group_graph_id,
36
+ group_source_app_name,
37
+ show_direct_messages,
38
+ show_archived,
39
+ show_unread,
40
+ },
35
41
  } = useConversationsContext()
36
42
  const navigation = useNavigation()
37
43
  const canCreateConversations = useCanCreateConversations()
38
44
  const clearFilters = useClearConversationFilters()
39
45
  const filterDropdownEnabled = useFilterDropdownEnabled()
40
46
  const isFilterApplied =
41
- !!chat_group_graph_id || !!group_source_app_name || !!show_direct_messages || !!show_archived
47
+ !!chat_group_graph_id ||
48
+ !!group_source_app_name ||
49
+ !!show_direct_messages ||
50
+ !!show_archived ||
51
+ !!show_unread
42
52
  const showArchivedResultsFooter = !!show_archived && !isLoading && !hasNextPage
43
53
  const showFilteredResultsFooter =
44
54
  filterDropdownEnabled &&
@@ -36,7 +36,7 @@ const FakeMenuView = ({ actions, onPressAction, children }: MenuViewProps) => {
36
36
  )
37
37
  }
38
38
 
39
- beforeAll(() => {
39
+ const configureAdapters = (menu?: MenuAdapter) => {
40
40
  ChatAdapters.configure({
41
41
  clipboard: new ClipboardAdapter({
42
42
  getStringAsync: async () => '',
@@ -48,8 +48,12 @@ beforeAll(() => {
48
48
  openCameraAsync: async () => ({ canceled: true, assets: null }),
49
49
  openImageLibraryAsync: async () => ({ canceled: true, assets: null }),
50
50
  }),
51
- menu: new MenuAdapter({ View: FakeMenuView }),
51
+ menu,
52
52
  })
53
+ }
54
+
55
+ beforeEach(() => {
56
+ configureAdapters(new MenuAdapter({ View: FakeMenuView }))
53
57
  })
54
58
 
55
59
  const options = [
@@ -111,4 +115,16 @@ describe('FilterDropdown', () => {
111
115
  expect(onMoreFilters).toHaveBeenCalledTimes(1)
112
116
  expect(onSelect).not.toHaveBeenCalled()
113
117
  })
118
+
119
+ it('opens the popover menu when the host configured no menu', () => {
120
+ configureAdapters()
121
+ const { onSelect } = renderFilterDropdown()
122
+
123
+ expect(screen.queryAllByRole('menuitem')).toHaveLength(0)
124
+
125
+ fireEvent.press(screen.getByRole('button', { name: 'Groups' }))
126
+ fireEvent.press(screen.getByRole('menuitem', { name: 'Teams' }))
127
+
128
+ expect(onSelect).toHaveBeenCalledWith('teams')
129
+ })
114
130
  })
@@ -0,0 +1,63 @@
1
+ import { fireEvent, render, screen } from '@testing-library/react-native'
2
+ import React from 'react'
3
+ import { Text } from 'react-native'
4
+ import { MenuAction } from '../../../utils/native_adapters'
5
+ import { PopoverMenuView } from '../popover_menu_view'
6
+
7
+ const actions: MenuAction[] = [
8
+ { id: 'all', title: 'All' },
9
+ { id: 'groups', title: 'Groups', state: 'on' },
10
+ { id: 'secret', title: 'Secret', attributes: { hidden: true } },
11
+ {
12
+ id: 'more',
13
+ title: '',
14
+ displayInline: true,
15
+ subactions: [{ id: 'more_filters', title: 'More filters' }],
16
+ },
17
+ ]
18
+
19
+ const renderMenu = () => {
20
+ const onPressAction = jest.fn()
21
+
22
+ render(
23
+ <PopoverMenuView actions={actions} onPressAction={onPressAction}>
24
+ <Text>Trigger</Text>
25
+ </PopoverMenuView>
26
+ )
27
+
28
+ fireEvent.press(screen.getByText('Trigger'))
29
+
30
+ return { onPressAction }
31
+ }
32
+
33
+ describe('PopoverMenuView', () => {
34
+ it('opens with a row per visible action, inline sections flattened, the on action checked', () => {
35
+ renderMenu()
36
+
37
+ expect(screen.getAllByRole('menuitem').map(item => item.props.accessibilityState)).toEqual([
38
+ { selected: false, disabled: false },
39
+ { selected: true, disabled: false },
40
+ { selected: false, disabled: false },
41
+ ])
42
+ expect(screen.getByRole('menuitem', { name: 'More filters' })).toBeTruthy()
43
+ expect(screen.queryByRole('menuitem', { name: 'Secret' })).toBeNull()
44
+ })
45
+
46
+ it('reports the pressed action by id and closes', () => {
47
+ const { onPressAction } = renderMenu()
48
+
49
+ fireEvent.press(screen.getByRole('menuitem', { name: 'More filters' }))
50
+
51
+ expect(onPressAction).toHaveBeenCalledWith({ nativeEvent: { event: 'more_filters' } })
52
+ expect(screen.queryAllByRole('menuitem')).toHaveLength(0)
53
+ })
54
+
55
+ it('closes without an action when the scrim is pressed', () => {
56
+ const { onPressAction } = renderMenu()
57
+
58
+ fireEvent.press(screen.getByLabelText('Close menu'))
59
+
60
+ expect(onPressAction).not.toHaveBeenCalled()
61
+ expect(screen.queryAllByRole('menuitem')).toHaveLength(0)
62
+ })
63
+ })
@@ -10,6 +10,7 @@ import {
10
10
  } from '../../utils'
11
11
  import { tokens } from '../../vendor/tapestry/tokens'
12
12
  import { Icon } from './icon'
13
+ import { PopoverMenuView } from './popover_menu_view'
13
14
  import { Text } from './text'
14
15
 
15
16
  // =================================
@@ -35,6 +36,11 @@ export interface FilterDropdownProps {
35
36
  onMoreFilters: () => void
36
37
  triggerLabel: string
37
38
  showMoreFilters?: boolean
39
+ /**
40
+ * Widest the trigger may render, in points. Hosts that measure the trigger at its intrinsic
41
+ * width need this for the label to truncate.
42
+ */
43
+ maxWidth?: number
38
44
  }
39
45
 
40
46
  export function FilterDropdown({
@@ -44,8 +50,10 @@ export function FilterDropdown({
44
50
  onMoreFilters,
45
51
  triggerLabel,
46
52
  showMoreFilters = true,
53
+ maxWidth,
47
54
  }: FilterDropdownProps) {
48
55
  const styles = useStyles()
56
+ const MenuView = Menu.View ?? PopoverMenuView
49
57
 
50
58
  const optionActions: MenuAction[] = options.map(option => ({
51
59
  id: option.id,
@@ -69,9 +77,9 @@ export function FilterDropdown({
69
77
  }
70
78
 
71
79
  return (
72
- <Menu.View actions={actions} onPressAction={handlePressAction} style={styles.container}>
80
+ <MenuView actions={actions} onPressAction={handlePressAction} style={styles.container}>
73
81
  <View
74
- style={styles.trigger}
82
+ style={[styles.trigger, maxWidth !== undefined && { maxWidth }]}
75
83
  accessible
76
84
  accessibilityRole="button"
77
85
  accessibilityHint="Opens filter options"
@@ -90,7 +98,7 @@ export function FilterDropdown({
90
98
  accessibilityElementsHidden
91
99
  />
92
100
  </View>
93
- </Menu.View>
101
+ </MenuView>
94
102
  )
95
103
  }
96
104
 
@@ -25,6 +25,7 @@ export * from './text_inline_button'
25
25
  export * from './text'
26
26
  export * from './toggle_button'
27
27
  export * from './filter_dropdown'
28
+ export * from './popover_menu_view'
28
29
  export * from './keyboard_scroll_view'
29
30
  export * from './keyboard_view'
30
31
  export * from './platform_modal_header_buttons'
@@ -0,0 +1,239 @@
1
+ import { clamp } from 'lodash'
2
+ import React, { useRef, useState } from 'react'
3
+ import { Modal, Pressable, ScrollView, StyleSheet, useWindowDimensions, View } from 'react-native'
4
+ import { useChatContext } from '../../contexts/chat_context'
5
+ import { useCreateAndroidRippleColor, useFontScale, useTheme } from '../../hooks'
6
+ import { MenuAction, MenuViewProps, platformPressedOpacityStyle } from '../../utils'
7
+ import { tokens } from '../../vendor/tapestry/tokens'
8
+ import { Icon } from './icon'
9
+ import { Text } from './text'
10
+
11
+ // =================================
12
+ // ====== Constants ================
13
+ // =================================
14
+
15
+ const MENU_WIDTH = 240
16
+ const MENU_OFFSET = 4
17
+ const SCREEN_MARGIN = 16
18
+
19
+ const CLOSED_ANCHOR = { x: 0, y: 0, width: 0, height: 0 }
20
+
21
+ // =================================
22
+ // ====== Rows =====================
23
+ // =================================
24
+
25
+ type Row = { kind: 'action'; action: MenuAction } | { kind: 'separator' }
26
+
27
+ const actionId = (action: MenuAction) => action.id ?? action.title
28
+
29
+ const visibleActions = (actions: MenuAction[]) =>
30
+ actions.filter(action => !action.attributes?.hidden)
31
+
32
+ // Lays the actions out the way the native menus draw them: an inline section's
33
+ // subactions sit at the top level, divided from neighboring rows by a separator.
34
+ const buildRows = (actions: MenuAction[]): Row[] => {
35
+ const rows: Row[] = []
36
+ const pushSeparator = () => {
37
+ if (rows.length > 0 && rows[rows.length - 1].kind !== 'separator') {
38
+ rows.push({ kind: 'separator' })
39
+ }
40
+ }
41
+
42
+ visibleActions(actions).forEach((action, index, visible) => {
43
+ if (action.displayInline && action.subactions) {
44
+ pushSeparator()
45
+ visibleActions(action.subactions).forEach(subaction =>
46
+ rows.push({ kind: 'action', action: subaction })
47
+ )
48
+ if (index < visible.length - 1) pushSeparator()
49
+ } else {
50
+ rows.push({ kind: 'action', action })
51
+ }
52
+ })
53
+
54
+ return rows
55
+ }
56
+
57
+ // =================================
58
+ // ====== Component ================
59
+ // =================================
60
+
61
+ /**
62
+ * A JS-only `MenuView` for hosts that cannot supply a native menu. Wraps the
63
+ * trigger in a pressable and opens a popover of the actions under it, or above
64
+ * it when the trigger sits in the lower half of the window.
65
+ */
66
+ export function PopoverMenuView({
67
+ actions,
68
+ onPressAction,
69
+ onOpenMenu,
70
+ onCloseMenu,
71
+ style,
72
+ testID,
73
+ children,
74
+ }: MenuViewProps) {
75
+ const triggerRef = useRef<View>(null)
76
+ const [expanded, setExpanded] = useState(false)
77
+ const [anchor, setAnchor] = useState(CLOSED_ANCHOR)
78
+ const { edgeToEdge = true } = useChatContext()
79
+ const { width: windowWidth, height: windowHeight } = useWindowDimensions()
80
+ const fontScale = useFontScale()
81
+ const styles = useStyles()
82
+
83
+ const open = () => {
84
+ triggerRef.current?.measureInWindow((x, y, width, height) => {
85
+ setAnchor({ x, y, width, height })
86
+ })
87
+ setExpanded(true)
88
+ onOpenMenu?.()
89
+ }
90
+
91
+ const close = () => {
92
+ setExpanded(false)
93
+ onCloseMenu?.()
94
+ }
95
+
96
+ const handlePress = (action: MenuAction) => {
97
+ onPressAction?.({ nativeEvent: { event: actionId(action) } })
98
+ close()
99
+ }
100
+
101
+ const menuWidth = clamp(MENU_WIDTH * fontScale, MENU_WIDTH, windowWidth - SCREEN_MARGIN * 2)
102
+ const anchorBottom = anchor.y + anchor.height
103
+ const opensUpward = anchor.y + anchor.height / 2 > windowHeight / 2
104
+ const menuPosition = {
105
+ width: menuWidth,
106
+ left: clamp(
107
+ anchor.x,
108
+ SCREEN_MARGIN,
109
+ Math.max(SCREEN_MARGIN, windowWidth - menuWidth - SCREEN_MARGIN)
110
+ ),
111
+ ...(opensUpward
112
+ ? {
113
+ bottom: windowHeight - anchor.y + MENU_OFFSET,
114
+ maxHeight: anchor.y - MENU_OFFSET - SCREEN_MARGIN,
115
+ }
116
+ : {
117
+ top: anchorBottom + MENU_OFFSET,
118
+ maxHeight: windowHeight - anchorBottom - MENU_OFFSET - SCREEN_MARGIN,
119
+ }),
120
+ }
121
+
122
+ return (
123
+ <>
124
+ <Pressable ref={triggerRef} style={style} testID={testID} onPress={open} accessible={false}>
125
+ {children}
126
+ </Pressable>
127
+ <Modal
128
+ visible={expanded}
129
+ transparent
130
+ animationType="fade"
131
+ statusBarTranslucent={edgeToEdge}
132
+ navigationBarTranslucent={edgeToEdge}
133
+ onRequestClose={close}
134
+ >
135
+ <Pressable
136
+ style={styles.scrim}
137
+ onPress={close}
138
+ accessibilityRole="button"
139
+ accessibilityLabel="Close menu"
140
+ />
141
+ <View style={[styles.menu, menuPosition]} accessibilityRole="menu">
142
+ <ScrollView bounces={false}>
143
+ {buildRows(actions).map((row, index) =>
144
+ row.kind === 'separator' ? (
145
+ <View key={index} style={styles.separator} />
146
+ ) : (
147
+ <PopoverMenuItem
148
+ key={index}
149
+ action={row.action}
150
+ onPress={() => handlePress(row.action)}
151
+ />
152
+ )
153
+ )}
154
+ </ScrollView>
155
+ </View>
156
+ </Modal>
157
+ </>
158
+ )
159
+ }
160
+
161
+ interface PopoverMenuItemProps {
162
+ action: MenuAction
163
+ onPress: () => void
164
+ }
165
+
166
+ function PopoverMenuItem({ action, onPress }: PopoverMenuItemProps) {
167
+ const styles = useStyles()
168
+ const { colors } = useTheme()
169
+ const androidRippleColor = useCreateAndroidRippleColor({ color: colors.fillColorNeutral050Base })
170
+ const selected = action.state === 'on'
171
+ const disabled = action.attributes?.disabled === true
172
+
173
+ return (
174
+ <Pressable
175
+ style={({ pressed }) => [
176
+ styles.item,
177
+ pressed && platformPressedOpacityStyle,
178
+ disabled && styles.itemDisabled,
179
+ ]}
180
+ onPress={onPress}
181
+ disabled={disabled}
182
+ accessibilityRole="menuitem"
183
+ accessibilityState={{ selected, disabled }}
184
+ android_ripple={{ color: androidRippleColor, borderless: false, foreground: true }}
185
+ >
186
+ <Text style={styles.itemLabel}>{action.title}</Text>
187
+ {selected && (
188
+ <Icon name="general.check" style={styles.itemCheck} accessibilityElementsHidden />
189
+ )}
190
+ </Pressable>
191
+ )
192
+ }
193
+
194
+ // =================================
195
+ // ====== Styles ===================
196
+ // =================================
197
+
198
+ const useStyles = () => {
199
+ const { colors } = useTheme()
200
+
201
+ return StyleSheet.create({
202
+ scrim: {
203
+ ...StyleSheet.absoluteFill,
204
+ },
205
+ menu: {
206
+ position: 'absolute',
207
+ paddingVertical: 4,
208
+ borderRadius: 8,
209
+ borderWidth: StyleSheet.hairlineWidth,
210
+ borderColor: colors.fillColorNeutral050Base,
211
+ backgroundColor: colors.fillColorNeutral100Inverted,
212
+ boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.15)',
213
+ elevation: 4,
214
+ overflow: 'hidden',
215
+ },
216
+ item: {
217
+ flexDirection: 'row',
218
+ alignItems: 'center',
219
+ gap: 12,
220
+ paddingHorizontal: 16,
221
+ paddingVertical: 12,
222
+ },
223
+ itemDisabled: {
224
+ opacity: 0.4,
225
+ },
226
+ itemLabel: {
227
+ flex: 1,
228
+ },
229
+ itemCheck: {
230
+ fontSize: tokens.fontSizeSm,
231
+ color: colors.interaction,
232
+ },
233
+ separator: {
234
+ height: StyleSheet.hairlineWidth,
235
+ marginVertical: 4,
236
+ backgroundColor: colors.fillColorNeutral050Base,
237
+ },
238
+ })
239
+ }
@@ -45,7 +45,13 @@ export const ConversationsContextProvider = ({
45
45
  opts?: ApiGetQueryOptions
46
46
  }>) => {
47
47
  const [activeConversationId, setActiveConversationId] = useState<number | undefined>()
48
- const { chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived } = args
48
+ const {
49
+ chat_group_graph_id,
50
+ group_source_app_name,
51
+ show_direct_messages,
52
+ show_archived,
53
+ show_unread,
54
+ } = args
49
55
 
50
56
  const query = useConversations(
51
57
  {
@@ -53,6 +59,7 @@ export const ConversationsContextProvider = ({
53
59
  group_source_app_name,
54
60
  show_direct_messages,
55
61
  show_archived,
62
+ show_unread,
56
63
  },
57
64
  paginatorOptions
58
65
  )