Skip to content

Commit

Permalink
CW-instant-reorder
Browse files Browse the repository at this point in the history
Added logic for inbox reorder
  • Loading branch information
MeyerPV committed Nov 16, 2024
1 parent 710cd9c commit cc29ff2
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 32 deletions.
10 changes: 8 additions & 2 deletions src/pages/common/components/ChatComponent/ChatComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import {
selectOptimisticDiscussionMessages,
inboxActions,
optimisticActions,
selectInstantDiscussionMessagesOrder,
} from "@/store/states";
import { ChatContentContext, ChatContentData } from "../CommonContent/context";
import {
Expand All @@ -89,6 +90,7 @@ import styles from "./ChatComponent.module.scss";
import { BaseTextEditorHandles } from "@/shared/ui-kit/TextEditor/BaseTextEditor";

const BASE_CHAT_INPUT_HEIGHT = 48;
const BASE_ORDER_INTERVAL = 1000;

interface ChatComponentInterface {
commonId: string;
Expand Down Expand Up @@ -277,6 +279,9 @@ export default function ChatComponent({
const optimisticDiscussionMessages = useSelector(
selectOptimisticDiscussionMessages,
);
const instantDiscussionMessagesOrder = useSelector(selectInstantDiscussionMessagesOrder);

const currentChatOrder = instantDiscussionMessagesOrder.get(discussionId)?.order || 1;

const isOptimisticChat = optimisticFeedItems.has(discussionId);

Expand Down Expand Up @@ -416,8 +421,8 @@ export default function ChatComponent({
setMessages([]);
}
},
1500,
[newMessages, discussionId, dispatch],
1500 + BASE_ORDER_INTERVAL * currentChatOrder,
[newMessages, discussionId, dispatch, currentChatOrder],
);

/**
Expand Down Expand Up @@ -584,6 +589,7 @@ export default function ChatComponent({

return [...prev, ...filePreviewPayload, payload];
});
dispatch(optimisticActions.setInstantDiscussionMessagesOrder({discussionId}));
}

if (isChatChannel) {
Expand Down
4 changes: 4 additions & 0 deletions src/pages/common/components/FeedCard/FeedCard.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
cursor: pointer;
}

.toggleCard {
outline: none;
}

.loader {
align-self: center;
}
4 changes: 2 additions & 2 deletions src/pages/common/components/FeedCard/FeedCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ const FeedCard = (props, ref) => {
]);

return (
<div ref={containerRef}>
{!isPreviewMode && <div {...getToggleProps()}>{feedItemBaseContent}</div>}
<div ref={containerRef} >
{!isPreviewMode && <div className={styles.toggleCard} {...getToggleProps()}>{feedItemBaseContent}</div>}
<div {...getCollapseProps()}>
<CommonCard
className={classNames(
Expand Down
29 changes: 17 additions & 12 deletions src/pages/inbox/BaseInbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
selectIsSearchingInboxItems,
selectNextChatChannelItemId,
selectSharedInboxItem,
selectInstantDiscussionMessagesOrder,
} from "@/store/states";
import { ChatChannelItem, FeedItemBaseContent } from "./components";
import { useInboxData } from "./hooks";
Expand Down Expand Up @@ -181,22 +182,26 @@ const InboxPage: FC<InboxPageProps> = (props) => {
[],
);

const instantDiscussionMessage = useSelector(selectInstantDiscussionMessagesOrder);

const handleFeedItemUpdate = useCallback(
(item: CommonFeed, isRemoved: boolean) => {
dispatch(
inboxActions.updateFeedItem({
item,
isRemoved,
}),
);

if (!isRemoved && item.data.lastMessage?.ownerId === userId) {
document
.getElementById("feedLayoutWrapper")
?.scrollIntoView({ behavior: "smooth" });
if(!instantDiscussionMessage.has(item.data.id)) {
dispatch(
inboxActions.updateFeedItem({
item,
isRemoved,
}),
);

if (!isRemoved && item.data.lastMessage?.ownerId === userId) {
document
.getElementById("feedLayoutWrapper")
?.scrollIntoView({ behavior: "smooth" });
}
}
},
[dispatch],
[dispatch, instantDiscussionMessage],
);

const handleFeedItemUnfollowed = useCallback(
Expand Down
52 changes: 47 additions & 5 deletions src/shared/hooks/useCases/useInboxItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ import {
checkIsFeedItemFollowLayoutItemWithFollowData,
FeedItemFollowLayoutItemWithFollowData,
FeedLayoutItemWithFollowData,
InboxItemBatch,
InboxItemsBatch as ItemsBatch,
} from "@/shared/interfaces";
import { InboxItem, Timestamp } from "@/shared/models";
import {
inboxActions,
InboxItems,
NewInboxItems,
optimisticActions,
selectFilteredInboxItems,
selectInboxItems,
selectInstantDiscussionMessagesOrder,
selectOptimisticInboxFeedItems,
} from "@/store/states";
import { useDeepCompareEffect } from "react-use";

interface Return
extends Pick<InboxItems, "data" | "loading" | "hasMore" | "batchNumber"> {
Expand Down Expand Up @@ -71,6 +75,9 @@ export const useInboxItems = (
): Return => {
const dispatch = useDispatch();
const optimisticInboxItems = useSelector(selectOptimisticInboxFeedItems);
const instantDiscussionMessages = useSelector(
selectInstantDiscussionMessagesOrder,
);
const [newItemsBatches, setNewItemsBatches] = useState<ItemsBatch[]>([]);
const [lastUpdatedAt, setLastUpdatedAt] = useState<Timestamp | null>(null);
const inboxItems = useSelector(selectInboxItems);
Expand Down Expand Up @@ -244,6 +251,33 @@ export const useInboxItems = (
unread,
]);

const [notListedFeedItems, setNotListedFeedItems] = useState<InboxItemBatch<FeedLayoutItemWithFollowData>[]>([]);

useDeepCompareEffect(() => {
if(notListedFeedItems.length > 0 && notListedFeedItems.length === instantDiscussionMessages.size) {
const updatedFeedItems = notListedFeedItems.map((item) => {
const itemData = item?.item as FeedItemFollowLayoutItemWithFollowData;
const feedItemData = itemData.feedItem?.data;
const messageUpdatedAt = instantDiscussionMessages.get(feedItemData?.discussionId ?? "")?.timestamp || instantDiscussionMessages.get(feedItemData.id)?.timestamp;
return {
...item,
item: {
...itemData,
feedItem: {
...itemData.feedItem,
updatedAt: messageUpdatedAt,
}
}
}
}) as NewInboxItems[];

setNotListedFeedItems([]);
dispatch(inboxActions.addNewInboxItems(updatedFeedItems));
dispatch(optimisticActions.clearInstantDiscussionMessagesOrder());
}

},[notListedFeedItems, instantDiscussionMessages]);

useEffect(() => {
if (!lastBatch || !userId) {
return;
Expand All @@ -264,16 +298,23 @@ export const useInboxItems = (
);

if (finalData.length > 0 && isMounted) {
dispatch(inboxActions.addNewInboxItems(finalData));
finalData.forEach(({item}) => {
const itemData = (item as FeedItemFollowLayoutItemWithFollowData).feedItem?.data;

const newItems: InboxItemBatch<FeedLayoutItemWithFollowData>[] = [];
finalData.forEach((item: InboxItemBatch<FeedLayoutItemWithFollowData>) => {
const itemData = (item.item as FeedItemFollowLayoutItemWithFollowData)?.feedItem?.data;

if(instantDiscussionMessages.has(itemData?.discussionId ?? "") || instantDiscussionMessages.has(itemData?.id)) {
setNotListedFeedItems((prev) => [...prev, item]);
} else {
newItems.push(item);
}
if(optimisticInboxItems.has(itemData.id)) {
dispatch(optimisticActions.removeOptimisticInboxFeedItemState({id: itemData.id}));
} else if (itemData?.discussionId && optimisticInboxItems.has(itemData?.discussionId)) {
dispatch(optimisticActions.removeOptimisticInboxFeedItemState({id: itemData?.discussionId}));
}
})

newItems.length > 0 && dispatch(inboxActions.addNewInboxItems(newItems));
}
} catch (error) {
Logger.error(error);
Expand All @@ -285,7 +326,8 @@ export const useInboxItems = (
return () => {
isMounted = false;
};
}, [lastBatch]);
}, [lastBatch, instantDiscussionMessages]);


return {
...inboxItems,
Expand Down
12 changes: 2 additions & 10 deletions src/store/states/inbox/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createAsyncAction, createStandardAction } from "typesafe-actions";
import { FeedLayoutItemWithFollowData } from "@/shared/interfaces";
import { ChatChannel, CommonFeed, LastMessageContentWithMessageId } from "@/shared/models";
import { InboxActionType } from "./constants";
import { InboxItems, InboxSearchState } from "./types";
import { InboxItems, InboxSearchState, NewInboxItems } from "./types";

export const resetInbox = createStandardAction(InboxActionType.RESET_INBOX)<{
onlyIfUnread?: boolean;
Expand All @@ -26,15 +26,7 @@ export const getInboxItems = createAsyncAction(

export const addNewInboxItems = createStandardAction(
InboxActionType.ADD_NEW_INBOX_ITEMS,
)<
{
item: FeedLayoutItemWithFollowData;
statuses: {
isAdded: boolean;
isRemoved: boolean;
};
}[]
>();
)<NewInboxItems[]>();

export const updateInboxItem = createStandardAction(
InboxActionType.UPDATE_INBOX_ITEM,
Expand Down
9 changes: 9 additions & 0 deletions src/store/states/inbox/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ export interface InboxItems {
unread: boolean;
}

export interface NewInboxItems {
item: FeedLayoutItemWithFollowData;
statuses: {
isAdded: boolean;
isRemoved: boolean;
};
}

export type LastState = Pick<
InboxState,
| "items"
Expand All @@ -39,3 +47,4 @@ export interface InboxState {
lastReadState: LastState | null;
lastUnreadState: LastState | null;
}

10 changes: 10 additions & 0 deletions src/store/states/optimistic/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ export const clearOptimisticDiscussionMessages = createStandardAction(
OptimisticActionType.CLEAR_OPTIMISTIC_DISCUSSION_MESSAGES,
)<string>();

export const setInstantDiscussionMessagesOrder = createStandardAction(
OptimisticActionType.SET_INSTANT_DISCUSSION_MESSAGES_ORDER,
)<{
discussionId: string;
}>();

export const clearInstantDiscussionMessagesOrder = createStandardAction(
OptimisticActionType.CLEAR_INSTANT_DISCUSSION_MESSAGES_ORDER,
)();

export const clearCreatedOptimisticFeedItem = createStandardAction(
OptimisticActionType.CLEAR_CREATED_OPTIMISTIC_FEED_ITEM,
)<string>();
Expand Down
3 changes: 3 additions & 0 deletions src/store/states/optimistic/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export enum OptimisticActionType {
SET_OPTIMISTIC_DISCUSSION_MESSAGES = "@OPTIMISTIC/SET_OPTIMISTIC_DISCUSSION_MESSAGES",
CLEAR_OPTIMISTIC_DISCUSSION_MESSAGES = "@OPTIMISTIC/CLEAR_OPTIMISTIC_DISCUSSION_MESSAGES",

SET_INSTANT_DISCUSSION_MESSAGES_ORDER = "@OPTIMISTIC/SET_INSTANT_DISCUSSION_MESSAGES_ORDER",
CLEAR_INSTANT_DISCUSSION_MESSAGES_ORDER = "@OPTIMISTIC/CLEAR_INSTANT_DISCUSSION_MESSAGES_ORDER",

CLEAR_CREATED_OPTIMISTIC_FEED_ITEM = "@OPTIMISTIC/CLEAR_CREATED_OPTIMISTIC_FEED_ITEM",
RESET_OPTIMISTIC_STATE = "RESET_OPTIMISTIC_STATE",
}
44 changes: 44 additions & 0 deletions src/store/states/optimistic/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
OptimisticState
} from "./types";
import { generateOptimisticFeedItemFollowWithMetadata } from "@/shared/utils";
import { Timestamp } from "@/shared/models";

type Action = ActionType<typeof actions>;

Expand All @@ -14,10 +15,53 @@ const initialState: OptimisticState = {
optimisticInboxFeedItems: new Map(),
optimisticDiscussionMessages: new Map(),
createdOptimisticFeedItems: new Map(),
instantDiscussionMessagesOrder: new Map(),
};

export const reducer = createReducer<OptimisticState, Action>(initialState)
.handleAction(actions.resetOptimisticState, () => initialState)
.handleAction(actions.setInstantDiscussionMessagesOrder, (state, { payload }) =>
produce(state, (nextState) => {
const updatedMap = new Map(nextState.instantDiscussionMessagesOrder);
const { discussionId } = payload;

if(updatedMap.size > 1 && updatedMap.has(discussionId)) {
const keys = Array.from(updatedMap.keys());

keys.forEach((key) => {
const orderValue = updatedMap.get(key)?.order || 2;
const timestampValue = updatedMap.get(key)?.timestamp || Timestamp.fromDate(new Date());
updatedMap.set(key, {
order: orderValue === 1 ? 1 : orderValue - 1,
timestamp: timestampValue
});
});
}

if(updatedMap.has(discussionId)) {
updatedMap.set(discussionId, {
order: updatedMap.size || 1,
timestamp: Timestamp.fromDate(new Date())
});
} else {
updatedMap.set(discussionId, {
order: (updatedMap.size + 1) || 1,
timestamp: Timestamp.fromDate(new Date())
});
}
nextState.instantDiscussionMessagesOrder = updatedMap;
}),
)
.handleAction(actions.clearInstantDiscussionMessagesOrder, (state) =>
produce(state, (nextState) => {
const updatedMap = new Map();

// updatedMap.delete(payload);

// Assign the new Map back to the state
nextState.instantDiscussionMessagesOrder = updatedMap;
}),
)
.handleAction(actions.setOptimisticFeedItem, (state, { payload }) =>
produce(state, (nextState) => {
const updatedMap = new Map(nextState.optimisticFeedItems);
Expand Down
3 changes: 3 additions & 0 deletions src/store/states/optimistic/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ export const selectOptimisticDiscussionMessages = (state: AppState) =>

export const selectCreatedOptimisticFeedItems = (state: AppState) =>
state.optimistic.createdOptimisticFeedItems;

export const selectInstantDiscussionMessagesOrder = (state: AppState) =>
state.optimistic.instantDiscussionMessagesOrder;
5 changes: 5 additions & 0 deletions src/store/states/optimistic/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ import {
FeedItemFollowLayoutItem
} from "@/shared/interfaces";
import { CreateDiscussionMessageDto } from "@/shared/interfaces/api/discussionMessages";
import { Timestamp } from "@/shared/models";

export interface OptimisticState {
createdOptimisticFeedItems: Map<string, FeedItemFollowLayoutItem | undefined>;
optimisticFeedItems: Map<string, FeedItemFollowLayoutItem>;
optimisticInboxFeedItems: Map<string, FeedItemFollowLayoutItem>;
optimisticDiscussionMessages: Map<string, CreateDiscussionMessageDto[]>;
instantDiscussionMessagesOrder: Map<string, {
order: number
timestamp: Timestamp;
}>;
}
3 changes: 2 additions & 1 deletion src/store/store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ const mapFields: Array<keyof OptimisticState> = [
"createdOptimisticFeedItems",
"optimisticFeedItems",
"optimisticInboxFeedItems",
"optimisticDiscussionMessages"
"optimisticDiscussionMessages",
"instantDiscussionMessagesOrder"
];

const mapTransformer = createTransform<OptimisticState, OptimisticState>(
Expand Down

0 comments on commit cc29ff2

Please sign in to comment.