diff --git a/src/services/Chat.ts b/src/services/Chat.ts index 581fad5d3a..df9b37ee8b 100644 --- a/src/services/Chat.ts +++ b/src/services/Chat.ts @@ -1,5 +1,5 @@ import { stringify } from "query-string"; -import { ApiEndpoint } from "@/shared/constants"; +import { ApiEndpoint, FirestoreDataSource } from "@/shared/constants"; import { DMUser, UnsubscribeFunction } from "@/shared/interfaces"; import { GetChatChannelMessagesResponse, @@ -20,7 +20,7 @@ import { firestoreDataConverter, getUserName, } from "@/shared/utils"; -import firebase from "@/shared/utils/firebase"; +import firebase, { isFirestoreCacheError } from "@/shared/utils/firebase"; import Api, { CancelToken } from "./Api"; const chatChannelConverter = firestoreDataConverter(); @@ -234,6 +234,31 @@ class ChatService { }); }; + public getChatChannelById = async ( + chatChannelId: string, + source = FirestoreDataSource.Default, + ): Promise => { + try { + const snapshot = await this.getChatChannelCollection() + .doc(chatChannelId) + .get({ source }); + + return snapshot?.data() || null; + } catch (error) { + if ( + source === FirestoreDataSource.Cache && + isFirestoreCacheError(error) + ) { + return this.getChatChannelById( + chatChannelId, + FirestoreDataSource.Server, + ); + } + + throw error; + } + }; + public getChatChannels = async (options: { participantId: string; startAt?: Timestamp; diff --git a/src/services/FeedItemFollows.ts b/src/services/FeedItemFollows.ts index 98a3d99626..eb1751024a 100644 --- a/src/services/FeedItemFollows.ts +++ b/src/services/FeedItemFollows.ts @@ -1,4 +1,4 @@ -import { ApiEndpoint } from "@/shared/constants"; +import { ApiEndpoint, FirestoreDataSource } from "@/shared/constants"; import { DocChange } from "@/shared/constants/docChange"; import { UnsubscribeFunction } from "@/shared/interfaces"; import { FollowFeedItemPayload } from "@/shared/interfaces/api"; @@ -10,7 +10,7 @@ import { Timestamp, } from "@/shared/models"; import { firestoreDataConverter } from "@/shared/utils"; -import firebase from "@/shared/utils/firebase"; +import firebase, { isFirestoreCacheError } from "@/shared/utils/firebase"; import Api, { CancelToken } from "./Api"; import CommonService from "./Common"; import CommonFeedService from "./CommonFeed"; @@ -26,6 +26,33 @@ class FeedItemFollowsService { .collection(SubCollections.FeedItemFollows) .withConverter(converter); + public getFeedItemFollowDataById = async ( + userId: string, + feedItemFollowId: string, + source = FirestoreDataSource.Default, + ): Promise => { + try { + const snapshot = await this.getFeedItemFollowsSubCollection(userId) + .doc(feedItemFollowId) + .get({ source }); + + return snapshot?.data() || null; + } catch (error) { + if ( + source === FirestoreDataSource.Cache && + isFirestoreCacheError(error) + ) { + return this.getFeedItemFollowDataById( + userId, + feedItemFollowId, + FirestoreDataSource.Server, + ); + } + + throw error; + } + }; + public getUserFeedItemFollowData = async ( userId: string, feedItemId: string, diff --git a/src/services/User.ts b/src/services/User.ts index 18c7664f31..bfd7e1af65 100644 --- a/src/services/User.ts +++ b/src/services/User.ts @@ -8,6 +8,8 @@ import { Collection, CommonFeed, FeedItemFollowWithMetadata, + InboxItem, + SubCollections, Timestamp, User, } from "@/shared/models"; @@ -22,11 +24,18 @@ import Api from "./Api"; import { waitForUserToBeLoaded } from "./utils"; const converter = firestoreDataConverter(); +const inboxConverter = firestoreDataConverter(); class UserService { private getUsersCollection = () => firebase.firestore().collection(Collection.Users).withConverter(converter); + private getInboxSubCollection = (userId: string) => + this.getUsersCollection() + .doc(userId) + .collection(SubCollections.Inbox) + .withConverter(inboxConverter); + public updateUser = async (user: User): Promise => { const body: UpdateUserDto = { userId: user.uid, @@ -135,7 +144,7 @@ class UserService { }); }; - public getInboxItems = async ( + public getInboxItemsWithMetadata = async ( options: { startAfter?: Timestamp | null; limit?: number; @@ -197,6 +206,67 @@ class UserService { hasMore: data.hasMore, }; }; + + public getInboxItems = async (options: { + userId: string; + startAt?: Timestamp; + endAt?: Timestamp; + }): Promise => { + const { userId, startAt, endAt } = options; + let query = this.getInboxSubCollection(userId).orderBy( + "itemUpdatedAt", + "desc", + ); + + if (startAt) { + query = query.startAt(startAt); + } + if (endAt) { + query = query.endAt(endAt); + } + + const snapshot = await query.get(); + + return snapshot.docs.map((doc) => doc.data()); + }; + + public subscribeToNewInboxItems = ( + options: { + userId: string; + endBefore: Timestamp; + unread?: boolean; + orderBy?: "itemUpdatedAt" | "updatedAt"; + }, + callback: ( + data: { + item: InboxItem; + statuses: { + isAdded: boolean; + isRemoved: boolean; + }; + }[], + ) => void, + ): UnsubscribeFunction => { + const { userId, endBefore, unread, orderBy = "itemUpdatedAt" } = options; + let query = this.getInboxSubCollection(userId) + .orderBy(orderBy, "desc") + .endBefore(endBefore); + + if (unread) { + query = query.where("unread", "==", true); + } + + return query.onSnapshot((snapshot) => { + const data = snapshot.docChanges().map((docChange) => ({ + item: docChange.doc.data(), + statuses: { + isAdded: docChange.type === "added", + isRemoved: docChange.type === "removed", + }, + })); + callback(data); + }); + }; } export default new UserService(); diff --git a/src/shared/hooks/useCases/useInboxItems.ts b/src/shared/hooks/useCases/useInboxItems.ts index 91aee3b7a3..9355d4258e 100644 --- a/src/shared/hooks/useCases/useInboxItems.ts +++ b/src/shared/hooks/useCases/useInboxItems.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { selectUser } from "@/pages/Auth/store/selectors"; import { @@ -7,18 +7,22 @@ import { CommonService, FeedItemFollowsService, Logger, + UserService, } from "@/services"; -import { InboxItemType } from "@/shared/constants"; +import { FirestoreDataSource, InboxItemType } from "@/shared/constants"; import { useIsMounted } from "@/shared/hooks"; -import { FeedLayoutItemWithFollowData } from "@/shared/interfaces"; +import { + ChatChannelLayoutItem, + FeedLayoutItemWithFollowData, +} from "@/shared/interfaces"; import { ChatChannel, - CommonFeedType, FeedItemFollow, FeedItemFollowWithMetadata, + InboxItem, + Timestamp, } from "@/shared/models"; import { inboxActions, InboxItems, selectInboxItems } from "@/store/states"; -import { useUnreadInboxItems } from "./useUnreadInboxItems"; interface Return extends Pick { @@ -26,7 +30,7 @@ interface Return refetch: () => void; } -interface ItemBatch { +interface ItemBatch { item: T; statuses: { isAdded: boolean; @@ -36,8 +40,8 @@ interface ItemBatch { type ItemsBatch = ItemBatch[]; -const addMetadataToItemsBatch = async ( - batch: ItemsBatch, +const addMetadataToFeedItemFollowItemsBatch = async ( + batch: ItemBatch[], ): Promise[]> => { const data = await Promise.all( batch.map(async ({ item }) => { @@ -117,6 +121,65 @@ const addMetadataToItemsBatch = async ( })); }; +const addMetadataToChatChannelsBatch = ( + batch: ItemBatch[], +): ItemBatch[] => + batch + .filter(({ item: chatChannel }) => chatChannel.messageCount > 0) + .map(({ item: chatChannel, statuses }) => ({ + item: { + itemId: chatChannel.id, + type: InboxItemType.ChatChannel, + chatChannel: chatChannel, + }, + statuses, + })); + +const addMetadataToItemsBatch = async ( + userId: string, + batch: ItemsBatch, + feedItemIdsForNotListening: string[] = [], +): Promise[]> => { + const batchWithFeedItemFollowItems = ( + await Promise.all( + batch.map(async (batchItem) => { + const item = + batchItem.item.type === InboxItemType.FeedItemFollow + ? await FeedItemFollowsService.getFeedItemFollowDataById( + userId, + batchItem.item.itemId, + FirestoreDataSource.Cache, + ) + : null; + + return item ? { item, statuses: batchItem.statuses } : null; + }, []), + ) + ).filter((item): item is ItemBatch => Boolean(item)); + const batchWithChatChannelLayoutItems = ( + await Promise.all( + batch.map(async (batchItem) => { + const item = + batchItem.item.type === InboxItemType.ChatChannel + ? await ChatService.getChatChannelById( + batchItem.item.itemId, + FirestoreDataSource.Cache, + ) + : null; + + return item ? { item, statuses: batchItem.statuses } : null; + }, []), + ) + ).filter((item): item is ItemBatch => Boolean(item)); + const batchWithFeedLayoutItemWithFollowItems = + await addMetadataToFeedItemFollowItemsBatch(batchWithFeedItemFollowItems); + + return [ + ...batchWithFeedLayoutItemWithFollowItems, + ...addMetadataToChatChannelsBatch(batchWithChatChannelLayoutItems), + ].filter(({ item }) => !feedItemIdsForNotListening.includes(item.itemId)); +}; + export const useInboxItems = ( feedItemIdsForNotListening?: string[], options?: { unread?: boolean }, @@ -124,12 +187,14 @@ export const useInboxItems = ( const dispatch = useDispatch(); const isMounted = useIsMounted(); const [newItemsBatches, setNewItemsBatches] = useState([]); + const [lastUpdatedAt, setLastUpdatedAt] = useState(null); const inboxItems = useSelector(selectInboxItems); const user = useSelector(selectUser()); + const inboxItemsRef = useRef(inboxItems); const userId = user?.uid; const unread = options?.unread; const lastBatch = newItemsBatches[0]; - useUnreadInboxItems(options?.unread); + inboxItemsRef.current = inboxItems; const fetch = () => { dispatch( @@ -145,43 +210,9 @@ export const useInboxItems = ( fetch(); }; - const addNewChatChannels = ( + const addNewInboxItems = ( data: { - chatChannel: ChatChannel; - statuses: { - isAdded: boolean; - isRemoved: boolean; - }; - }[], - ) => { - const finalData = - feedItemIdsForNotListening && feedItemIdsForNotListening.length > 0 - ? data.filter( - (item) => !feedItemIdsForNotListening.includes(item.chatChannel.id), - ) - : data; - - if (finalData.length === 0) { - return; - } - - dispatch( - inboxActions.addNewInboxItems( - finalData.map((item) => ({ - item: { - itemId: item.chatChannel.id, - type: InboxItemType.ChatChannel, - chatChannel: item.chatChannel, - }, - statuses: item.statuses, - })), - ), - ); - }; - - const addNewFollowFeedItems = ( - data: { - item: FeedItemFollow; + item: InboxItem; statuses: { isAdded: boolean; isRemoved: boolean; @@ -192,58 +223,40 @@ export const useInboxItems = ( return; } - const filteredData = data.filter(({ item }) => - [CommonFeedType.Discussion, CommonFeedType.Proposal].includes(item.type), - ); - const finalData = - feedItemIdsForNotListening && feedItemIdsForNotListening.length > 0 - ? filteredData.filter( - (item) => - !feedItemIdsForNotListening.includes(item.item.feedItemId), - ) - : filteredData; - setNewItemsBatches((currentItems) => [...currentItems, finalData]); + setNewItemsBatches((currentItems) => [...currentItems, data]); }; useEffect(() => { (async () => { try { - const { firstDocTimestamp: startAt, lastDocTimestamp: endAt } = - inboxItems; + const { + data, + firstDocTimestamp: startAt, + lastDocTimestamp: endAt, + } = inboxItems; if (!userId || !startAt || !endAt) { return; } - const [chatChannels, feedItemFollows] = await Promise.all([ - ChatService.getChatChannels({ - participantId: userId, - startAt, - endAt, - onlyWithMessages: true, - }), - FeedItemFollowsService.getFollowFeedItems({ - userId, - startAt, - endAt, - }), - ]); + const fetchedInboxItems = await UserService.getInboxItems({ + userId, + startAt, + endAt, + }); if (!isMounted()) { return; } - addNewChatChannels( - chatChannels.map((chatChannel) => ({ - chatChannel, - statuses: { - isAdded: false, - isRemoved: false, - }, - })), - ); - addNewFollowFeedItems( - feedItemFollows.map((item) => ({ + const filteredInboxItems = data + ? fetchedInboxItems.filter((fetchedItem) => + data.every((item) => item.itemId !== fetchedItem.itemId), + ) + : fetchedInboxItems; + + addNewInboxItems( + filteredInboxItems.map((item) => ({ item, statuses: { isAdded: false, @@ -258,16 +271,17 @@ export const useInboxItems = ( }, []); useEffect(() => { - if (!inboxItems.firstDocTimestamp || !userId || unread) { + if (!inboxItems.firstDocTimestamp || !userId) { return; } - const unsubscribe = ChatService.subscribeToNewUpdatedChatChannels( - userId, - inboxItems.firstDocTimestamp, - (data) => { - addNewChatChannels(data); + const unsubscribe = UserService.subscribeToNewInboxItems( + { + userId, + endBefore: inboxItems.firstDocTimestamp, + unread, }, + addNewInboxItems, ); return unsubscribe; @@ -279,29 +293,57 @@ export const useInboxItems = ( ]); useEffect(() => { - if (!inboxItems.firstDocTimestamp || !userId || unread) { + const endBefore = lastUpdatedAt || inboxItems.firstDocTimestamp; + + if (!endBefore || !userId) { return; } - const unsubscribe = - FeedItemFollowsService.subscribeToNewUpdatedFollowFeedItem( + const unsubscribe = UserService.subscribeToNewInboxItems( + { userId, - inboxItems.firstDocTimestamp, - (data) => { - addNewFollowFeedItems(data); - }, - ); + endBefore, + unread, + orderBy: "updatedAt", + }, + (data) => { + const lastDocTimestampSeconds = + inboxItemsRef.current?.lastDocTimestamp?.seconds; + const currentInboxData = inboxItemsRef.current?.data; + const filteredData = + lastDocTimestampSeconds && currentInboxData + ? data.filter( + ({ item }) => + item.itemUpdatedAt.seconds >= lastDocTimestampSeconds && + !currentInboxData.some( + (currentItem) => currentItem.itemId === item.itemId, + ), + ) + : []; + + if (data[0]) { + setLastUpdatedAt(data[0].item.updatedAt); + } + + addNewInboxItems(filteredData); + + if (filteredData.length !== data.length) { + dispatch(inboxActions.setHasMoreInboxItems(true)); + } + }, + ); return unsubscribe; }, [ inboxItems.firstDocTimestamp, + lastUpdatedAt, userId, feedItemIdsForNotListening, unread, ]); useEffect(() => { - if (!lastBatch) { + if (!lastBatch || !userId) { return; } if (lastBatch.length === 0) { @@ -311,8 +353,15 @@ export const useInboxItems = ( (async () => { try { - const finalData = await addMetadataToItemsBatch(lastBatch); - dispatch(inboxActions.addNewInboxItems(finalData)); + const finalData = await addMetadataToItemsBatch( + userId, + lastBatch, + feedItemIdsForNotListening, + ); + + if (finalData.length > 0) { + dispatch(inboxActions.addNewInboxItems(finalData)); + } } catch (error) { Logger.error(error); } finally { diff --git a/src/shared/hooks/useCases/useUnreadInboxItems.ts b/src/shared/hooks/useCases/useUnreadInboxItems.ts deleted file mode 100644 index 6b3ae8f6c0..0000000000 --- a/src/shared/hooks/useCases/useUnreadInboxItems.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useEffect } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { usePreviousDistinct, useUpdateEffect } from "react-use"; -import { selectUserStreamsWithNotificationsAmount } from "@/pages/Auth/store/selectors"; -import { inboxActions } from "@/store/states"; - -export const useUnreadInboxItems = (unread?: boolean): void => { - const dispatch = useDispatch(); - const notificationsAmount = useSelector( - selectUserStreamsWithNotificationsAmount(), - ); - const previousNotificationsAmount = usePreviousDistinct(notificationsAmount); - - useUpdateEffect(() => { - if ( - !unread || - !notificationsAmount || - (typeof previousNotificationsAmount === "number" && - notificationsAmount < previousNotificationsAmount) - ) { - return; - } - - dispatch(inboxActions.refreshUnreadInboxItems.request()); - }, [notificationsAmount]); - - useEffect(() => { - return () => { - dispatch( - inboxActions.refreshUnreadInboxItems.cancel( - "Cancel unread inbox items refresh on unmount", - ), - ); - }; - }, []); -}; diff --git a/src/shared/models/InboxItem.ts b/src/shared/models/InboxItem.ts index cbf79c3326..43caa507b3 100644 --- a/src/shared/models/InboxItem.ts +++ b/src/shared/models/InboxItem.ts @@ -1,8 +1,34 @@ import { InboxItemType } from "@/shared/constants"; +import { CommonFeedType } from "./CommonFeed"; import { Timestamp } from "./Timestamp"; -export interface InboxItem { +export type InboxItemFeedItemData = + | { + feedItemType: CommonFeedType.Discussion; + discussionId: string; + } + | { + feedItemType: CommonFeedType.Proposal; + proposalId: string; + discussionId: string | null; + } + | { + feedItemType: Exclude< + CommonFeedType, + CommonFeedType.Discussion | CommonFeedType.Proposal + >; + }; + +export type InboxItem = { itemId: string; // id of feedItemFollow or ChatChannel updatedAt: Timestamp; - type: InboxItemType; -} + itemUpdatedAt: Timestamp; + unread: boolean; +} & ( + | ({ + type: InboxItemType.FeedItemFollow; + } & InboxItemFeedItemData) + | { + type: InboxItemType.ChatChannel; + } +); diff --git a/src/shared/models/index.tsx b/src/shared/models/index.tsx index fbb74b0468..26a3c1e922 100644 --- a/src/shared/models/index.tsx +++ b/src/shared/models/index.tsx @@ -15,6 +15,7 @@ export * from "./DiscussionMessage"; export * from "./Discussion"; export * from "./FeedItemFollow"; export * from "./Gender"; +export * from "./InboxItem"; export * from "./Invoices"; export * from "./Link"; export * from "./Payment"; diff --git a/src/shared/models/shared.tsx b/src/shared/models/shared.tsx index 60980a54d1..2858e1e69f 100644 --- a/src/shared/models/shared.tsx +++ b/src/shared/models/shared.tsx @@ -43,7 +43,7 @@ export enum SubCollections { ChatChannelUserUnique = "chatChannelUserUnique", CommonFeed = "commonFeed", CommonFeedObjectUserUnique = "commonFeedObjectUserUnique", - Inbox = "Inbox", + Inbox = "inbox", FeedItemFollows = "feedItemFollows", } diff --git a/src/store/states/inbox/actions.ts b/src/store/states/inbox/actions.ts index aad2217412..b8790864cc 100644 --- a/src/store/states/inbox/actions.ts +++ b/src/store/states/inbox/actions.ts @@ -23,13 +23,6 @@ export const getInboxItems = createAsyncAction( string >(); -export const refreshUnreadInboxItems = createAsyncAction( - InboxActionType.REFRESH_UNREAD_INBOX_ITEMS, - InboxActionType.REFRESH_UNREAD_INBOX_ITEMS_SUCCESS, - InboxActionType.REFRESH_UNREAD_INBOX_ITEMS_FAILURE, - InboxActionType.REFRESH_UNREAD_INBOX_ITEMS_CANCEL, -)(); - export const addNewInboxItems = createStandardAction( InboxActionType.ADD_NEW_INBOX_ITEMS, )< @@ -74,6 +67,10 @@ export const resetInboxItems = createStandardAction( InboxActionType.RESET_INBOX_ITEMS, )(); +export const setHasMoreInboxItems = createStandardAction( + InboxActionType.SET_HAS_MORE_INBOX_ITEMS, +)(); + export const setSharedFeedItemId = createStandardAction( InboxActionType.SET_SHARED_FEED_ITEM_ID, )(); diff --git a/src/store/states/inbox/constants.ts b/src/store/states/inbox/constants.ts index bc42bc1710..123079defb 100644 --- a/src/store/states/inbox/constants.ts +++ b/src/store/states/inbox/constants.ts @@ -6,11 +6,6 @@ export enum InboxActionType { GET_INBOX_ITEMS_FAILURE = "@INBOX/GET_INBOX_ITEMS_FAILURE", GET_INBOX_ITEMS_CANCEL = "@INBOX/GET_INBOX_ITEMS_CANCEL", - REFRESH_UNREAD_INBOX_ITEMS = "@INBOX/REFRESH_UNREAD_INBOX_ITEMS", - REFRESH_UNREAD_INBOX_ITEMS_SUCCESS = "@INBOX/REFRESH_UNREAD_INBOX_ITEMS_SUCCESS", - REFRESH_UNREAD_INBOX_ITEMS_FAILURE = "@INBOX/REFRESH_UNREAD_INBOX_ITEMS_FAILURE", - REFRESH_UNREAD_INBOX_ITEMS_CANCEL = "@INBOX/REFRESH_UNREAD_INBOX_ITEMS_CANCEL", - ADD_NEW_INBOX_ITEMS = "@INBOX/ADD_NEW_INBOX_ITEMS", UPDATE_INBOX_ITEM = "@INBOX/UPDATE_INBOX_ITEM", @@ -20,6 +15,8 @@ export enum InboxActionType { RESET_INBOX_ITEMS = "@INBOX/RESET_INBOX_ITEMS", + SET_HAS_MORE_INBOX_ITEMS = "@INBOX/SET_HAS_MORE_INBOX_ITEMS", + SET_SHARED_FEED_ITEM_ID = "@INBOX/SET_SHARED_FEED_ITEM_ID", SET_SHARED_INBOX_ITEM = "@INBOX/SET_SHARED_INBOX_ITEM", diff --git a/src/store/states/inbox/reducer.ts b/src/store/states/inbox/reducer.ts index c6d488bebb..4888cf07ec 100644 --- a/src/store/states/inbox/reducer.ts +++ b/src/store/states/inbox/reducer.ts @@ -558,6 +558,11 @@ export const reducer = createReducer(INITIAL_INBOX_STATE) nextState.nextChatChannelItemId = null; }), ) + .handleAction(actions.setHasMoreInboxItems, (state, { payload }) => + produce(state, (nextState) => { + nextState.items.hasMore = payload; + }), + ) .handleAction(actions.setSharedFeedItemId, (state, { payload }) => produce(state, (nextState) => { nextState.sharedFeedItemId = payload; diff --git a/src/store/states/inbox/saga/getInboxItems.ts b/src/store/states/inbox/saga/getInboxItems.ts index d69503832f..231c88714e 100644 --- a/src/store/states/inbox/saga/getInboxItems.ts +++ b/src/store/states/inbox/saga/getInboxItems.ts @@ -34,13 +34,13 @@ export function* getInboxItems( const currentItems = (yield select(selectInboxItems)) as InboxItems; const isFirstRequest = !currentItems.lastDocTimestamp; const { data, firstDocTimestamp, lastDocTimestamp, hasMore } = (yield call( - UserService.getInboxItems, + UserService.getInboxItemsWithMetadata, { startAfter: currentItems.lastDocTimestamp, limit, unread, }, - )) as Awaited>; + )) as Awaited>; const chatChannelItems = data.chatChannels .map((item) => ChatChannelToLayoutItemConverter.toTargetEntity(item)) .filter((item) => item.chatChannel.messageCount > 0); diff --git a/src/store/states/inbox/saga/index.ts b/src/store/states/inbox/saga/index.ts index b71cbe048b..f040cb9624 100644 --- a/src/store/states/inbox/saga/index.ts +++ b/src/store/states/inbox/saga/index.ts @@ -1,7 +1,6 @@ import { takeLatestWithCancel } from "@/shared/utils/saga"; import * as actions from "../actions"; import { getInboxItems } from "./getInboxItems"; -import { refreshUnreadInboxItems } from "./refreshUnreadInboxItems"; export function* mainSaga() { yield takeLatestWithCancel( @@ -9,9 +8,4 @@ export function* mainSaga() { actions.getInboxItems.cancel, getInboxItems, ); - yield takeLatestWithCancel( - actions.refreshUnreadInboxItems.request, - actions.refreshUnreadInboxItems.cancel, - refreshUnreadInboxItems, - ); } diff --git a/src/store/states/inbox/saga/refreshUnreadInboxItems.ts b/src/store/states/inbox/saga/refreshUnreadInboxItems.ts deleted file mode 100644 index f2f61f2681..0000000000 --- a/src/store/states/inbox/saga/refreshUnreadInboxItems.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { call, put, select } from "redux-saga/effects"; -import { Logger, UserService } from "@/services"; -import { - ChatChannelToLayoutItemConverter, - FeedItemFollowToLayoutItemWithFollowDataConverter, -} from "@/shared/converters"; -import { Awaited, FeedLayoutItemWithFollowData } from "@/shared/interfaces"; -import { Timestamp } from "@/shared/models"; -import * as actions from "../actions"; -import { selectInboxItems } from "../selectors"; -import { InboxItems } from "../types"; - -export function* refreshUnreadInboxItems() { - try { - const currentItems = (yield select(selectInboxItems)) as InboxItems; - const newInboxItems: FeedLayoutItemWithFollowData[] = []; - let startAfter: Timestamp | null = null; - let keepItemsFetching = true; - - while (keepItemsFetching) { - const { data, lastDocTimestamp, hasMore } = (yield call( - UserService.getInboxItems, - { - startAfter, - limit: 5, - unread: true, - }, - )) as Awaited>; - const chatChannelItems = data.chatChannels - .filter( - (chatChannel) => - chatChannel.messageCount > 0 && - (!currentItems.data || - currentItems.data.every( - (item) => item.itemId !== chatChannel.id, - )), - ) - .map((item) => ChatChannelToLayoutItemConverter.toTargetEntity(item)); - const feedItemFollowItems = data.feedItemFollows - .filter( - (feedItemFollow) => - !currentItems.data || - currentItems.data.every( - (item) => item.itemId !== feedItemFollow.id, - ), - ) - .map((item) => - FeedItemFollowToLayoutItemWithFollowDataConverter.toTargetEntity( - item, - ), - ); - newInboxItems.push(...chatChannelItems, ...feedItemFollowItems); - keepItemsFetching = hasMore; - startAfter = lastDocTimestamp; - } - - if (newInboxItems.length > 0) { - yield put( - actions.addNewInboxItems( - newInboxItems.map((item) => ({ - item, - statuses: { - isAdded: false, - isRemoved: false, - }, - })), - ), - ); - } - } catch (err) { - Logger.error(err); - } -}