Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instant reorder of streams upon messaging in inbox and space feeds #2763

Merged
merged 6 commits into from
Nov 18, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
CW-instant-reorder
Added logic for inbox reorder
MeyerPV committed Nov 16, 2024
commit cc29ff29ae96a57c8edfe2732d378b60e209cd8d
10 changes: 8 additions & 2 deletions src/pages/common/components/ChatComponent/ChatComponent.tsx
Original file line number Diff line number Diff line change
@@ -67,6 +67,7 @@ import {
selectOptimisticDiscussionMessages,
inboxActions,
optimisticActions,
selectInstantDiscussionMessagesOrder,
} from "@/store/states";
import { ChatContentContext, ChatContentData } from "../CommonContent/context";
import {
@@ -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;
@@ -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);

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

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

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

if (isChatChannel) {
4 changes: 4 additions & 0 deletions src/pages/common/components/FeedCard/FeedCard.module.scss
Original file line number Diff line number Diff line change
@@ -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
@@ -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(
29 changes: 17 additions & 12 deletions src/pages/inbox/BaseInbox.tsx
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@ import {
selectIsSearchingInboxItems,
selectNextChatChannelItemId,
selectSharedInboxItem,
selectInstantDiscussionMessagesOrder,
} from "@/store/states";
import { ChatChannelItem, FeedItemBaseContent } from "./components";
import { useInboxData } from "./hooks";
@@ -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(
52 changes: 47 additions & 5 deletions src/shared/hooks/useCases/useInboxItems.ts
Original file line number Diff line number Diff line change
@@ -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"> {
@@ -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);
@@ -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;
@@ -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);
@@ -285,7 +326,8 @@ export const useInboxItems = (
return () => {
isMounted = false;
};
}, [lastBatch]);
}, [lastBatch, instantDiscussionMessages]);


return {
...inboxItems,
12 changes: 2 additions & 10 deletions src/store/states/inbox/actions.ts
Original file line number Diff line number Diff line change
@@ -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;
@@ -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,
9 changes: 9 additions & 0 deletions src/store/states/inbox/types.ts
Original file line number Diff line number Diff line change
@@ -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"
@@ -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
@@ -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>();
3 changes: 3 additions & 0 deletions src/store/states/optimistic/constants.ts
Original file line number Diff line number Diff line change
@@ -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
@@ -6,6 +6,7 @@ import {
OptimisticState
} from "./types";
import { generateOptimisticFeedItemFollowWithMetadata } from "@/shared/utils";
import { Timestamp } from "@/shared/models";

type Action = ActionType<typeof actions>;

@@ -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);
3 changes: 3 additions & 0 deletions src/store/states/optimistic/selectors.ts
Original file line number Diff line number Diff line change
@@ -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
@@ -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
@@ -33,7 +33,8 @@ const mapFields: Array<keyof OptimisticState> = [
"createdOptimisticFeedItems",
"optimisticFeedItems",
"optimisticInboxFeedItems",
"optimisticDiscussionMessages"
"optimisticDiscussionMessages",
"instantDiscussionMessagesOrder"
];

const mapTransformer = createTransform<OptimisticState, OptimisticState>(