diff --git a/lib/shared/farcaster/farcaster-api.js b/lib/shared/farcaster/farcaster-api.js new file mode 100644 --- /dev/null +++ b/lib/shared/farcaster/farcaster-api.js @@ -0,0 +1,270 @@ +// @flow + +import * as React from 'react'; +import type { TInterface } from 'tcomb'; +import t from 'tcomb'; + +import type { + FarcasterConversation, + FarcasterInboxConversation, +} from './farcaster-conversation-types.js'; +import { + farcasterConversationValidator, + farcasterInboxConversationValidator, +} from './farcaster-conversation-types.js'; +import type { FarcasterMessage } from './farcaster-messages-types.js'; +import { farcasterMessageValidator } from './farcaster-messages-types.js'; +import { useTunnelbroker } from '../../tunnelbroker/tunnelbroker-context.js'; +import { + tShapeInexact, + assertWithValidator, +} from '../../utils/validation-utils.js'; + +export type SendFarcasterTextMessageInput = + | { + +groupId: string, + +message: string, + } + | { +recipientFid: string, +message: string }; + +type SendFarcasterMessageResultData = { + +messageId: string, + ... +}; +const sendFarcasterMessageResultDataValidator: TInterface = + tShapeInexact({ + messageId: t.String, + }); + +type SendFarcasterMessageResult = { + +result: SendFarcasterMessageResultData, + ... +}; +const sendFarcasterMessageResultValidator: TInterface = + tShapeInexact({ + result: sendFarcasterMessageResultDataValidator, + }); + +function useSendFarcasterTextMessage(): ( + input: SendFarcasterTextMessageInput, +) => Promise { + const { sendFarcasterRequest } = useTunnelbroker(); + return React.useCallback( + async (input: SendFarcasterTextMessageInput) => { + const response = await sendFarcasterRequest({ + apiVersion: 'fc', + endpoint: 'message', + method: { type: 'PUT' }, + payload: JSON.stringify(input), + }); + + const parsedResult = JSON.parse(response); + const result: SendFarcasterMessageResult = assertWithValidator( + parsedResult, + sendFarcasterMessageResultValidator, + ); + return result; + }, + [sendFarcasterRequest], + ); +} + +export type FetchFarcasterMessageInput = { + +conversationId: string, + +cursor?: string, + +limit?: number, + +messageId?: string, +}; + +type FetchFarcasterMessageResultData = { + +messages: $ReadOnlyArray, + ... +}; +const fetchFarcasterMessageResultDataValidator: TInterface = + tShapeInexact({ + messages: t.list(farcasterMessageValidator), + }); + +export type FetchFarcasterMessageResult = { + +result: FetchFarcasterMessageResultData, + ... +}; +const fetchFarcasterMessageResultValidator: TInterface = + tShapeInexact({ + result: fetchFarcasterMessageResultDataValidator, + }); + +function useFetchFarcasterMessage(): ( + input: FetchFarcasterMessageInput, +) => Promise { + const { sendFarcasterRequest } = useTunnelbroker(); + return React.useCallback( + async (input: FetchFarcasterMessageInput) => { + const { conversationId, cursor, limit, messageId } = input; + const params: { [string]: string } = { + conversationId, + }; + if (cursor !== undefined && cursor !== null) { + params.cursor = cursor; + } + if (limit !== undefined && limit !== null) { + params.limit = limit.toString(); + } + if (messageId !== undefined && messageId !== null) { + params.messageId = messageId; + } + + const response = await sendFarcasterRequest({ + apiVersion: 'v2', + endpoint: 'direct-cast-conversation-messages', + method: { type: 'GET' }, + payload: new URLSearchParams(params).toString(), + }); + const parsedResult = JSON.parse(response); + const result: FetchFarcasterMessageResult = assertWithValidator( + parsedResult, + fetchFarcasterMessageResultValidator, + ); + return result; + }, + [sendFarcasterRequest], + ); +} + +export type FetchFarcasterInboxInput = { + +limit?: number, + +category?: 'archived' | 'request', + +cursor?: string, + +filter?: 'unread' | 'group' | '1-1', +}; + +type FetchFarcasterInboxResultData = { + +conversations: $ReadOnlyArray, + +hasArchived: boolean, + +hasUnreadRequests: boolean, + +requestsCount: number, + ... +}; +const fetchFarcasterInboxResultDataValidator: TInterface = + tShapeInexact({ + conversations: t.list(farcasterInboxConversationValidator), + hasArchived: t.Boolean, + hasUnreadRequests: t.Boolean, + requestsCount: t.Number, + }); + +type FetchFarcasterInboxResultNext = { + +cursor?: string, + ... +}; +const fetchFarcasterInboxResultNextValidator: TInterface = + tShapeInexact({ + cursor: t.maybe(t.String), + }); + +export type FetchFarcasterInboxResult = { + +result: FetchFarcasterInboxResultData, + +next?: FetchFarcasterInboxResultNext, + ... +}; +const fetchFarcasterInboxResultValidator: TInterface = + tShapeInexact({ + result: fetchFarcasterInboxResultDataValidator, + next: t.maybe(fetchFarcasterInboxResultNextValidator), + }); + +function useFetchFarcasterInbox(): ( + input: FetchFarcasterInboxInput, +) => Promise { + const { sendFarcasterRequest } = useTunnelbroker(); + return React.useCallback( + async (input: FetchFarcasterInboxInput) => { + const { limit, category, cursor, filter } = input; + + const params: { [string]: string } = {}; + if (cursor !== undefined && cursor !== null) { + params.cursor = cursor; + } + if (limit !== undefined && limit !== null) { + params.limit = limit.toString(); + } + if (category !== undefined && category !== null) { + params.category = category; + } + if (filter !== undefined && filter !== null) { + params.filter = filter; + } + + const response = await sendFarcasterRequest({ + apiVersion: 'v2', + endpoint: 'direct-cast-inbox', + method: { type: 'GET' }, + payload: new URLSearchParams(params).toString(), + }); + const parsedResult = JSON.parse(response); + const result: FetchFarcasterInboxResult = assertWithValidator( + parsedResult, + fetchFarcasterInboxResultValidator, + ); + return result; + }, + [sendFarcasterRequest], + ); +} + +export type FetchFarcasterConversationInput = { + +conversationId: string, +}; + +type FetchFarcasterConversationResultData = { + +conversation: FarcasterConversation, + ... +}; +const fetchFarcasterConversationResultDataValidator: TInterface = + tShapeInexact({ + conversation: farcasterConversationValidator, + }); + +export type FetchFarcasterConversationResult = { + +result: FetchFarcasterConversationResultData, + ... +}; +const fetchFarcasterConversationResultValidator: TInterface = + tShapeInexact({ + result: fetchFarcasterConversationResultDataValidator, + }); + +function useFetchFarcasterConversation(): ( + input: FetchFarcasterConversationInput, +) => Promise { + const { sendFarcasterRequest } = useTunnelbroker(); + return React.useCallback( + async (input: FetchFarcasterConversationInput) => { + const { conversationId } = input; + const params = new URLSearchParams({ + conversationId, + }); + + const response = await sendFarcasterRequest({ + apiVersion: 'v2', + endpoint: 'direct-cast-conversation', + method: { type: 'GET' }, + payload: params.toString(), + }); + const parsedResult = JSON.parse(response); + const result: FetchFarcasterConversationResult = assertWithValidator( + parsedResult, + fetchFarcasterConversationResultValidator, + ); + return result; + }, + [sendFarcasterRequest], + ); +} + +export { + useSendFarcasterTextMessage, + useFetchFarcasterMessage, + useFetchFarcasterConversation, + useFetchFarcasterInbox, +};