102 lines
3.5 KiB
TypeScript
102 lines
3.5 KiB
TypeScript
import type { ZodType } from 'zod';
|
|
import {
|
|
ConversationsResponseSchema,
|
|
ConversationDetailResponseSchema,
|
|
ErrorResponseSchema,
|
|
ProcessedPostsResponseSchema,
|
|
SendReplyRequestSchema,
|
|
StartConversationRequestSchema,
|
|
StartConversationResponseSchema,
|
|
SuccessResponseSchema,
|
|
SuggestedReplyResponseSchema,
|
|
type ActiveChat,
|
|
type ConversationDetailResponse,
|
|
type ProcessedPost,
|
|
type SuccessResponse,
|
|
} from '../../../../../packages/api-contracts/src/index.js';
|
|
import { createDashboardRequest } from '../../api/request.js';
|
|
|
|
async function parseResponse<T>(response: Response, schema: ZodType<T>): Promise<T> {
|
|
const payload: unknown = await response.json();
|
|
if (!response.ok) {
|
|
const parsedError = ErrorResponseSchema.safeParse(payload);
|
|
throw new Error(parsedError.success ? parsedError.data.error : `Request failed (${response.status})`);
|
|
}
|
|
return schema.parse(payload);
|
|
}
|
|
|
|
function conversationPath(id: string): string {
|
|
return `/api/conversations/${encodeURIComponent(id)}`;
|
|
}
|
|
|
|
export interface ConversationApi {
|
|
list(): Promise<ActiveChat[]>;
|
|
detail(id: string): Promise<ConversationDetailResponse>;
|
|
suggestReply(id: string): Promise<string>;
|
|
suggestMediaAsk(id: string): Promise<string>;
|
|
processedPosts(username: string): Promise<ProcessedPost[]>;
|
|
sendReply(id: string, message: string): Promise<SuccessResponse>;
|
|
startConversation(username: string, message: string): Promise<ActiveChat>;
|
|
}
|
|
|
|
export function createConversationApi(fetchImplementation?: typeof fetch): ConversationApi {
|
|
const request = createDashboardRequest(fetchImplementation);
|
|
|
|
return {
|
|
async list() {
|
|
return parseResponse(await request('/api/conversations'), ConversationsResponseSchema);
|
|
},
|
|
async detail(id) {
|
|
return parseResponse(await request(conversationPath(id)), ConversationDetailResponseSchema);
|
|
},
|
|
async suggestReply(id) {
|
|
const response = await parseResponse(
|
|
await request(`${conversationPath(id)}/suggest-reply`, { method: 'POST' }),
|
|
SuggestedReplyResponseSchema,
|
|
);
|
|
return response.suggestedReply;
|
|
},
|
|
async suggestMediaAsk(id) {
|
|
const response = await parseResponse(
|
|
await request(`${conversationPath(id)}/suggest-media-ask`, { method: 'POST' }),
|
|
SuggestedReplyResponseSchema,
|
|
);
|
|
return response.suggestedReply;
|
|
},
|
|
async processedPosts(username) {
|
|
return parseResponse(
|
|
await request(`/api/posts/by-author/${encodeURIComponent(username.trim().replace(/^u\//i, ''))}`),
|
|
ProcessedPostsResponseSchema,
|
|
);
|
|
},
|
|
async sendReply(id, message) {
|
|
const body = SendReplyRequestSchema.parse({ message: message.trim() });
|
|
return parseResponse(
|
|
await request(`${conversationPath(id)}/send-reply`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body),
|
|
}),
|
|
SuccessResponseSchema,
|
|
);
|
|
},
|
|
async startConversation(username, message) {
|
|
const body = StartConversationRequestSchema.parse({
|
|
username: username.trim().replace(/^u\//i, ''),
|
|
message: message.trim(),
|
|
});
|
|
const response = await parseResponse(
|
|
await request('/api/conversations/start', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body),
|
|
}),
|
|
StartConversationResponseSchema,
|
|
);
|
|
return response.chat;
|
|
},
|
|
};
|
|
}
|
|
|
|
export const conversationApi = createConversationApi();
|