81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import { useCallback, useEffect, useState } from 'react';
|
|
import type { Conversation } from '@altricade/core';
|
|
import { userChannel } from '@altricade/core';
|
|
import { listConversations, createDirect, createGroup } from '@altricade/core/api';
|
|
import { apiConfig } from '../../shared/api';
|
|
import { useRealtime } from '../realtime';
|
|
|
|
export interface UseConversations {
|
|
conversations: Conversation[];
|
|
loading: boolean;
|
|
startDirect: (username: string) => Promise<Conversation>;
|
|
createGroupChat: (title: string, members: string[]) => Promise<Conversation>;
|
|
}
|
|
|
|
const isConversationNew = (
|
|
data: unknown,
|
|
): data is { type: 'conversation.new'; conversation: Conversation } => {
|
|
if (typeof data !== 'object' || data === null) {
|
|
return false;
|
|
}
|
|
return 'type' in data && data.type === 'conversation.new' && 'conversation' in data;
|
|
};
|
|
|
|
const upsert = (list: Conversation[], conversation: Conversation): Conversation[] => {
|
|
const rest = list.filter((item) => item.id !== conversation.id);
|
|
return [conversation, ...rest];
|
|
};
|
|
|
|
export const useConversations = (userId: string): UseConversations => {
|
|
const { subscribe } = useRealtime();
|
|
const [conversations, setConversations] = useState<Conversation[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
const load = async (): Promise<void> => {
|
|
try {
|
|
const list = await listConversations(apiConfig);
|
|
if (!cancelled) {
|
|
setConversations(list);
|
|
}
|
|
} finally {
|
|
if (!cancelled) {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
};
|
|
void load();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, []);
|
|
|
|
// New conversations (a DM someone started with me, or a group I was added to)
|
|
// arrive on my personal channel.
|
|
useEffect(() => {
|
|
return subscribe(userChannel(userId), (event) => {
|
|
if (isConversationNew(event.data)) {
|
|
const { conversation } = event.data;
|
|
setConversations((prev) => upsert(prev, conversation));
|
|
}
|
|
});
|
|
}, [subscribe, userId]);
|
|
|
|
const startDirect = useCallback(async (username: string): Promise<Conversation> => {
|
|
const conversation = await createDirect(apiConfig, { username });
|
|
setConversations((prev) => upsert(prev, conversation));
|
|
return conversation;
|
|
}, []);
|
|
|
|
const createGroupChat = useCallback(
|
|
async (title: string, members: string[]): Promise<Conversation> => {
|
|
const conversation = await createGroup(apiConfig, { title, members });
|
|
setConversations((prev) => upsert(prev, conversation));
|
|
return conversation;
|
|
},
|
|
[],
|
|
);
|
|
|
|
return { conversations, loading, startDirect, createGroupChat };
|
|
};
|