Files
ncd-fe/packages/pl-fe/src/stores/shoutbox.ts
nicole mikołajczyk ea86246647 pl-fe: use more specific zustand selectors
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2025-10-22 21:42:11 +02:00

95 lines
3.2 KiB
TypeScript

import { useEffect } from 'react';
import { create } from 'zustand';
import { mutative } from 'zustand-mutative';
import { importEntities } from 'pl-fe/actions/importer';
import { useClient } from 'pl-fe/hooks/use-client';
import { useFeatures } from 'pl-fe/hooks/use-features';
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
import type { PlApiClient, ShoutMessage as BaseShoutMessage } from 'pl-api';
import type { store } from 'pl-fe/store';
let lazyStore: typeof store;
import('pl-fe/store').then(({ store }) => lazyStore = store).catch(() => {});
const minifyMessage = ({ author, ...message }: BaseShoutMessage) => ({
author_id: author.id,
...message,
});
type ShoutMessage = ReturnType<typeof minifyMessage>;
type State = {
socket: ReturnType<(InstanceType<typeof PlApiClient>)['shoutbox']['connect']> | null;
messages: Array<ShoutMessage>;
isLoading: boolean;
actions: {
setMessages: (messages: Array<BaseShoutMessage>) => void;
pushMessage: (message: BaseShoutMessage) => void;
setSocket: (socket: State['socket']) => void;
};
};
const useShoutboxStore = create<State>()(mutative((set) => ({
socket: null,
messages: [],
isLoading: true,
actions: {
setMessages: (messages) => set((state: State) => {
lazyStore?.dispatch(importEntities({ accounts: messages.map((msg) => msg.author) }, { override: false }) as any);
state.messages = messages.map(minifyMessage);
state.isLoading = false;
}),
pushMessage: (message) => set((state: State) => {
lazyStore?.dispatch(importEntities({ accounts: [message.author] }) as any);
state.messages.push(minifyMessage(message));
}),
setSocket: (socket) => set((state: State) => {
state.socket = socket;
}),
},
}), {
enableAutoFreeze: false,
}));
const useShoutboxMessages = () => useShoutboxStore((state) => state.messages);
const useShoutboxIsLoading = () => useShoutboxStore((state) => state.isLoading);
const useShoutboxSocket = () => useShoutboxStore((state) => state.socket);
const useShoutboxActions = () => useShoutboxStore((state) => state.actions);
const useShoutboxSubscription = () => {
const client = useClient();
const { shoutbox: shoutboxAvailable } = useFeatures();
const { isLoggedIn } = useLoggedIn();
const shoutboxStore = useShoutboxActions();
useEffect(() => {
if (!(shoutboxAvailable && isLoggedIn)) return;
let socket: ReturnType<(InstanceType<typeof PlApiClient>)['shoutbox']['connect']>;
client.settings.verifyCredentials().then((account) => {
if (account.__meta.pleroma?.chat_token) {
socket = client.shoutbox.connect(account.__meta.pleroma?.chat_token, {
onMessage: (message) => shoutboxStore.pushMessage(message),
onMessages: (messages) => shoutboxStore.setMessages(messages),
});
shoutboxStore.setSocket(socket);
}
}).catch(() => {});
return () => {
socket?.close();
shoutboxStore.setSocket(null);
};
}, [shoutboxAvailable && isLoggedIn]);
};
const useCreateShoutboxMessage = () => {
const socket = useShoutboxSocket();
return { mutate: socket?.message };
};
export { useShoutboxStore, useShoutboxMessages, useShoutboxIsLoading, useShoutboxSubscription, useCreateShoutboxMessage, type ShoutMessage };