@messenger-box/platform-mobile 10.0.3-alpha.19 → 10.0.3-alpha.22

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 (26) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/screens/inbox/components/CachedImage/index.js +0 -19
  3. package/lib/screens/inbox/components/CachedImage/index.js.map +1 -1
  4. package/lib/screens/inbox/components/DialogsListItem.js +423 -50
  5. package/lib/screens/inbox/components/DialogsListItem.js.map +1 -1
  6. package/lib/screens/inbox/components/ServiceDialogsListItem.js +375 -51
  7. package/lib/screens/inbox/components/ServiceDialogsListItem.js.map +1 -1
  8. package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js +175 -0
  9. package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js.map +1 -0
  10. package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js +191 -0
  11. package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js.map +1 -0
  12. package/lib/screens/inbox/containers/Dialogs.js +536 -66
  13. package/lib/screens/inbox/containers/Dialogs.js.map +1 -1
  14. package/lib/screens/inbox/containers/ThreadConversationView.js +95 -23
  15. package/lib/screens/inbox/containers/ThreadConversationView.js.map +1 -1
  16. package/lib/screens/inbox/containers/workflow/dialogs-xstate.js +211 -0
  17. package/lib/screens/inbox/containers/workflow/dialogs-xstate.js.map +1 -0
  18. package/package.json +2 -2
  19. package/src/screens/inbox/components/CachedImage/index.tsx +9 -9
  20. package/src/screens/inbox/components/DialogsListItem.tsx +624 -107
  21. package/src/screens/inbox/components/ServiceDialogsListItem.tsx +506 -114
  22. package/src/screens/inbox/components/SupportServiceDialogsListItem.tsx +35 -17
  23. package/src/screens/inbox/components/workflow/dialogs-list-item-xstate.ts +145 -0
  24. package/src/screens/inbox/components/workflow/service-dialogs-list-item-xstate.ts +159 -0
  25. package/src/screens/inbox/containers/Dialogs.tsx +711 -169
  26. package/src/screens/inbox/containers/ThreadConversationView.tsx +151 -35
@@ -1,4 +1,6 @@
1
- import React__default,{useState,useCallback}from'react';import {Box,FlatList,Center,Spinner,Heading,Input,InputField,Text}from'@admin-layout/gluestack-ui-mobile';import {Ionicons}from'@expo/vector-icons';import {useSelector,useDispatch}from'react-redux';import {useRoute,useNavigation,useIsFocused,useFocusEffect}from'@react-navigation/native';import {orderBy}from'lodash-es';import {DialogsListItem}from'../components/DialogsListItem.js';import {ServiceDialogsListItem}from'../components/ServiceDialogsListItem.js';import {useGetChannelsByUserWithServiceChannelsQuery}from'common/graphql';import {RoomType}from'common';import {userSelector}from'@adminide-stack/user-auth0-client';import {config}from'../config/config.js';import colors from'tailwindcss/colors';var __defProp = Object.defineProperty;
1
+ import React__default,{useRef,useCallback,useEffect,useState,useMemo}from'react';import {Box,FlatList,Center,Spinner,Text,Heading,Input,InputField}from'@admin-layout/gluestack-ui-mobile';import {Ionicons}from'@expo/vector-icons';import {useSelector}from'react-redux';import {useRoute,useNavigation,useFocusEffect}from'@react-navigation/native';import {orderBy}from'lodash-es';import {DialogsListItem}from'../components/DialogsListItem.js';import {ServiceDialogsListItem}from'../components/ServiceDialogsListItem.js';import {useGetChannelsByUserWithServiceChannelsQuery}from'common/graphql';import {RoomType}from'common';import {userSelector}from'@adminide-stack/user-auth0-client';import {config}from'../config/config.js';import colors from'tailwindcss/colors';import'./workflow/dialogs-xstate.js';var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
2
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
4
6
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -14,6 +16,174 @@ var __spreadValues = (a, b) => {
14
16
  }
15
17
  return a;
