nicolium: migrate conversations
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -1,129 +0,0 @@
|
||||
import { isLoggedIn } from '@/utils/auth';
|
||||
|
||||
import { getClient } from '../api';
|
||||
|
||||
import { importEntities } from './importer';
|
||||
|
||||
import type { AppDispatch, RootState } from '@/store';
|
||||
import type { Account, Conversation, PaginatedResponse } from 'pl-api';
|
||||
|
||||
const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT' as const;
|
||||
const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT' as const;
|
||||
|
||||
const CONVERSATIONS_FETCH_REQUEST = 'CONVERSATIONS_FETCH_REQUEST' as const;
|
||||
const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS' as const;
|
||||
const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL' as const;
|
||||
const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE' as const;
|
||||
|
||||
const CONVERSATIONS_READ = 'CONVERSATIONS_READ' as const;
|
||||
|
||||
const mountConversations = () => ({ type: CONVERSATIONS_MOUNT });
|
||||
|
||||
const unmountConversations = () => ({ type: CONVERSATIONS_UNMOUNT });
|
||||
|
||||
interface ConversationsReadAction {
|
||||
type: typeof CONVERSATIONS_READ;
|
||||
conversationId: string;
|
||||
}
|
||||
|
||||
const markConversationRead =
|
||||
(conversationId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
dispatch<ConversationsReadAction>({
|
||||
type: CONVERSATIONS_READ,
|
||||
conversationId,
|
||||
});
|
||||
|
||||
return getClient(getState).timelines.markConversationRead(conversationId);
|
||||
};
|
||||
|
||||
const expandConversations =
|
||||
(expand = true) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
const state = getState();
|
||||
if (state.conversations.isLoading) return;
|
||||
|
||||
const hasMore = state.conversations.hasMore;
|
||||
if (expand && !hasMore) return;
|
||||
|
||||
dispatch(expandConversationsRequest());
|
||||
|
||||
return (state.conversations.next?.() ?? getClient(state).timelines.getConversations())
|
||||
.then((response) => {
|
||||
dispatch(
|
||||
importEntities({
|
||||
accounts: response.items.reduce(
|
||||
(aggr: Array<Account>, item) => aggr.concat(item.accounts),
|
||||
[],
|
||||
),
|
||||
statuses: response.items.map((item) => item.last_status),
|
||||
}),
|
||||
);
|
||||
dispatch(expandConversationsSuccess(response.items, response.next, expand));
|
||||
})
|
||||
.catch((err) => dispatch(expandConversationsFail(err)));
|
||||
};
|
||||
|
||||
const expandConversationsRequest = () => ({ type: CONVERSATIONS_FETCH_REQUEST });
|
||||
|
||||
const expandConversationsSuccess = (
|
||||
conversations: Conversation[],
|
||||
next: (() => Promise<PaginatedResponse<Conversation>>) | null,
|
||||
isLoadingRecent: boolean,
|
||||
) => ({
|
||||
type: CONVERSATIONS_FETCH_SUCCESS,
|
||||
conversations,
|
||||
next,
|
||||
isLoadingRecent,
|
||||
});
|
||||
|
||||
const expandConversationsFail = (error: unknown) => ({
|
||||
type: CONVERSATIONS_FETCH_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
interface ConversataionsUpdateAction {
|
||||
type: typeof CONVERSATIONS_UPDATE;
|
||||
conversation: Conversation;
|
||||
}
|
||||
|
||||
const updateConversations = (conversation: Conversation) => (dispatch: AppDispatch) => {
|
||||
dispatch(
|
||||
importEntities({
|
||||
accounts: conversation.accounts,
|
||||
statuses: [conversation.last_status],
|
||||
}),
|
||||
);
|
||||
|
||||
return dispatch<ConversataionsUpdateAction>({
|
||||
type: CONVERSATIONS_UPDATE,
|
||||
conversation,
|
||||
});
|
||||
};
|
||||
|
||||
type ConversationsAction =
|
||||
| ReturnType<typeof mountConversations>
|
||||
| ReturnType<typeof unmountConversations>
|
||||
| ConversationsReadAction
|
||||
| ReturnType<typeof expandConversationsRequest>
|
||||
| ReturnType<typeof expandConversationsSuccess>
|
||||
| ReturnType<typeof expandConversationsFail>
|
||||
| ConversataionsUpdateAction;
|
||||
|
||||
export {
|
||||
CONVERSATIONS_MOUNT,
|
||||
CONVERSATIONS_UNMOUNT,
|
||||
CONVERSATIONS_FETCH_REQUEST,
|
||||
CONVERSATIONS_FETCH_SUCCESS,
|
||||
CONVERSATIONS_FETCH_FAIL,
|
||||
CONVERSATIONS_UPDATE,
|
||||
CONVERSATIONS_READ,
|
||||
mountConversations,
|
||||
unmountConversations,
|
||||
markConversationRead,
|
||||
expandConversations,
|
||||
updateConversations,
|
||||
type ConversationsAction,
|
||||
};
|
||||
@ -1,6 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { updateConversations } from '@/actions/conversations';
|
||||
import { fetchFilters } from '@/actions/filters';
|
||||
import { MARKER_FETCH_SUCCESS } from '@/actions/markers';
|
||||
import { updateNotificationsQueue } from '@/actions/notifications';
|
||||
@ -12,6 +11,7 @@ import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useLoggedIn } from '@/hooks/use-logged-in';
|
||||
import messages from '@/messages';
|
||||
import { queryClient } from '@/queries/client';
|
||||
import { updateConversations } from '@/queries/conversations/use-conversations';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
import { getUnreadChatsCount, updateChatListItem } from '@/utils/chats';
|
||||
import { play, soundCache } from '@/utils/sounds';
|
||||
@ -131,7 +131,7 @@ const useUserStream = () => {
|
||||
});
|
||||
break;
|
||||
case 'conversation':
|
||||
dispatch(updateConversations(event.payload));
|
||||
updateConversations(event.payload);
|
||||
break;
|
||||
case 'filters_changed':
|
||||
dispatch(fetchFilters());
|
||||
|
||||
@ -1,32 +1,29 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import React from 'react';
|
||||
|
||||
import { markConversationRead } from '@/actions/conversations';
|
||||
import { useAccount } from '@/api/hooks/accounts/use-account';
|
||||
import StatusContainer from '@/containers/status-container';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from '@/hooks/use-app-selector';
|
||||
import {
|
||||
useMarkConversationRead,
|
||||
type MinifiedConversation,
|
||||
} from '@/queries/conversations/use-conversations';
|
||||
|
||||
interface IConversation {
|
||||
conversationId: string;
|
||||
conversation: MinifiedConversation;
|
||||
onMoveUp: (id: string) => void;
|
||||
onMoveDown: (id: string) => void;
|
||||
}
|
||||
|
||||
const Conversation: React.FC<IConversation> = ({ conversationId, onMoveUp, onMoveDown }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const Conversation: React.FC<IConversation> = ({ conversation, onMoveUp, onMoveDown }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
account_ids,
|
||||
unread,
|
||||
last_status: lastStatusId,
|
||||
} = useAppSelector((state) => state.conversations.items.find((x) => x.id === conversationId)!);
|
||||
const { id: conversationId, account_ids, unread, last_status: lastStatusId } = conversation;
|
||||
const { mutate: markConversationRead } = useMarkConversationRead(conversationId);
|
||||
const { account: lastStatusAccount } = useAccount(account_ids[0]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (unread) {
|
||||
dispatch(markConversationRead(conversationId));
|
||||
markConversationRead();
|
||||
}
|
||||
|
||||
if (lastStatusId)
|
||||
|
||||
@ -2,10 +2,9 @@ import { debounce } from '@tanstack/react-pacer/debouncer';
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { expandConversations } from '@/actions/conversations';
|
||||
import ScrollableList from '@/components/scrollable-list';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from '@/hooks/use-app-selector';
|
||||
import PlaceholderStatus from '@/features/placeholder/components/placeholder-status';
|
||||
import { useConversations } from '@/queries/conversations/use-conversations';
|
||||
import { selectChild } from '@/utils/scroll-utils';
|
||||
|
||||
import Conversation from './conversation';
|
||||
@ -13,12 +12,9 @@ import Conversation from './conversation';
|
||||
import type { VirtuosoHandle } from 'react-virtuoso';
|
||||
|
||||
const ConversationsList: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const ref = useRef<VirtuosoHandle>(null);
|
||||
|
||||
const conversations = useAppSelector((state) => state.conversations.items);
|
||||
const isLoading = useAppSelector((state) => state.conversations.isLoading);
|
||||
const hasMore = useAppSelector((state) => state.conversations.hasMore);
|
||||
const { conversations, isLoading, hasNextPage, isFetching, fetchNextPage } = useConversations();
|
||||
|
||||
const getCurrentIndex = (id: string) => conversations.findIndex((x) => x.id === id);
|
||||
|
||||
@ -40,22 +36,22 @@ const ConversationsList: React.FC = () => {
|
||||
const handleLoadOlder = useCallback(
|
||||
debounce(
|
||||
() => {
|
||||
if (hasMore) dispatch(expandConversations());
|
||||
if (hasNextPage) fetchNextPage();
|
||||
},
|
||||
{ wait: 300, leading: true },
|
||||
),
|
||||
[hasMore],
|
||||
[hasNextPage, fetchNextPage],
|
||||
);
|
||||
|
||||
return (
|
||||
<ScrollableList
|
||||
scrollKey='direct'
|
||||
ref={ref}
|
||||
hasMore={hasMore}
|
||||
hasMore={hasNextPage}
|
||||
onLoadMore={handleLoadOlder}
|
||||
id='direct-list'
|
||||
isLoading={isLoading}
|
||||
showLoading={isLoading && conversations.length === 0}
|
||||
isLoading={isFetching}
|
||||
showLoading={isLoading}
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.direct'
|
||||
@ -63,11 +59,13 @@ const ConversationsList: React.FC = () => {
|
||||
/>
|
||||
}
|
||||
listClassName='⁂-status-list'
|
||||
placeholderComponent={PlaceholderStatus}
|
||||
placeholderCount={20}
|
||||
>
|
||||
{conversations.map((item: any) => (
|
||||
{conversations.map((item) => (
|
||||
<Conversation
|
||||
key={item.id}
|
||||
conversationId={item.id}
|
||||
conversation={item}
|
||||
onMoveUp={handleMoveUp}
|
||||
onMoveDown={handleMoveDown}
|
||||
/>
|
||||
|
||||
@ -1,15 +1,9 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import {
|
||||
mountConversations,
|
||||
unmountConversations,
|
||||
expandConversations,
|
||||
} from '@/actions/conversations';
|
||||
import { useDirectStream } from '@/api/hooks/streaming/use-direct-stream';
|
||||
import Column from '@/components/ui/column';
|
||||
import ConversationsList from '@/features/conversations/components/conversations-list';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.direct', defaultMessage: 'Direct messages' },
|
||||
@ -18,19 +12,9 @@ const messages = defineMessages({
|
||||
|
||||
const ConversationsTimeline = () => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useDirectStream();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(mountConversations());
|
||||
dispatch(expandConversations(false));
|
||||
|
||||
return () => {
|
||||
dispatch(unmountConversations());
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.title)}>
|
||||
<ConversationsList />
|
||||
|
||||
166
packages/pl-fe/src/queries/conversations/use-conversations.ts
Normal file
166
packages/pl-fe/src/queries/conversations/use-conversations.ts
Normal file
@ -0,0 +1,166 @@
|
||||
import {
|
||||
type InfiniteData,
|
||||
useInfiniteQuery,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query';
|
||||
import { create } from 'mutative';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { importEntities } from '@/actions/importer';
|
||||
import { useClient } from '@/hooks/use-client';
|
||||
import { useLoggedIn } from '@/hooks/use-logged-in';
|
||||
import { store } from '@/store';
|
||||
import { compareDate } from '@/utils/comparators';
|
||||
|
||||
import { queryClient } from '../client';
|
||||
import { updatePaginatedResponse } from '../utils/update-paginated-response';
|
||||
|
||||
import type { Conversation, PaginatedResponse } from 'pl-api';
|
||||
|
||||
type MinifiedConversation = {
|
||||
id: string;
|
||||
unread: boolean;
|
||||
account_ids: string[];
|
||||
last_status: string | null;
|
||||
last_status_created_at: string | null;
|
||||
};
|
||||
|
||||
type MinifiedConversationPage = PaginatedResponse<MinifiedConversation>;
|
||||
|
||||
const minifyConversation = (conversation: Conversation): MinifiedConversation => ({
|
||||
id: conversation.id,
|
||||
unread: conversation.unread,
|
||||
account_ids: conversation.accounts.map((account) => account.id),
|
||||
last_status: conversation.last_status?.id ?? null,
|
||||
last_status_created_at: conversation.last_status?.created_at ?? null,
|
||||
});
|
||||
|
||||
const sortConversations = (items: MinifiedConversation[]) =>
|
||||
items.toSorted((a, b) => {
|
||||
if (a.last_status_created_at === null || b.last_status_created_at === null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return compareDate(a.last_status_created_at, b.last_status_created_at);
|
||||
});
|
||||
|
||||
const importConversationEntities = (conversations: Conversation[]) => {
|
||||
store.dispatch(
|
||||
importEntities({
|
||||
accounts: conversations.flatMap((conversation) => conversation.accounts),
|
||||
statuses: conversations.map((conversation) => conversation.last_status),
|
||||
}) as any,
|
||||
);
|
||||
};
|
||||
|
||||
const minifyConversationPage = (
|
||||
response: PaginatedResponse<Conversation>,
|
||||
): MinifiedConversationPage => {
|
||||
importConversationEntities(response.items);
|
||||
|
||||
return {
|
||||
...response,
|
||||
previous: response.previous
|
||||
? () => response.previous!().then((page) => minifyConversationPage(page))
|
||||
: null,
|
||||
next: response.next
|
||||
? () => response.next!().then((page) => minifyConversationPage(page))
|
||||
: null,
|
||||
items: response.items.map(minifyConversation),
|
||||
};
|
||||
};
|
||||
|
||||
const updateConversations = (conversation: Conversation) => {
|
||||
importConversationEntities([conversation]);
|
||||
|
||||
queryClient.setQueryData<InfiniteData<MinifiedConversationPage>>(['conversations'], (data) => {
|
||||
if (!data || !data.pages.length) return data;
|
||||
|
||||
return create(data, (draft) => {
|
||||
const updatedConversation = minifyConversation(conversation);
|
||||
|
||||
let found = false;
|
||||
|
||||
for (const page of draft.pages) {
|
||||
const index = page.items.findIndex((item) => item.id === updatedConversation.id);
|
||||
if (index !== -1) {
|
||||
page.items[index] = updatedConversation;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
draft.pages[0].items.unshift(updatedConversation);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const useConversations = () => {
|
||||
const client = useClient();
|
||||
const { isLoggedIn } = useLoggedIn();
|
||||
|
||||
const query = useInfiniteQuery({
|
||||
queryKey: ['conversations'],
|
||||
queryFn: async ({ pageParam }) => {
|
||||
if (pageParam.next) {
|
||||
return pageParam.next();
|
||||
}
|
||||
|
||||
const response = await client.timelines.getConversations();
|
||||
return minifyConversationPage(response);
|
||||
},
|
||||
initialPageParam: {
|
||||
previous: null,
|
||||
next: null,
|
||||
items: [],
|
||||
partial: false,
|
||||
} as MinifiedConversationPage,
|
||||
getNextPageParam: (page) => (page.next ? page : undefined),
|
||||
enabled: isLoggedIn,
|
||||
});
|
||||
|
||||
const conversations = useMemo(
|
||||
() => sortConversations(query.data?.pages.flatMap((page) => page.items) ?? []),
|
||||
[query.data],
|
||||
);
|
||||
|
||||
return { ...query, conversations };
|
||||
};
|
||||
|
||||
const useMarkConversationRead = (conversationId: string) => {
|
||||
const client = useClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['conversations', conversationId, 'read'],
|
||||
mutationFn: () => client.timelines.markConversationRead(conversationId),
|
||||
onMutate: async () => {
|
||||
await queryClient.cancelQueries({ queryKey: ['conversations'] });
|
||||
|
||||
const previous = queryClient.getQueryData<InfiniteData<MinifiedConversationPage>>([
|
||||
'conversations',
|
||||
]);
|
||||
|
||||
updatePaginatedResponse<MinifiedConversation>(['conversations'], (items) =>
|
||||
items.map((item) => (item.id === conversationId ? { ...item, unread: false } : item)),
|
||||
);
|
||||
|
||||
return { previous };
|
||||
},
|
||||
onError: (_, __, context) => {
|
||||
if (context?.previous) {
|
||||
queryClient.setQueryData(['conversations'], context.previous);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
useConversations,
|
||||
useMarkConversationRead,
|
||||
updateConversations,
|
||||
type MinifiedConversation,
|
||||
};
|
||||
@ -1,10 +1,10 @@
|
||||
import { type InfiniteData, useMutation } from '@tanstack/react-query';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import { importEntities } from '@/actions/importer';
|
||||
import { useClient } from '@/hooks/use-client';
|
||||
import { queryClient } from '@/queries/client';
|
||||
import { makePaginatedResponseQuery } from '@/queries/utils/make-paginated-response-query';
|
||||
import { minifyList } from '@/queries/utils/minify-list';
|
||||
import { updatePaginatedResponse } from '@/queries/utils/update-paginated-response';
|
||||
import { store } from '@/store';
|
||||
|
||||
import type { PlApiClient } from 'pl-api';
|
||||
@ -24,20 +24,12 @@ const minifyRequestList = (
|
||||
);
|
||||
|
||||
type MinifiedRequestList = ReturnType<typeof minifyRequestList>;
|
||||
type MinifiedRequest = MinifiedRequestList['items'][0];
|
||||
|
||||
const removeRequest = (statusId: string, accountId: string) =>
|
||||
queryClient.setQueryData<InfiniteData<MinifiedRequestList>>(
|
||||
updatePaginatedResponse<MinifiedRequest>(
|
||||
['accountsLists', 'eventParticipationRequests', statusId],
|
||||
(data) =>
|
||||
data
|
||||
? {
|
||||
...data,
|
||||
pages: data.pages.map(({ items, ...page }) => ({
|
||||
...page,
|
||||
items: items.filter(({ account_id }) => account_id !== accountId),
|
||||
})),
|
||||
}
|
||||
: undefined,
|
||||
(items) => items.filter(({ account_id }) => account_id !== accountId),
|
||||
);
|
||||
|
||||
const useEventParticipationRequests = makePaginatedResponseQuery(
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
import { type InfiniteData, type QueryKey } from '@tanstack/react-query';
|
||||
import { PaginatedResponse } from 'pl-api';
|
||||
|
||||
import { queryClient } from '@/queries/client';
|
||||
|
||||
const updatePaginatedResponse = <T>(
|
||||
queryKey: QueryKey,
|
||||
updater: (items: PaginatedResponse<T>['items']) => PaginatedResponse<T>['items'],
|
||||
) =>
|
||||
queryClient.setQueryData<InfiniteData<PaginatedResponse<T>>>(queryKey, (data) => {
|
||||
if (!data) return undefined;
|
||||
return {
|
||||
...data,
|
||||
pages: data.pages.map((page) => ({
|
||||
...page,
|
||||
items: updater(page.items),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
export { updatePaginatedResponse };
|
||||
@ -1,138 +0,0 @@
|
||||
import pick from 'lodash/pick';
|
||||
import { create } from 'mutative';
|
||||
|
||||
import {
|
||||
CONVERSATIONS_MOUNT,
|
||||
CONVERSATIONS_UNMOUNT,
|
||||
CONVERSATIONS_FETCH_REQUEST,
|
||||
CONVERSATIONS_FETCH_SUCCESS,
|
||||
CONVERSATIONS_FETCH_FAIL,
|
||||
CONVERSATIONS_UPDATE,
|
||||
CONVERSATIONS_READ,
|
||||
type ConversationsAction,
|
||||
} from '../actions/conversations';
|
||||
import { compareDate } from '../utils/comparators';
|
||||
|
||||
import type { Conversation, PaginatedResponse } from 'pl-api';
|
||||
|
||||
interface State {
|
||||
items: Array<MinifiedConversation>;
|
||||
isLoading: boolean;
|
||||
hasMore: boolean;
|
||||
next: (() => Promise<PaginatedResponse<Conversation>>) | null;
|
||||
mounted: number;
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
items: [],
|
||||
isLoading: false,
|
||||
hasMore: true,
|
||||
next: null,
|
||||
mounted: 0,
|
||||
};
|
||||
|
||||
const minifyConversation = (conversation: Conversation) => ({
|
||||
...pick(conversation, ['id', 'unread']),
|
||||
account_ids: conversation.accounts.map((a) => a.id),
|
||||
last_status: conversation.last_status?.id ?? null,
|
||||
last_status_created_at: conversation.last_status?.created_at ?? null,
|
||||
});
|
||||
|
||||
type MinifiedConversation = ReturnType<typeof minifyConversation>;
|
||||
|
||||
const updateConversation = (state: State, item: Conversation) => {
|
||||
const index = state.items.findIndex((x) => x.id === item.id);
|
||||
const newItem = minifyConversation(item);
|
||||
|
||||
if (index === -1) {
|
||||
state.items = [newItem, ...state.items];
|
||||
} else {
|
||||
state.items[index] = newItem;
|
||||
}
|
||||
};
|
||||
|
||||
const expandNormalizedConversations = (
|
||||
state: State,
|
||||
conversations: Conversation[],
|
||||
next: (() => Promise<PaginatedResponse<Conversation>>) | null,
|
||||
isLoadingRecent?: boolean,
|
||||
) => {
|
||||
let items = conversations.map(minifyConversation);
|
||||
|
||||
if (items.length) {
|
||||
let list = state.items.map((oldItem) => {
|
||||
const newItemIndex = items.findIndex((x) => x.id === oldItem.id);
|
||||
|
||||
if (newItemIndex === -1) {
|
||||
return oldItem;
|
||||
}
|
||||
|
||||
const newItem = items[newItemIndex];
|
||||
items = items.filter((_, index) => index !== newItemIndex);
|
||||
|
||||
return newItem;
|
||||
});
|
||||
|
||||
list = list.concat(items);
|
||||
|
||||
state.items = list.toSorted((a, b) => {
|
||||
if (a.last_status_created_at === null || b.last_status_created_at === null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return compareDate(a.last_status_created_at, b.last_status_created_at);
|
||||
});
|
||||
}
|
||||
|
||||
state.hasMore = !next;
|
||||
state.next = next;
|
||||
state.isLoading = false;
|
||||
};
|
||||
|
||||
const conversations = (state = initialState, action: ConversationsAction): State => {
|
||||
switch (action.type) {
|
||||
case CONVERSATIONS_FETCH_REQUEST:
|
||||
return create(state, (draft) => {
|
||||
draft.isLoading = true;
|
||||
});
|
||||
case CONVERSATIONS_FETCH_FAIL:
|
||||
return create(state, (draft) => {
|
||||
draft.isLoading = false;
|
||||
});
|
||||
case CONVERSATIONS_FETCH_SUCCESS:
|
||||
return create(state, (draft) => {
|
||||
expandNormalizedConversations(
|
||||
draft,
|
||||
action.conversations,
|
||||
action.next,
|
||||
action.isLoadingRecent,
|
||||
);
|
||||
});
|
||||
case CONVERSATIONS_UPDATE:
|
||||
return create(state, (draft) => {
|
||||
updateConversation(state, action.conversation);
|
||||
});
|
||||
case CONVERSATIONS_MOUNT:
|
||||
return create(state, (draft) => {
|
||||
draft.mounted += 1;
|
||||
});
|
||||
case CONVERSATIONS_UNMOUNT:
|
||||
return create(state, (draft) => {
|
||||
draft.mounted -= 1;
|
||||
});
|
||||
case CONVERSATIONS_READ:
|
||||
return create(state, (draft) => {
|
||||
state.items = state.items.map((item) => {
|
||||
if (item.id === action.conversationId) {
|
||||
return { ...item, unread: false };
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
});
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export { conversations as default };
|
||||
@ -7,7 +7,6 @@ import entities from '@/entity-store/reducer';
|
||||
import admin from './admin';
|
||||
import auth from './auth';
|
||||
import compose from './compose';
|
||||
import conversations from './conversations';
|
||||
import filters from './filters';
|
||||
import frontendConfig from './frontend-config';
|
||||
import instance from './instance';
|
||||
@ -22,7 +21,6 @@ const reducers = {
|
||||
admin,
|
||||
auth,
|
||||
compose,
|
||||
conversations,
|
||||
entities,
|
||||
filters,
|
||||
frontendConfig,
|
||||
|
||||
Reference in New Issue
Block a user