import { createContext, useContext, useEffect, useMemo } from "react" import type { NextPage } from "../hooks/usePagination" import { usePagination } from "../hooks/usePagination" import { useAuthentication } from "../tools/authentication" import type { MessageWithMember } from "../models/Message" import type { GuildsChannelsPath } from "../components/Application" import type { SocketData } from "../tools/handleSocketData" import { handleSocketData } from "../tools/handleSocketData" import type { CacheKey } from "../tools/cache" import { 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 = useMemo(() => { return `${path.channelId}-${MESSAGES_CACHE_KEY}` }, [path.channelId]) 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 }