16
18
  };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ const Actions = {
21
+ INITIAL_CONTEXT: "INITIAL_CONTEXT",
22
+ ERROR_HANDLED: "ERROR_HANDLED",
23
+ FETCH_CHANNELS: "FETCH_CHANNELS",
24
+ APPEND_CHANNELS: "APPEND_CHANNELS",
25
+ REFRESH_CHANNELS: "REFRESH_CHANNELS",
26
+ SELECT_CHANNEL: "SELECT_CHANNEL",
27
+ START_LOADING: "START_LOADING",
28
+ STOP_LOADING: "STOP_LOADING",
29
+ LOAD_MORE_CHANNELS: "LOAD_MORE_CHANNELS",
30
+ SET_SEARCH_QUERY: "SET_SEARCH_QUERY"
31
+ };
32
+ const BaseState = {
33
+ Idle: "idle",
34
+ Error: "error",
35
+ Loading: "loading",
36
+ Done: "done",
37
+ FetchChannels: "fetchChannels"
38
+ };
39
+ const MainState = {
40
+ RefreshChannels: "refreshChannels",
41
+ SelectChannel: "selectChannel",
42
+ LoadMoreChannels: "loadMoreChannels"
43
+ };
44
+ function useSafeMachine(machine) {
45
+ const [state, setState] = useState({
46
+ context: {
47
+ channels: [],
48
+ refreshing: false,
49
+ loading: false,
50
+ error: null,
51
+ searchQuery: "",
52
+ selectedChannelId: null,
53
+ channelRole: null,
54
+ channelFilters: {},
55
+ supportServices: false,
56
+ page: 1,
57
+ hasMoreChannels: true,
58
+ loadingMore: false
59
+ },
60
+ value: "idle"
61
+ });
62
+ const send = useCallback((event) => {
63
+ var _a, _b, _c, _d;
64
+ try {
65
+ console.log("Event received:", event.type);
66
+ if (event.type === Actions.INITIAL_CONTEXT) {
67
+ setState((prev) => {
68
+ var _a2, _b2, _c2, _d2;
69
+ return __spreadProps(__spreadValues({}, prev), {
70
+ context: __spreadProps(__spreadValues({}, prev.context), {
71
+ channelRole: ((_a2 = event.data) == null ? void 0 : _a2.channelRole) || null,
72
+ channelFilters: ((_b2 = event.data) == null ? void 0 : _b2.channelFilters) || {},
73
+ supportServices: ((_c2 = event.data) == null ? void 0 : _c2.supportServices) || false,
74
+ selectedChannelId: ((_d2 = event.data) == null ? void 0 : _d2.selectedChannelId) || null,
75
+ loading: true,
76
+ page: 1,
77
+ hasMoreChannels: true
78
+ }),
79
+ value: BaseState.FetchChannels
80
+ });
81
+ });
82
+ } else if (event.type === Actions.FETCH_CHANNELS) {
83
+ console.log("Setting channels:", ((_b = (_a = event.data) == null ? void 0 : _a.channels) == null ? void 0 : _b.length) || 0);
84
+ setState((prev) => {
85
+ var _a2, _b2, _c2, _d2, _e;
86
+ return __spreadProps(__spreadValues({}, prev), {
87
+ context: __spreadProps(__spreadValues({}, prev.context), {
88
+ channels: ((_a2 = event.data) == null ? void 0 : _a2.channels) || [],
89
+ hasMoreChannels: (((_c2 = (_b2 = event.data) == null ? void 0 : _b2.channels) == null ? void 0 : _c2.length) || 0) > 0,
90
+ loading: ((_d2 = event.data) == null ? void 0 : _d2.stopLoading) ? false : prev.context.loading,
91
+ refreshing: ((_e = event.data) == null ? void 0 : _e.stopLoading) ? false : prev.context.refreshing,
92
+ loadingMore: false
93
+ }),
94
+ value: BaseState.Idle
95
+ });
96
+ });
97
+ } else if (event.type === Actions.APPEND_CHANNELS) {
98
+ const newChannels = ((_c = event.data) == null ? void 0 : _c.channels) || [];
99
+ console.log("Appending channels:", newChannels.length);
100
+ setState((prev) => __spreadProps(__spreadValues({}, prev), {
101
+ context: __spreadProps(__spreadValues({}, prev.context), {
102
+ channels: [...prev.context.channels, ...newChannels],
103
+ hasMoreChannels: newChannels.length >= 10,
104
+ page: prev.context.page + 1,
105
+ loadingMore: false
106
+ }),
107
+ value: BaseState.Idle
108
+ }));
109
+ } else if (event.type === Actions.REFRESH_CHANNELS) {
110
+ setState((prev) => __spreadProps(__spreadValues({}, prev), {
111
+ context: __spreadProps(__spreadValues({}, prev.context), {
112
+ refreshing: true,
113
+ page: 1,
114
+ hasMoreChannels: true
115
+ }),
116
+ value: MainState.RefreshChannels
117
+ }));
118
+ } else if (event.type === Actions.SELECT_CHANNEL) {
119
+ setState((prev) => {
120
+ var _a2;
121
+ return __spreadProps(__spreadValues({}, prev), {
122
+ context: __spreadProps(__spreadValues({}, prev.context), {
123
+ selectedChannelId: ((_a2 = event.data) == null ? void 0 : _a2.channelId) || null
124
+ })
125
+ });
126
+ });
127
+ } else if (event.type === Actions.START_LOADING) {
128
+ setState((prev) => __spreadProps(__spreadValues({}, prev), {
129
+ context: __spreadProps(__spreadValues({}, prev.context), {
130
+ loading: true
131
+ })
132
+ }));
133
+ } else if (event.type === Actions.STOP_LOADING) {
134
+ console.log("Explicitly stopping loading state");
135
+ setState((prev) => __spreadProps(__spreadValues({}, prev), {
136
+ context: __spreadProps(__spreadValues({}, prev.context), {
137
+ loading: false,
138
+ refreshing: false,
139
+ loadingMore: false
140
+ }),
141
+ value: prev.value === BaseState.FetchChannels ? BaseState.Idle : prev.value
142
+ }));
143
+ } else if (event.type === Actions.LOAD_MORE_CHANNELS) {
144
+ setState((prev) => __spreadProps(__spreadValues({}, prev), {
145
+ context: __spreadProps(__spreadValues({}, prev.context), {
146
+ loadingMore: true
147
+ }),
148
+ value: MainState.LoadMoreChannels
149
+ }));
150
+ } else if (event.type === Actions.SET_SEARCH_QUERY) {
151
+ setState((prev) => {
152
+ var _a2;
153
+ return __spreadProps(__spreadValues({}, prev), {
154
+ context: __spreadProps(__spreadValues({}, prev.context), {
155
+ searchQuery: ((_a2 = event.data) == null ? void 0 : _a2.searchQuery) || ""
156
+ })
157
+ });
158
+ });
159
+ } else if (event.type === Actions.ERROR_HANDLED) {
160
+ console.log("Error handled:", (_d = event.data) == null ? void 0 : _d.message);
161
+ setState((prev) => {
162
+ var _a2;
163
+ return __spreadProps(__spreadValues({}, prev), {
164
+ context: __spreadProps(__spreadValues({}, prev.context), {
165
+ error: ((_a2 = event.data) == null ? void 0 : _a2.message) || null,
166
+ loading: false,
167
+ refreshing: false,
168
+ loadingMore: false
169
+ }),
170
+ value: BaseState.Idle
171
+ });
172
+ });
173
+ }
174
+ } catch (error) {
175
+ console.error("Error handling event:", error);
176
+ }
177
+ }, []);
178
+ const stateWithMatches = useMemo(() => {
179
+ return __spreadProps(__spreadValues({}, state), {
180
+ matches: (checkState) => {
181
+ return state.value === checkState;
182
+ }
183
+ });
184
+ }, [state]);
185
+ return [stateWithMatches, send];
186
+ }
17
187
  const DialogsComponent = (props) => {
18
188
  var _a;
19
189
  const {
@@ -27,13 +197,79 @@ const DialogsComponent = (props) => {
27
197
  params
28
198
  } = useRoute();
29
199
  const auth = useSelector(userSelector);
30
- useDispatch();
31
200
  const navigation = useNavigation();
32
- useIsFocused();
33
- const [refreshing, setRefresh] = useState(false);
201
+ const isMountedRef = useRef(true);
202
+ const [state, send] = useSafeMachine();
203
+ useCallback(() => {
204
+ try {
205
+ return (state == null ? void 0 : state.context) || {};
206
+ } catch (error) {
207
+ console.error("Error accessing state.context:", error);
208
+ return {};
209
+ }
210
+ }, [state]);
211
+ const safeContextProperty = useCallback((property, defaultValue = null) => {
212
+ var _a2, _b;
213
+ try {
214
+ return (_b = (_a2 = state == null ? void 0 : state.context) == null ? void 0 : _a2[property]) != null ? _b : defaultValue;
215
+ } catch (error) {
216
+ console.error(`Error accessing state.context.${property}:`, error);
217
+ return defaultValue;
218
+ }
219
+ }, [state]);
220
+ const safeMatches = useCallback((stateValue) => {
221
+ var _a2;
222
+ try {
223
+ return ((_a2 = state == null ? void 0 : state.matches) == null ? void 0 : _a2.call(state, stateValue)) || false;
224
+ } catch (error) {
225
+ console.error(`Error calling state.matches with ${stateValue}:`, error);
226
+ return false;
227
+ }
228
+ }, [state]);
229
+ const safeSend = useCallback((event) => {
230
+ try {
231
+ send(event);
232
+ } catch (error) {
233
+ console.error("Error sending event to state machine:", error, event);
234
+ }
235
+ }, [send]);
236
+ const channels = safeContextProperty("channels", []);
237
+ const refreshing = safeContextProperty("refreshing", false);
238
+ const loading = safeContextProperty("loading", false);
239
+ const searchQuery = safeContextProperty("searchQuery", "");
240
+ const selectedChannelId = safeContextProperty("selectedChannelId", null);
241
+ const loadingMore = safeContextProperty("loadingMore", false);
242
+ const hasMoreChannels = safeContextProperty("hasMoreChannels", true);
243
+ const page = safeContextProperty("page", 1);
244
+ const stateRef = useRef(state);
245
+ useEffect(() => {
246
+ stateRef.current = state;
247
+ }, [state]);
248
+ const safeGetContext = useCallback(() => {
249
+ if (stateRef.current && stateRef.current.context) {
250
+ return stateRef.current.context;
251
+ }
252
+ return {
253
+ channels: [],
254
+ refreshing: false,
255
+ loading: false,
256
+ error: null,
257
+ searchQuery: "",
258
+ selectedChannelId: null,
259
+ channelRole: null,
260
+ channelFilters: {},
261
+ supportServices: false,
262
+ page: 1,
263
+ hasMoreChannels: true,
264
+ loadingMore: false
265
+ };
266
+ }, []);
267
+ useEffect(() => {
268
+ return () => {
269
+ isMountedRef.current = false;
270
+ };
271
+ }, []);
34
272
  const {
35
- data: userChannels,
36
- loading: userChannelsLoading,
37
273
  refetch: getChannelsRefetch
38
274
  } = useGetChannelsByUserWithServiceChannelsQuery({
39
275
  variables: {
@@ -42,81 +278,315 @@ const DialogsComponent = (props) => {
42
278
  supportServices: supportServices ? true : false,
43
279
  supportServiceCriteria: {
44
280
  type: RoomType.Service
45
- }
46
- }
281
+ },
282
+ limit: 15,
283
+ skip: 0
284
+ },
285
+ fetchPolicy: "cache-and-network",
286
+ nextFetchPolicy: "network-only",
287
+ notifyOnNetworkStatusChange: true,
288
+ skip: true
47
289
  });
48
- useFocusEffect(React__default.useCallback(() => {
49
- setRefresh(false);
50
- getChannelsRefetch({
51
- role: channelRole,
52
- criteria: channelFilters,
53
- supportServices: supportServices ? true : false,
54
- supportServiceCriteria: {
55
- type: RoomType.Service
290
+ const fetchChannelsDirectly = useCallback(async (pageNum = 1, append = false) => {
291
+ var _a2, _b, _c;
292
+ try {
293
+ const context = safeGetContext();
294
+ console.log(`\u{1F4AB} FETCHING channels (page: ${pageNum}, append: ${append})`);
295
+ const skipCount = (pageNum - 1) * 15;
296
+ const {
297
+ data
298
+ } = await getChannelsRefetch({
299
+ role: channelRole,
300
+ criteria: channelFilters,
301
+ supportServices: supportServices ? true : false,
302
+ supportServiceCriteria: {
303
+ type: RoomType.Service
304
+ },
305
+ limit: 15,
306
+ skip: skipCount
307
+ });
308
+ const allChannels = [...(_a2 = data == null ? void 0 : data.supportServiceChannels) != null ? _a2 : [], ...(_b = data == null ? void 0 : data.channelsByUser) != null ? _b : []];
309
+ const filteredChannels = (_c = allChannels == null ? void 0 : allChannels.filter((c) => c.members.some((u) => {
310
+ var _a3;
311
+ return u !== null && ((_a3 = u == null ? void 0 : u.user) == null ? void 0 : _a3.id) != (auth == null ? void 0 : auth.id) && u.user.__typename == "UserAccount";
312
+ }))) != null ? _c : [];
313
+ const sortedChannels = filteredChannels && orderBy(filteredChannels, ["updatedAt"], ["desc"]) || [];
314
+ console.log(`\u{1F4CA} Processed channels: ${sortedChannels.length} (page: ${pageNum}, skip: ${skipCount})`);
315
+ if (isMountedRef.current) {
316
+ if (append) {
317
+ safeSend({
318
+ type: Actions.APPEND_CHANNELS,
319
+ data: {
320
+ channels: sortedChannels
321
+ }
322
+ });
323
+ } else {
324
+ safeSend({
325
+ type: Actions.FETCH_CHANNELS,
326
+ data: {
327
+ channels: sortedChannels
328
+ }
329
+ });
330
+ }
331
+ safeSend({
332
+ type: Actions.STOP_LOADING
333
+ });
56
334
  }
57
- });
58
- return () => {
59
- };
60
- }, [channelFilters]));
61
- const channels = React__default.useMemo(() => {
335
+ } catch (error) {
336
+ console.error("Error fetching channels:", error);
337
+ if (isMountedRef.current) {
338
+ safeSend({
339
+ type: Actions.ERROR_HANDLED,
340
+ data: {
341
+ message: "Failed to fetch channels"
342
+ }
343
+ });
344
+ }
345
+ }
346
+ }, [getChannelsRefetch, channelRole, channelFilters, supportServices, auth == null ? void 0 : auth.id, safeSend]);
347
+ useEffect(() => {
348
+ if (loading) {
349
+ const safetyTimeout = setTimeout(() => {
350
+ console.log("\u26A0\uFE0F Safety timeout triggered - forcing loading state to stop");
351
+ if (isMountedRef.current) {
352
+ safeSend({
353
+ type: Actions.STOP_LOADING
354
+ });
355
+ }
356
+ }, 5e3);
357
+ return () => clearTimeout(safetyTimeout);
358
+ }
359
+ }, [loading, safeSend]);
360
+ const fastRefresh = useCallback(async () => {
62
361
  var _a2, _b, _c;
63
- const allChannels = [...(_a2 = userChannels == null ? void 0 : userChannels.supportServiceChannels) != null ? _a2 : [], ...(_b = userChannels == null ? void 0 : userChannels.channelsByUser) != null ? _b : []];
64
- let uChannels = (_c = allChannels == null ? void 0 : allChannels.filter((c) => c.members.some((u) => {
65
- var _a3;
66
- return u !== null && ((_a3 = u == null ? void 0 : u.user) == null ? void 0 : _a3.id) != (auth == null ? void 0 : auth.id) && u.user.__typename == "UserAccount";
67
- }))) != null ? _c : [];
68
- return uChannels && orderBy(uChannels, ["updatedAt"], ["desc"]) || [];
69
- }, [userChannels]);
70
- const handleSelectChannel = useCallback((id, title) => {
71
- var _a2;
72
- if (params == null ? void 0 : params.channelId) {
73
- navigation.navigate(config.INBOX_MESSEGE_PATH, {
74
- channelId: params == null ? void 0 : params.channelId,
75
- role: params == null ? void 0 : params.role,
76
- title: (_a2 = params == null ? void 0 : params.title) != null ? _a2 : null,
77
- hideTabBar: true
362
+ try {
363
+ console.log("\u{1F504} Fast refreshing channels...");
364
+ const clearRefreshingTimeout = setTimeout(() => {
365
+ if (isMountedRef.current) {
366
+ console.log("\u26A0\uFE0F Fast refresh timeout - stopping refresh state");
367
+ safeSend({
368
+ type: Actions.STOP_LOADING
369
+ });
370
+ }
371
+ }, 3e3);
372
+ const {
373
+ data
374
+ } = await getChannelsRefetch({
375
+ role: channelRole,
376
+ criteria: channelFilters,
377
+ supportServices: supportServices ? true : false,
378
+ supportServiceCriteria: {
379
+ type: RoomType.Service
380
+ },
381
+ limit: 10,
382
+ skip: 0
78
383
  });
384
+ clearTimeout(clearRefreshingTimeout);
385
+ if (!isMountedRef.current)
386
+ return;
387
+ const allChannels = [...(_a2 = data == null ? void 0 : data.supportServiceChannels) != null ? _a2 : [], ...(_b = data == null ? void 0 : data.channelsByUser) != null ? _b : []];
388
+ const filteredChannels = (_c = allChannels == null ? void 0 : allChannels.filter((c) => c.members.some((u) => {
389
+ var _a3;
390
+ return u !== null && ((_a3 = u == null ? void 0 : u.user) == null ? void 0 : _a3.id) != (auth == null ? void 0 : auth.id) && u.user.__typename == "UserAccount";
391
+ }))) != null ? _c : [];
392
+ const sortedChannels = filteredChannels && orderBy(filteredChannels, ["updatedAt"], ["desc"]) || [];
393
+ console.log(`\u{1F4CA} Fast refresh completed: ${sortedChannels.length} channels`);
394
+ if (isMountedRef.current) {
395
+ safeSend({
396
+ type: Actions.FETCH_CHANNELS,
397
+ data: {
398
+ channels: sortedChannels,
399
+ stopLoading: true
400
+ }
401
+ });
402
+ }
403
+ } catch (error) {
404
+ console.error("Error during fast refresh:", error);
405
+ if (isMountedRef.current) {
406
+ safeSend({
407
+ type: Actions.STOP_LOADING
408
+ });
409
+ }
410
+ }
411
+ }, [getChannelsRefetch, channelRole, channelFilters, supportServices, auth == null ? void 0 : auth.id, safeSend]);
412
+ useEffect(() => {
413
+ const context = safeGetContext();
414
+ const isAlreadyFetching = context.refreshing || context.loading;
415
+ if (!isAlreadyFetching) {
416
+ if (safeMatches(BaseState.FetchChannels)) {
417
+ console.log("\u{1F504} Fetching channels...");
418
+ fetchChannelsDirectly(1, false);
419
+ } else if (safeMatches(MainState.RefreshChannels)) {
420
+ console.log("\u{1F504} Refreshing channels...");
421
+ fetchChannelsDirectly(1, false);
422
+ } else if (safeMatches(MainState.LoadMoreChannels)) {
423
+ console.log("\u{1F504} Loading more channels...");
424
+ fetchChannelsDirectly(page, true);
425
+ }
79
426
  } else {
80
- navigation.navigate(config.INBOX_MESSEGE_PATH, {
81
- channelId: id,
82
- role: channelRole,
83
- title,
84
- hideTabBar: true
427
+ console.log("\u23E9 Skipping fetch because isAlreadyFetching:", isAlreadyFetching);
428
+ }
429
+ }, [fetchChannelsDirectly, safeMatches, safeGetContext, state.value, page]);
430
+ useEffect(() => {
431
+ console.log("State changed to:", state.value);
432
+ console.log("Context:", JSON.stringify({
433
+ channelsCount: channels.length,
434
+ loading,
435
+ refreshing
436
+ }));
437
+ }, [state.value, channels.length, loading, refreshing]);
438
+ useEffect(() => {
439
+ if (isMountedRef.current) {
440
+ console.log("\u{1F680} Initializing state machine with props", {
441
+ channelRole,
442
+ channelFilters,
443
+ supportServices,
444
+ selectedChannelId: params == null ? void 0 : params.channelId
445
+ });
446
+ safeSend({
447
+ type: Actions.INITIAL_CONTEXT,
448
+ data: {
449
+ channelRole,
450
+ channelFilters,
451
+ supportServices,
452
+ selectedChannelId: params == null ? void 0 : params.channelId
453
+ }
85
454
  });
455
+ const initSafetyTimeout = setTimeout(() => {
456
+ if (isMountedRef.current && loading) {
457
+ console.log("\u26A0\uFE0F Init safety timeout triggered - forcing loading state to stop");
458
+ safeSend({
459
+ type: Actions.STOP_LOADING
460
+ });
461
+ }
462
+ }, 8e3);
463
+ return () => clearTimeout(initSafetyTimeout);
86
464
  }
87
465
  }, []);
88
- const handleSelectServiceChannel = useCallback((id, title, postParentId) => {
89
- var _a2;
90
- if (params == null ? void 0 : params.channelId) {
91
- navigation.navigate((params == null ? void 0 : params.postParentId) || (params == null ? void 0 : params.postParentId) == 0 ? config.THREAD_MESSEGE_PATH : config.THREADS_PATH, {
92
- channelId: params == null ? void 0 : params.channelId,
93
- role: params == null ? void 0 : params.role,
94
- title: (_a2 = params == null ? void 0 : params.title) != null ? _a2 : null,
95
- postParentId: params == null ? void 0 : params.postParentId,
96
- hideTabBar: true
466
+ const focusRefreshRef = useRef(null);
467
+ useFocusEffect(useCallback(() => {
468
+ let hasTriggeredRefresh = false;
469
+ const now = Date.now();
470
+ const lastRefresh = focusRefreshRef.current;
471
+ const shouldRefresh = lastRefresh === null || now - lastRefresh > 2e3;
472
+ const context = safeGetContext();
473
+ const isAlreadyFetching = context.refreshing || context.loading;
474
+ if (isMountedRef.current && !hasTriggeredRefresh && !isAlreadyFetching && shouldRefresh && (channels.length > 0 || safeMatches(BaseState.Idle))) {
475
+ hasTriggeredRefresh = true;
476
+ focusRefreshRef.current = now;
477
+ console.log("\u{1F504} Focus effect: triggering fast refresh");
478
+ safeSend({
479
+ type: Actions.START_LOADING,
480
+ data: {
481
+ refreshing: true
482
+ }
97
483
  });
484
+ setTimeout(() => {
485
+ if (isMountedRef.current) {
486
+ fastRefresh();
487
+ }
488
+ }, 100);
98
489
  } else {
99
- navigation.navigate(postParentId || postParentId == 0 ? config.THREAD_MESSEGE_PATH : config.THREADS_PATH, {
100
- channelId: id,
101
- role: channelRole,
102
- title,
103
- postParentId,
104
- hideTabBar: true
490
+ console.log("\u23E9 Skipping focus refresh:", {
491
+ isAlreadyFetching,
492
+ hasTriggeredRefresh,
493
+ shouldRefresh,
494
+ timeGap: lastRefresh === null ? "first refresh" : now - lastRefresh
105
495
  });
106
496
  }
107
- }, []);
108
- const handleRefresh = useCallback(() => {
109
- var _a2;
110
- setRefresh(true);
111
- (_a2 = getChannelsRefetch({
497
+ return () => {
498
+ hasTriggeredRefresh = false;
499
+ };
500
+ }, [safeSend, channels.length, safeMatches, safeGetContext, fastRefresh]));
501
+ const handleSelectChannel = useCallback((id, title) => {
502
+ safeSend({
503
+ type: Actions.SELECT_CHANNEL,
504
+ data: {
505
+ channelId: id
506
+ }
507
+ });
508
+ navigation.navigate(config.INBOX_MESSEGE_PATH, {
509
+ channelId: id,
112
510
  role: channelRole,
113
- criteria: channelFilters
114
- })) == null ? void 0 : _a2.finally(() => setRefresh(false));
115
- }, []);
116
- return /* @__PURE__ */ React__default.createElement(Box, { className: "p-2" }, /* @__PURE__ */ React__default.createElement(FlatList, { data: channels && (channels == null ? void 0 : channels.length) > 0 ? channels : [], onRefresh: handleRefresh, refreshing, contentContainerStyle: {
511
+ title,
512
+ hideTabBar: true,
513
+ timestamp: new Date().getTime()
514
+ });
515
+ }, [navigation, channelRole, safeSend]);
516
+ const handleSelectServiceChannel = useCallback((id, title, postParentId) => {
517
+ safeSend({
518
+ type: Actions.SELECT_CHANNEL,
519
+ data: {
520
+ channelId: id
521
+ }
522
+ });
523
+ navigation.navigate(postParentId || postParentId === 0 ? config.THREAD_MESSEGE_PATH : config.THREADS_PATH, {
524
+ channelId: id,
525
+ role: channelRole,
526
+ title,
527
+ postParentId,
528
+ hideTabBar: true
529
+ });
530
+ }, [navigation, channelRole, safeSend]);
531
+ const handlePullToRefresh = useCallback(() => {
532
+ if (refreshing) {
533
+ console.log("\u23E9 Skipping refresh because already refreshing");
534
+ return;
535
+ }
536
+ const now = Date.now();
537
+ focusRefreshRef.current = now;
538
+ console.log("\u{1F504} Pull-to-refresh triggered");
539
+ safeSend({
540
+ type: Actions.START_LOADING,
541
+ data: {
542
+ refreshing: true
543
+ }
544
+ });
545
+ fastRefresh();
546
+ }, [safeSend, refreshing, fastRefresh]);
547
+ const handleSearchChange = useCallback((text) => {
548
+ safeSend({
549
+ type: Actions.SET_SEARCH_QUERY,
550
+ data: {
551
+ searchQuery: text
552
+ }
553
+ });
554
+ }, [safeSend]);
555
+ const handleLoadMore = useCallback(() => {
556
+ if (!loadingMore && hasMoreChannels) {
557
+ console.log("Loading more channels at page:", page + 1);
558
+ safeSend({
559
+ type: Actions.LOAD_MORE_CHANNELS
560
+ });
561
+ } else {
562
+ console.log("Skip loading more: loadingMore=", loadingMore, "hasMoreChannels=", hasMoreChannels);
563
+ }
564
+ }, [safeSend, loadingMore, hasMoreChannels, page]);
565
+ return /* @__PURE__ */ React__default.createElement(Box, { className: "p-2" }, /* @__PURE__ */ React__default.createElement(FlatList, { data: channels, onRefresh: handlePullToRefresh, refreshing, contentContainerStyle: {
117
566
  minHeight: "100%"
118
567
  }, ItemSeparatorComponent: () => /* @__PURE__ */ React__default.createElement(Box, { className: "h-0.5 bg-gray-200" }), renderItem: ({
119
568
  item: channel
120
- }) => (channel == null ? void 0 : channel.type) === RoomType.Service ? /* @__PURE__ */ React__default.createElement(ServiceDialogsListItem, { onOpen: handleSelectServiceChannel, currentUser: auth, channel, refreshing, selectedChannelId: params == null ? void 0 : params.channelId, role: channelRole }) : /* @__PURE__ */ React__default.createElement(DialogsListItem, { onOpen: handleSelectChannel, currentUser: auth, channel, selectedChannelId: params == null ? void 0 : params.channelId }), ListEmptyComponent: () => /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, userChannelsLoading ? /* @__PURE__ */ React__default.createElement(Center, { className: "flex-1 justify-center items-center" }, /* @__PURE__ */ React__default.createElement(Spinner, { color: colors.blue[500] })) : /* @__PURE__ */ React__default.createElement(Box, { className: "p-5" }, /* @__PURE__ */ React__default.createElement(Heading, null, "Chat"), /* @__PURE__ */ React__default.createElement(Input, { className: `h-[50] mt-3 rounded-[50] border-gray-200 border ` }, /* @__PURE__ */ React__default.createElement(InputField, { placeholder: "Search" })), /* @__PURE__ */ React__default.createElement(Center, { className: "mt-6" }, /* @__PURE__ */ React__default.createElement(Ionicons, { name: "chatbubbles", size: 50 }), /* @__PURE__ */ React__default.createElement(Text, null, "You don't have any messages yet!")))), keyExtractor: (item, index) => "key" + index }));
569
+ }) => {
570
+ return (channel == null ? void 0 : channel.type) === RoomType.Service ? /* @__PURE__ */ React__default.createElement(ServiceDialogsListItem, { key: `service-${channel.id}`, onOpen: handleSelectServiceChannel, currentUser: auth, channel, refreshing, selectedChannelId, role: channelRole }) : /* @__PURE__ */ React__default.createElement(DialogsListItem, { key: `direct-${channel.id}`, onOpen: handleSelectChannel, currentUser: auth, channel, selectedChannelId, forceRefresh: true });
571
+ }, ListFooterComponent: () => loadingMore ? /* @__PURE__ */ React__default.createElement(Center, { className: "py-4" }, /* @__PURE__ */ React__default.createElement(Spinner, { color: colors.blue[500], size: "small" })) : null, onEndReached: handleLoadMore, onEndReachedThreshold: 0.3, initialNumToRender: 10, maxToRenderPerBatch: 10, windowSize: 10, removeClippedSubviews: true, updateCellsBatchingPeriod: 50, ListEmptyComponent: () => {
572
+ console.log("Rendering ListEmptyComponent", {
573
+ loading,
574
+ refreshing,
575
+ stateValue: state.value
576
+ });
577
+ if (loading && channels.length === 0) {
578
+ return /* @__PURE__ */ React__default.createElement(Center, { className: "flex-1 justify-center items-center", style: {
579
+ height: 300
580
+ } }, /* @__PURE__ */ React__default.createElement(Spinner, { color: colors.blue[500], size: "large" }), /* @__PURE__ */ React__default.createElement(Text, { className: "mt-4 text-gray-500" }, "Loading conversations..."));
581
+ }
582
+ return /* @__PURE__ */ React__default.createElement(Box, { className: "p-6" }, /* @__PURE__ */ React__default.createElement(Box, { className: "mb-6" }, /* @__PURE__ */ React__default.createElement(Heading, { className: "text-2xl font-bold" }, "Direct Messages"), /* @__PURE__ */ React__default.createElement(Text, { className: "text-gray-600 mt-1" }, "Private conversations with other users")), /* @__PURE__ */ React__default.createElement(Input, { className: "mb-8 h-[50] rounded-md border-gray-300 border", size: "md", style: {
583
+ paddingVertical: 8,
584
+ marginBottom: 10,
585
+ borderColor: "#d1d5db",
586
+ borderRadius: 10
587
+ } }, /* @__PURE__ */ React__default.createElement(InputField, { placeholder: "Search messages...", onChangeText: handleSearchChange, value: searchQuery })), /* @__PURE__ */ React__default.createElement(Center, { className: "items-center", style: {
588
+ paddingVertical: 5
589
+ } }, /* @__PURE__ */ React__default.createElement(Box, { className: "w-16 h-16 rounded-full bg-blue-500 flex items-center justify-center mb-5" }, /* @__PURE__ */ React__default.createElement(Ionicons, { name: "chatbubble-ellipses", size: 30, color: "white" })), /* @__PURE__ */ React__default.createElement(Text, { className: "text-2xl font-bold text-center mb-2" }, "No messages yet"), /* @__PURE__ */ React__default.createElement(Text, { className: "text-gray-600 text-center mb-8" }, "When you start conversations with others,", "\n", "they'll appear here.")));
590
+ }, keyExtractor: (item) => `channel-${item.id}` }));
121
591
  };
122
592
  const Dialogs = React__default.memo(DialogsComponent);export{Dialogs};//# sourceMappingURL=Dialogs.js.map