2022-09-16 05:57:09 -07:00
|
|
|
import { useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query';
|
2022-09-27 13:05:19 -07:00
|
|
|
import sumBy from 'lodash/sumBy';
|
2022-09-23 06:48:52 -07:00
|
|
|
import { useState } from 'react';
|
2022-08-10 05:38:49 -07:00
|
|
|
|
2022-09-08 09:47:19 -07:00
|
|
|
import { fetchRelationships } from 'soapbox/actions/accounts';
|
2022-09-29 10:36:35 -07:00
|
|
|
import { importFetchedAccount, importFetchedAccounts } from 'soapbox/actions/importer';
|
2022-08-25 09:32:59 -07:00
|
|
|
import snackbar from 'soapbox/actions/snackbar';
|
2022-09-15 07:49:09 -07:00
|
|
|
import { getNextLink } from 'soapbox/api';
|
2022-08-29 14:21:00 -07:00
|
|
|
import compareId from 'soapbox/compare_id';
|
2022-08-16 10:38:17 -07:00
|
|
|
import { useChatContext } from 'soapbox/contexts/chat-context';
|
2022-09-27 13:05:19 -07:00
|
|
|
import { useStatContext } from 'soapbox/contexts/stat-context';
|
2022-09-22 14:04:52 -07:00
|
|
|
import { useApi, useAppDispatch, useFeatures } from 'soapbox/hooks';
|
2022-09-27 12:43:30 -07:00
|
|
|
import { flattenPages, PaginatedResult, updatePageItem } from 'soapbox/utils/queries';
|
2022-08-10 05:38:49 -07:00
|
|
|
|
2022-08-16 10:38:17 -07:00
|
|
|
import { queryClient } from './client';
|
|
|
|
|
2022-09-08 09:47:19 -07:00
|
|
|
import type { IAccount } from './accounts';
|
|
|
|
|
2022-08-10 05:38:49 -07:00
|
|
|
export interface IChat {
|
|
|
|
id: string
|
|
|
|
unread: number
|
2022-08-16 10:38:17 -07:00
|
|
|
created_by_account: string
|
2022-08-26 09:41:25 -07:00
|
|
|
last_message: null | {
|
|
|
|
account_id: string
|
|
|
|
chat_id: string
|
|
|
|
content: string
|
|
|
|
created_at: string
|
|
|
|
discarded_at: string | null
|
|
|
|
id: string
|
|
|
|
unread: boolean
|
|
|
|
}
|
2022-08-10 05:38:49 -07:00
|
|
|
created_at: Date
|
|
|
|
updated_at: Date
|
|
|
|
accepted: boolean
|
|
|
|
discarded_at: null | string
|
2022-09-08 09:47:19 -07:00
|
|
|
account: IAccount
|
2022-08-10 05:38:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface IChatMessage {
|
|
|
|
account_id: string
|
|
|
|
chat_id: string
|
|
|
|
content: string
|
|
|
|
created_at: Date
|
|
|
|
id: string
|
|
|
|
unread: boolean
|
2022-08-31 10:20:37 -07:00
|
|
|
pending?: boolean
|
2022-08-10 05:38:49 -07:00
|
|
|
}
|
|
|
|
|
2022-09-16 05:57:09 -07:00
|
|
|
export interface IChatSilence {
|
|
|
|
id: number
|
|
|
|
account_id: number
|
|
|
|
target_account_id: number
|
|
|
|
}
|
|
|
|
|
2022-09-27 12:42:24 -07:00
|
|
|
const chatKeys = {
|
2022-09-28 13:38:05 -07:00
|
|
|
chat: (chatId?: string) => ['chats', 'chat', chatId] as const,
|
2022-09-27 12:42:24 -07:00
|
|
|
chatMessages: (chatId: string) => ['chats', 'messages', chatId] as const,
|
2022-09-30 07:28:19 -07:00
|
|
|
chatSearch: (searchQuery?: string) => searchQuery ? ['chats', 'search', searchQuery] : ['chats', 'search'] as const,
|
2022-09-27 12:42:24 -07:00
|
|
|
chatSilences: ['chatSilences'] as const,
|
|
|
|
};
|
|
|
|
|
2022-08-29 14:21:00 -07:00
|
|
|
const reverseOrder = (a: IChat, b: IChat): number => compareId(a.id, b.id);
|
2022-08-10 05:38:49 -07:00
|
|
|
|
|
|
|
const useChatMessages = (chatId: string) => {
|
|
|
|
const api = useApi();
|
|
|
|
|
2022-09-27 12:43:30 -07:00
|
|
|
const getChatMessages = async (chatId: string, pageParam?: any): Promise<PaginatedResult<IChatMessage>> => {
|
2022-09-15 07:49:09 -07:00
|
|
|
const nextPageLink = pageParam?.link;
|
|
|
|
const uri = nextPageLink || `/api/v1/pleroma/chats/${chatId}/messages`;
|
|
|
|
const response = await api.get(uri);
|
|
|
|
const { data } = response;
|
2022-08-10 05:38:49 -07:00
|
|
|
|
2022-09-15 07:49:09 -07:00
|
|
|
const link = getNextLink(response);
|
|
|
|
const hasMore = !!link;
|
2022-10-04 07:48:37 -07:00
|
|
|
const result = data.sort(reverseOrder);
|
2022-08-10 05:38:49 -07:00
|
|
|
|
|
|
|
return {
|
|
|
|
result,
|
2022-09-15 07:49:09 -07:00
|
|
|
link,
|
2022-08-10 05:38:49 -07:00
|
|
|
hasMore,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2022-09-27 12:42:24 -07:00
|
|
|
const queryInfo = useInfiniteQuery(chatKeys.chatMessages(chatId), ({ pageParam }) => getChatMessages(chatId, pageParam), {
|
2022-08-10 05:38:49 -07:00
|
|
|
getNextPageParam: (config) => {
|
|
|
|
if (config.hasMore) {
|
2022-09-15 07:49:09 -07:00
|
|
|
return { link: config.link };
|
2022-08-10 05:38:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-09-22 14:28:05 -07:00
|
|
|
const data = flattenPages(queryInfo);
|
2022-08-10 05:38:49 -07:00
|
|
|
|
|
|
|
return {
|
|
|
|
...queryInfo,
|
|
|
|
data,
|
|
|
|
};
|
|
|
|
};
|
2022-08-26 09:41:25 -07:00
|
|
|
|
2022-09-09 07:24:25 -07:00
|
|
|
const useChats = (search?: string) => {
|
2022-08-10 05:38:49 -07:00
|
|
|
const api = useApi();
|
2022-09-08 09:47:19 -07:00
|
|
|
const dispatch = useAppDispatch();
|
2022-09-22 14:04:52 -07:00
|
|
|
const features = useFeatures();
|
2022-09-27 13:05:19 -07:00
|
|
|
const { setUnreadChatsCount } = useStatContext();
|
2022-08-10 05:38:49 -07:00
|
|
|
|
2022-09-27 12:43:30 -07:00
|
|
|
const getChats = async (pageParam?: any): Promise<PaginatedResult<IChat>> => {
|
2022-09-22 14:04:52 -07:00
|
|
|
const endpoint = features.chatsV2 ? '/api/v2/pleroma/chats' : '/api/v1/pleroma/chats';
|
2022-09-15 07:49:09 -07:00
|
|
|
const nextPageLink = pageParam?.link;
|
2022-09-22 14:04:52 -07:00
|
|
|
const uri = nextPageLink || endpoint;
|
2022-09-15 07:49:09 -07:00
|
|
|
const response = await api.get<IChat[]>(uri, {
|
2022-08-26 09:41:25 -07:00
|
|
|
params: {
|
2022-09-09 07:24:25 -07:00
|
|
|
search,
|
2022-08-26 09:41:25 -07:00
|
|
|
},
|
|
|
|
});
|
2022-09-15 07:49:09 -07:00
|
|
|
const { data } = response;
|
2022-08-26 09:41:25 -07:00
|
|
|
|
2022-09-15 07:49:09 -07:00
|
|
|
const link = getNextLink(response);
|
|
|
|
const hasMore = !!link;
|
2022-08-26 09:41:25 -07:00
|
|
|
|
2022-09-27 13:05:19 -07:00
|
|
|
// TODO: change to response header
|
|
|
|
setUnreadChatsCount(sumBy(data, (chat) => chat.unread));
|
|
|
|
|
2022-09-08 09:47:19 -07:00
|
|
|
// Set the relationships to these users in the redux store.
|
|
|
|
dispatch(fetchRelationships(data.map((item) => item.account.id)));
|
2022-09-29 10:36:35 -07:00
|
|
|
dispatch(importFetchedAccounts(data.map((item) => item.account)));
|
2022-09-08 09:47:19 -07:00
|
|
|
|
2022-08-26 09:41:25 -07:00
|
|
|
return {
|
|
|
|
result: data,
|
|
|
|
hasMore,
|
2022-09-15 07:49:09 -07:00
|
|
|
link,
|
2022-08-26 09:41:25 -07:00
|
|
|
};
|
2022-08-10 05:38:49 -07:00
|
|
|
};
|
|
|
|
|
2022-09-27 12:42:24 -07:00
|
|
|
const queryInfo = useInfiniteQuery(chatKeys.chatSearch(search), ({ pageParam }) => getChats(pageParam), {
|
2022-08-26 09:41:25 -07:00
|
|
|
keepPreviousData: true,
|
|
|
|
getNextPageParam: (config) => {
|
|
|
|
if (config.hasMore) {
|
2022-09-15 07:49:09 -07:00
|
|
|
return { link: config.link };
|
2022-08-26 09:41:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
},
|
2022-08-10 05:38:49 -07:00
|
|
|
});
|
|
|
|
|
2022-09-22 14:28:05 -07:00
|
|
|
const data = flattenPages(queryInfo);
|
2022-08-26 09:41:25 -07:00
|
|
|
|
|
|
|
const chatsQuery = {
|
|
|
|
...queryInfo,
|
|
|
|
data,
|
|
|
|
};
|
|
|
|
|
2022-08-10 05:38:49 -07:00
|
|
|
const getOrCreateChatByAccountId = (accountId: string) => api.post<IChat>(`/api/v1/pleroma/chats/by-account-id/${accountId}`);
|
|
|
|
|
|
|
|
return { chatsQuery, getOrCreateChatByAccountId };
|
|
|
|
};
|
|
|
|
|
2022-09-28 13:38:05 -07:00
|
|
|
const useChat = (chatId?: string) => {
|
|
|
|
const api = useApi();
|
|
|
|
const actions = useChatActions(chatId!);
|
2022-09-29 10:36:35 -07:00
|
|
|
const dispatch = useAppDispatch();
|
2022-09-28 13:38:05 -07:00
|
|
|
|
|
|
|
const getChat = async () => {
|
|
|
|
if (chatId) {
|
|
|
|
const { data } = await api.get<IChat>(`/api/v1/pleroma/chats/${chatId}`);
|
2022-09-29 10:36:35 -07:00
|
|
|
|
|
|
|
dispatch(importFetchedAccount(data.account));
|
|
|
|
|
2022-09-28 13:38:05 -07:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const chat = useQuery<IChat | undefined>(chatKeys.chat(chatId), getChat);
|
|
|
|
|
|
|
|
return { ...actions, chat };
|
|
|
|
};
|
|
|
|
|
2022-09-28 13:20:59 -07:00
|
|
|
const useChatActions = (chatId: string) => {
|
2022-08-10 05:38:49 -07:00
|
|
|
const api = useApi();
|
2022-08-17 12:48:04 -07:00
|
|
|
const { setChat, setEditing } = useChatContext();
|
2022-08-10 05:38:49 -07:00
|
|
|
|
2022-08-30 07:10:31 -07:00
|
|
|
const markChatAsRead = (lastReadId: string) => {
|
|
|
|
api.post<IChat>(`/api/v1/pleroma/chats/${chatId}/read`, { last_read_id: lastReadId })
|
2022-09-22 14:34:20 -07:00
|
|
|
.then(({ data }) => updatePageItem(['chats', 'search'], data, (o, n) => o.id === n.id))
|
2022-08-30 07:10:31 -07:00
|
|
|
.catch(() => null);
|
|
|
|
};
|
2022-08-10 05:38:49 -07:00
|
|
|
|
|
|
|
const createChatMessage = (chatId: string, content: string) => {
|
|
|
|
return api.post<IChat>(`/api/v1/pleroma/chats/${chatId}/messages`, { content });
|
|
|
|
};
|
|
|
|
|
2022-08-16 05:39:58 -07:00
|
|
|
const deleteChatMessage = (chatMessageId: string) => api.delete<IChat>(`/api/v1/pleroma/chats/${chatId}/messages/${chatMessageId}`);
|
|
|
|
|
2022-08-16 10:38:17 -07:00
|
|
|
const acceptChat = useMutation(() => api.post<IChat>(`/api/v1/pleroma/chats/${chatId}/accept`), {
|
|
|
|
onSuccess(response) {
|
|
|
|
setChat(response.data);
|
2022-09-27 12:42:24 -07:00
|
|
|
queryClient.invalidateQueries(chatKeys.chatMessages(chatId));
|
|
|
|
queryClient.invalidateQueries(chatKeys.chatSearch());
|
2022-08-16 10:38:17 -07:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-08-16 10:54:36 -07:00
|
|
|
const deleteChat = useMutation(() => api.delete<IChat>(`/api/v1/pleroma/chats/${chatId}`), {
|
|
|
|
onSuccess(response) {
|
|
|
|
setChat(null);
|
2022-08-17 12:48:04 -07:00
|
|
|
setEditing(false);
|
2022-09-27 12:42:24 -07:00
|
|
|
queryClient.invalidateQueries(chatKeys.chatMessages(chatId));
|
|
|
|
queryClient.invalidateQueries(chatKeys.chatSearch());
|
2022-08-16 10:54:36 -07:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
return { createChatMessage, markChatAsRead, deleteChatMessage, acceptChat, deleteChat };
|
2022-08-10 05:38:49 -07:00
|
|
|
};
|
|
|
|
|
2022-08-29 12:05:16 -07:00
|
|
|
const useChatSilences = () => {
|
2022-08-25 09:32:59 -07:00
|
|
|
const api = useApi();
|
2022-09-16 05:57:09 -07:00
|
|
|
|
2022-09-27 12:43:30 -07:00
|
|
|
const getChatSilences = async () => {
|
2022-09-16 05:57:09 -07:00
|
|
|
const { data } = await api.get<IChatSilence[]>('/api/v1/pleroma/chats/silences');
|
|
|
|
|
|
|
|
return data;
|
|
|
|
};
|
|
|
|
|
2022-09-27 12:42:24 -07:00
|
|
|
return useQuery<IChatSilence[]>(chatKeys.chatSilences, getChatSilences, {
|
2022-09-16 05:57:09 -07:00
|
|
|
placeholderData: [],
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const useChatSilence = (chat: IChat | null) => {
|
|
|
|
const api = useApi();
|
2022-08-25 09:32:59 -07:00
|
|
|
const dispatch = useAppDispatch();
|
|
|
|
|
2022-08-29 12:05:16 -07:00
|
|
|
const [isSilenced, setSilenced] = useState<boolean>(false);
|
2022-08-25 09:32:59 -07:00
|
|
|
|
2022-09-27 12:43:30 -07:00
|
|
|
const getChatSilences = async () => {
|
2022-08-29 12:05:16 -07:00
|
|
|
const { data } = await api.get(`api/v1/pleroma/chats/silence?account_id=${chat?.account.id}`);
|
2022-08-25 09:32:59 -07:00
|
|
|
return data;
|
|
|
|
};
|
|
|
|
|
2022-09-27 12:43:30 -07:00
|
|
|
const fetchChatSilence = async () => {
|
2022-08-29 12:05:16 -07:00
|
|
|
const data = await getChatSilences();
|
2022-08-25 09:32:59 -07:00
|
|
|
if (data) {
|
2022-08-29 12:05:16 -07:00
|
|
|
setSilenced(true);
|
2022-09-13 13:24:19 -07:00
|
|
|
} else {
|
|
|
|
setSilenced(false);
|
2022-08-25 09:32:59 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-08-29 12:05:16 -07:00
|
|
|
const handleSilence = () => {
|
|
|
|
if (isSilenced) {
|
|
|
|
deleteSilence();
|
2022-08-25 09:32:59 -07:00
|
|
|
} else {
|
2022-08-29 12:05:16 -07:00
|
|
|
createSilence();
|
2022-08-25 09:32:59 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-08-29 12:05:16 -07:00
|
|
|
const createSilence = () => {
|
|
|
|
setSilenced(true);
|
2022-08-25 09:32:59 -07:00
|
|
|
|
2022-08-29 12:05:16 -07:00
|
|
|
api.post(`api/v1/pleroma/chats/silence?account_id=${chat?.account.id}`)
|
2022-08-25 09:32:59 -07:00
|
|
|
.then(() => {
|
2022-08-29 12:05:16 -07:00
|
|
|
dispatch(snackbar.success('Successfully silenced this chat.'));
|
2022-09-27 12:42:24 -07:00
|
|
|
queryClient.invalidateQueries(chatKeys.chatSilences);
|
2022-08-25 09:32:59 -07:00
|
|
|
})
|
|
|
|
.catch(() => {
|
2022-08-29 12:05:16 -07:00
|
|
|
dispatch(snackbar.error('Something went wrong trying to silence this chat. Please try again.'));
|
|
|
|
setSilenced(false);
|
2022-08-25 09:32:59 -07:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2022-08-29 12:05:16 -07:00
|
|
|
const deleteSilence = () => {
|
|
|
|
setSilenced(false);
|
2022-08-25 09:32:59 -07:00
|
|
|
|
2022-08-29 12:05:16 -07:00
|
|
|
api.delete(`api/v1/pleroma/chats/silence?account_id=${chat?.account.id}`)
|
2022-08-25 09:32:59 -07:00
|
|
|
.then(() => {
|
2022-08-29 12:05:16 -07:00
|
|
|
dispatch(snackbar.success('Successfully unsilenced this chat.'));
|
2022-09-27 12:42:24 -07:00
|
|
|
queryClient.invalidateQueries(chatKeys.chatSilences);
|
2022-08-25 09:32:59 -07:00
|
|
|
})
|
|
|
|
.catch(() => {
|
2022-08-29 12:05:16 -07:00
|
|
|
dispatch(snackbar.error('Something went wrong trying to unsilence this chat. Please try again.'));
|
|
|
|
setSilenced(true);
|
2022-08-25 09:32:59 -07:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2022-09-23 06:48:52 -07:00
|
|
|
return { isSilenced, handleSilence, fetchChatSilence };
|
2022-08-25 09:32:59 -07:00
|
|
|
};
|
|
|
|
|
2022-09-28 13:38:05 -07:00
|
|
|
export { chatKeys, useChat, useChatActions, useChats, useChatMessages, useChatSilences, useChatSilence };
|