messenger.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import { memo, useState, useEffect, useCallback, useMemo, useRef } from 'react';
  2. import { parseUrl } from 'app/lib/util'
  3. import Messenger from 'app/components/elements/messenger/parts/common'
  4. import { CreateConvoButton } from 'app/components/elements/messenger/parts/new-convo'
  5. import { fetcher } from 'app/lib/fetcher';
  6. import { Platform } from 'react-native'
  7. import { useIsDesktop } from 'app/context/measure';
  8. import { BlockWrapper } from 'app/components/block-wrapper'
  9. export default function MessengerEl(props) {
  10. const url = props.url;
  11. let defaultMenuName = 'inbox';
  12. let defaultConvoId = '';
  13. let b = parseUrl(url);
  14. const aUrl = b['path'].split('/');
  15. if (aUrl.length > 1)
  16. defaultMenuName = aUrl[1];
  17. if (aUrl.length > 2)
  18. defaultConvoId = aUrl[2];
  19. const menuDefaultList = useMemo(() => {
  20. return props.data.menu.items.filter(item => ['inbox', 'direct'].includes(item.name));
  21. }, [props]);
  22. const [menu, setMenu] = useState(() => {
  23. const index = menuDefaultList.findIndex(item => item.name == defaultMenuName);
  24. return { data: menuDefaultList, index: index >= 0 ? index : 0 };
  25. });
  26. const [convos, setConvos] = useState(false);
  27. const [initedConvoId, setInitedConvoId] = useState(defaultConvoId);
  28. const menuRef = useRef(menu);
  29. menuRef.current = menu;
  30. const fetchRequestId = useRef(0);
  31. const fetchConvos = useCallback(async (term = '') => {
  32. const menuItem = menuRef.current?.data?.[menuRef.current?.index]?.name;
  33. if (!menuItem) return;
  34. const requestId = ++fetchRequestId.current;
  35. const request_url = term
  36. ? '/api.php?r=bx_messenger/search_lots/Services&params=' + JSON.stringify({ term })
  37. : '/api.php?r=bx_messenger/get_convos_list/Services&params[]=' + JSON.stringify({ group: menuItem, count: 0 });
  38. try {
  39. const sResponse = await fetcher(request_url);
  40. if (requestId !== fetchRequestId.current) return;
  41. setConvos({
  42. data: term
  43. ? (sResponse.data?.lots ? sResponse.data.lots : [])
  44. : sResponse.data,
  45. // Which inbox/direct group this list belongs to — consumers use
  46. // it to ignore stale lists right after a menu switch.
  47. menu: menuItem,
  48. });
  49. } catch {
  50. if (requestId !== fetchRequestId.current) return;
  51. }
  52. }, []);
  53. useEffect(() => {
  54. fetchConvos();
  55. }, [menu.index, fetchConvos]);
  56. const onSave = useCallback((data) => {
  57. setConvos(prevConvos => ({
  58. ...prevConvos,
  59. data: [data.convo, ...prevConvos.data]
  60. }));
  61. setInitedConvoId(data.convo.id)
  62. }, []);
  63. // Inbox / direct switch: drop any deep-linked conversation so the new
  64. // list drives selection (desktop auto-selects its first conversation).
  65. const onMenuChange = useCallback((index) => {
  66. setInitedConvoId('');
  67. setMenu(prev => (prev.index === index ? prev : { ...prev, index }));
  68. }, []);
  69. const addButtons = useMemo(() => {
  70. if (!props.data?.config?.permissions || props.data?.config?.permissions?.create_talk == 1){
  71. return [
  72. <CreateConvoButton key="a" onSave={onSave} style="glass" />
  73. ]
  74. }
  75. return null
  76. }, []);
  77. const messengerContainer = (menu && convos) && <MessengerContainer
  78. fetchConvos={fetchConvos}
  79. convos={convos}
  80. blockData={props.data}
  81. pageData={props.pageData}
  82. block={props.block}
  83. alreadyWrapped={Boolean(props.blockWrapperProps)}
  84. selectedMenu={menu?.data[menu?.index]?.name}
  85. menuItems={menu.data}
  86. menuIndex={menu.index}
  87. onMenuChange={onMenuChange}
  88. defaultConvoId={initedConvoId}
  89. onSave={onSave}
  90. addButtons={addButtons}
  91. />
  92. return messengerContainer;
  93. }
  94. const MessengerContainer = memo(({ convos, selectedMenu, blockData, pageData, fetchConvos, defaultConvoId, onSave, addButtons, menuItems, menuIndex, onMenuChange, block, alreadyWrapped }) => {
  95. const isWeb = Platform.OS == 'web'
  96. const isDesktop = useIsDesktop();
  97. const messenger = (
  98. <Messenger
  99. pageData={pageData}
  100. data={blockData}
  101. addButtons={addButtons}
  102. onSave={onSave}
  103. fetchConvos={fetchConvos}
  104. selectedMenu={selectedMenu}
  105. menuItems={menuItems}
  106. menuIndex={menuIndex}
  107. onMenuChange={onMenuChange}
  108. convos={convos}
  109. defaultConvoId={defaultConvoId}
  110. />
  111. );
  112. // Cell already wraps. Native uses the injected page header only.
  113. // Web always goes through BlockWrapper so desktop ↔ mobile does not
  114. // remount; contentOnly skips UNA chrome on small screens.
  115. // Inbox/direct live in the conversation list — drop block.menu so
  116. // BlockWrapper does not force a header (or duplicate those tabs)
  117. // when UNA has the title designbox off.
  118. if (alreadyWrapped || !isWeb) {
  119. return messenger;
  120. }
  121. const blockForWrapper = block?.menu ? { ...block, menu: undefined } : block;
  122. return (
  123. <BlockWrapper
  124. block={blockForWrapper}
  125. config={block?.config_api}
  126. fullWidth
  127. fill={isDesktop}
  128. contentOnly={!isDesktop}
  129. wrapperClassses={isDesktop ? 'h-full min-h-0' : undefined}
  130. >
  131. {messenger}
  132. </BlockWrapper>
  133. );
  134. });