import { createContext, useContext, useEffect } from 'react' import { NextPage, usePagination } from '../hooks/usePagination' import { useAuthentication } from '../tools/authentication' import { MessageWithMember } from '../models/Message' import { GuildsChannelsPath } from '../components/Application' import { handleSocketData, SocketData } from '../tools/handleSocketData' import { CacheKey, MESSAGES_CACHE_KEY } from '../tools/cache' export interface Messages { messages: MessageWithMember[] hasMore: boolean nextPage: NextPage } const defaultMessagesContext = {} as any const MessagesContext = createContext(defaultMessagesContext) export interface MessagesProviderProps { path: GuildsChannelsPath } export const MessagesProvider: React.FC< React.PropsWithChildren > = (props) => { const { path, children } = props const { authentication, user } = useAuthentication() const cacheKey: CacheKey = `${path.channelId}-${MESSAGES_CACHE_KEY}` const { items: messages, hasMore, nextPage, resetPagination, setItems } = usePagination({ api: authentication.api, url: `/channels/${path.channelId}/messages`, inverse: true, cacheKey }) useEffect(() => { authentication?.socket?.on( 'messages', (data: SocketData) => { if (data.item.channelId === path.channelId) { const messagesDiv = window.document.getElementById( 'messages' ) as HTMLDivElement const isAtBottom = messagesDiv.scrollHeight - messagesDiv.scrollTop <= messagesDiv.clientHeight handleSocketData({ data, setItems, cacheKey }) if ( data.action === 'create' && (isAtBottom || data.item.member.userId === user.id) ) { messagesDiv.scrollTo(0, messagesDiv.scrollHeight) } } } ) return () => { authentication?.socket?.off('messages') } }, [authentication.socket, setItems, path, user.id, cacheKey]) useEffect(() => { resetPagination() nextPage(undefined, () => { const messagesDiv = window.document.getElementById( 'messages' ) as HTMLDivElement messagesDiv.scrollTo(0, messagesDiv.scrollHeight) }) }, [nextPage, resetPagination]) return ( {children} ) } export const useMessages = (): Messages => { const messages = useContext(MessagesContext) if (messages === defaultMessagesContext) { throw new Error('useMessages must be used within a MessagesProvider') } return messages }