Page MenuHomePhorge

D15189.1765066972.diff
No OneTemporary

Size
14 KB
Referenced Files
None
Subscribers
None

D15189.1765066972.diff

diff --git a/lib/hooks/input-state-container-hooks.js b/lib/hooks/input-state-container-hooks.js
--- a/lib/hooks/input-state-container-hooks.js
+++ b/lib/hooks/input-state-container-hooks.js
@@ -10,6 +10,7 @@
import { useMediaMetadataReassignment } from './upload-hooks.js';
import { useProcessBlobHolders } from '../actions/holder-actions.js';
import { useSendComposableDMOperation } from '../shared/dm-ops/process-dm-ops.js';
+import { useSendFarcasterTextMessage } from '../shared/farcaster/farcaster-api.js';
import { useMessageCreationSideEffectsFunc } from '../shared/message-utils.js';
import { threadSpecs } from '../shared/threads/thread-specs.js';
import { messageTypes } from '../types/message-types-enum.js';
@@ -32,6 +33,7 @@
const sendComposableDMOperation = useSendComposableDMOperation();
const sideEffectsFunction =
useMessageCreationSideEffectsFunc<RawTextMessageInfo>(messageTypes.TEXT);
+ const sendFarcasterTextMessage = useSendFarcasterTextMessage();
return React.useCallback(
(
@@ -51,9 +53,15 @@
sendKeyserverTextMessage,
sendComposableDMOperation,
sideEffectsFunction,
+ sendFarcasterTextMessage,
},
),
- [sendComposableDMOperation, sendKeyserverTextMessage, sideEffectsFunction],
+ [
+ sendComposableDMOperation,
+ sendFarcasterTextMessage,
+ sendKeyserverTextMessage,
+ sideEffectsFunction,
+ ],
);
}
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,85 @@
+// @flow
+
+import * as React from 'react';
+
+import type { FarcasterMessage } from './farcaster-message-types.js';
+import { useTunnelbroker } from '../../tunnelbroker/tunnelbroker-context.js';
+
+export type SendFarcasterTextMessageInput =
+ | {
+ +groupId: string,
+ +message: string,
+ }
+ | { +recipientFid: string, +message: string };
+export type SendFarcasterMessageResult = {
+ +result: {
+ +messageId: string,
+ },
+};
+function useSendFarcasterTextMessage(): (
+ input: SendFarcasterTextMessageInput,
+) => Promise<SendFarcasterMessageResult> {
+ const { sendFarcasterRequest } = useTunnelbroker();
+ return React.useCallback(
+ async (input: SendFarcasterTextMessageInput) => {
+ const response = await sendFarcasterRequest({
+ apiVersion: 'fc',
+ endpoint: 'message',
+ method: { type: 'PUT' },
+ payload: JSON.stringify(input),
+ });
+
+ //FIXME: add validators to avoid crashing clients
+ const result: SendFarcasterMessageResult = JSON.parse(response);
+ return result;
+ },
+ [sendFarcasterRequest],
+ );
+}
+
+export type FetchFarcasterMessageInput = {
+ +conversationId: string,
+ +cursor?: number,
+ +limit?: number,
+ +messageId?: string,
+};
+export type FetchFarcasterMessageResult = {
+ +result: {
+ +messages: $ReadOnlyArray<FarcasterMessage>,
+ },
+};
+function useFetchFarcasterMessage(): (
+ input: FetchFarcasterMessageInput,
+) => Promise<FetchFarcasterMessageResult> {
+ const { sendFarcasterRequest } = useTunnelbroker();
+ return React.useCallback(
+ async (input: FetchFarcasterMessageInput) => {
+ const { conversationId, cursor, limit, messageId } = input;
+ const params = new URLSearchParams({
+ conversationId: conversationId.toString(),
+ });
+ if (cursor !== undefined && cursor !== null) {
+ params.set('cursor', cursor.toString());
+ }
+ if (limit !== undefined && limit !== null) {
+ params.set('limit', limit.toString());
+ }
+ if (messageId !== undefined && messageId !== null) {
+ params.set('messageId', messageId.toString());
+ }
+
+ const response = await sendFarcasterRequest({
+ apiVersion: 'v2',
+ endpoint: 'direct-cast-conversation-messages',
+ method: { type: 'GET' },
+ payload: params.toString(),
+ });
+ //FIXME: add validators to avoid crashing clients
+ const result: FetchFarcasterMessageResult = JSON.parse(response);
+ return result;
+ },
+ [sendFarcasterRequest],
+ );
+}
+
+export { useSendFarcasterTextMessage, useFetchFarcasterMessage };
diff --git a/lib/shared/farcaster/farcaster-message-types.js b/lib/shared/farcaster/farcaster-message-types.js
new file mode 100644
--- /dev/null
+++ b/lib/shared/farcaster/farcaster-message-types.js
@@ -0,0 +1,70 @@
+// @flow
+
+type FarcasterBaseMessage = {
+ +conversationId: string,
+ +hasMention: boolean,
+ +isDeleted: boolean,
+ +isPinned: boolean,
+ +viewerContext: ViewerContext,
+ +senderContext: UserContext,
+ +reactions: $ReadOnlyArray<ReactionType>,
+ +senderFid: number,
+ +serverTimestamp: number,
+ +message: string,
+ +messageId: string,
+};
+
+type ViewerContext = {
+ +focused: boolean,
+ +isLastReadMessage: boolean,
+ +reactions: $ReadOnlyArray<ReactionType>,
+};
+
+type UserContext = {
+ +displayName: string,
+ +fid: number,
+ +username: number,
+ +pfp: {
+ +url: string,
+ +verified: boolean,
+ },
+};
+
+//TODO: confirm this type
+type ReactionType = string;
+type MentionType = string;
+
+type FarcasterMedia = {
+ +height: number,
+ +width: number,
+ +mimeType: string,
+ +staticRaster: string,
+};
+
+export type FarcasterTextMessage = {
+ ...FarcasterBaseMessage,
+ +type: 'text',
+ +isProgrammatic: boolean,
+ +mentions: $ReadOnlyArray<MentionType>,
+};
+
+export type FarcasterMultimediaMessage = {
+ ...FarcasterBaseMessage,
+ +type: 'text',
+ +isProgrammatic: boolean,
+ +mentions: $ReadOnlyArray<MentionType>,
+ +metadata: {
+ +medias: $ReadOnlyArray<FarcasterMedia>,
+ },
+};
+
+export type FarcasterGroupMembershipAdditionMessage = {
+ ...FarcasterBaseMessage,
+ +type: 'group_membership_addition',
+ +actionTargetUserContext: UserContext,
+};
+
+export type FarcasterMessage =
+ | FarcasterTextMessage
+ | FarcasterMultimediaMessage
+ | FarcasterGroupMembershipAdditionMessage;
diff --git a/lib/shared/message-utils.js b/lib/shared/message-utils.js
--- a/lib/shared/message-utils.js
+++ b/lib/shared/message-utils.js
@@ -4,6 +4,7 @@
import _maxBy from 'lodash/fp/maxBy.js';
import * as React from 'react';
+import { useFetchFarcasterMessage } from './farcaster/farcaster-api.js';
import { codeBlockRegex, type ParserRules } from './markdown.js';
import type { CreationSideEffectsFunc } from './messages/message-spec.js';
import { messageSpecs } from './messages/message-specs.js';
@@ -603,6 +604,7 @@
const callFetchMessagesBeforeCursor = useFetchMessagesBeforeCursor();
const callFetchMostRecentMessages = useFetchMostRecentMessages();
const dispatchActionPromise = useDispatchActionPromise();
+ const fetchFarcasterMessages = useFetchFarcasterMessage();
React.useEffect(() => {
registerFetchKey(fetchMessagesBeforeCursorActionTypes);
@@ -630,6 +632,7 @@
keyserverFetchMessagesBeforeCursor: callFetchMessagesBeforeCursor,
keyserverFetchMostRecentMessages: callFetchMostRecentMessages,
dispatchActionPromise,
+ fetchFarcasterMessages,
},
);
},
@@ -637,6 +640,7 @@
callFetchMessagesBeforeCursor,
callFetchMostRecentMessages,
dispatchActionPromise,
+ fetchFarcasterMessages,
messageIDs?.length,
oldestMessageServerID,
threadID,
diff --git a/lib/shared/threads/protocols/farcaster-thread-protocol.js b/lib/shared/threads/protocols/farcaster-thread-protocol.js
--- a/lib/shared/threads/protocols/farcaster-thread-protocol.js
+++ b/lib/shared/threads/protocols/farcaster-thread-protocol.js
@@ -1,5 +1,9 @@
// @flow
+import invariant from 'invariant';
+import uuid from 'uuid';
+
+import { fetchMessagesBeforeCursorActionTypes } from '../../../actions/message-actions.js';
import type { RolePermissionBlobs } from '../../../permissions/keyserver-permissions.js';
import type { SetThreadUnreadStatusPayload } from '../../../types/activity-types.js';
import type {
@@ -7,9 +11,13 @@
DeleteEntryResult,
SaveEntryResult,
} from '../../../types/entry-types.js';
+import type { Media } from '../../../types/media-types.js';
+import { messageTypes } from '../../../types/message-types-enum.js';
import {
type SendMessagePayload,
type SendMultimediaMessagePayload,
+ messageTruncationStatus,
+ type RawMessageInfo,
} from '../../../types/message-types.js';
import type {
MemberInfoSansPermissions,
@@ -24,11 +32,44 @@
} from '../../../types/thread-types.js';
import { pendingThickSidebarURLPrefix } from '../../../utils/validation-utils.js';
import { messageNotifyTypes } from '../../messages/message-spec.js';
-import type { ThreadProtocol } from '../thread-spec.js';
+import type {
+ ThreadProtocol,
+ ProtocolSendTextMessageInput,
+ SendTextMessageUtils,
+ ProtocolFetchMessageInput,
+ FetchMessageUtils,
+} from '../thread-spec.js';
+
+function getUserID(fid: number): string {
+ //TODO: this should be Comm's userID, blocked on ENG-10946
+ return fid.toString();
+}
const farcasterThreadProtocol: ThreadProtocol<MemberInfoSansPermissions> = {
- sendTextMessage: async (): Promise<SendMessagePayload> => {
- throw new Error('sendTextMessage method is not yet implemented');
+ sendTextMessage: async (
+ message: ProtocolSendTextMessageInput,
+ utils: SendTextMessageUtils,
+ ): Promise<SendMessagePayload> => {
+ const { sendFarcasterTextMessage } = utils;
+ const { messageInfo } = message;
+ const { localID } = messageInfo;
+ invariant(
+ localID !== null && localID !== undefined,
+ 'localID should be set',
+ );
+
+ const time = Date.now();
+ const result = await sendFarcasterTextMessage({
+ groupId: 'efa192faf954b2f8',
+ message: messageInfo.text,
+ });
+
+ return {
+ localID,
+ serverID: result.result.messageId,
+ threadID: messageInfo.threadID,
+ time: time,
+ };
},
sendMultimediaMessage: async (): Promise<SendMultimediaMessagePayload> => {
@@ -85,8 +126,96 @@
throw new Error('convertClientDBThreadInfo method is not yet implemented');
},
- fetchMessages: async (): Promise<void> => {
- throw new Error('fetchMessages method is not yet implemented');
+ fetchMessages: async (
+ input: ProtocolFetchMessageInput,
+ utils: FetchMessageUtils,
+ ): Promise<void> => {
+ console.log('fetchMessages method is not yet implemented');
+ const { fetchFarcasterMessages } = utils;
+ //TODO: currentNumberOfFetchedMessages should be offset,
+ // but this is not supported by Farcaster
+ const { threadID, numMessagesToFetch, currentNumberOfFetchedMessages } =
+ input;
+
+ //TODO: use threadID
+ let payload = {
+ conversationId: 'efa192faf954b2f8',
+ };
+ if (currentNumberOfFetchedMessages) {
+ payload = {
+ ...payload,
+ limit: numMessagesToFetch,
+ };
+ }
+
+ const promise = (async () => {
+ const {
+ result: { messages },
+ } = await fetchFarcasterMessages(payload);
+
+ const rawMessageInfos: $ReadOnlyArray<RawMessageInfo> = messages
+ .map(msg => {
+ if (msg.type === 'group_membership_addition') {
+ return ({
+ id: msg.messageId,
+ type: messageTypes.ADD_MEMBERS,
+ threadID,
+ creatorID: getUserID(msg.senderFid),
+ time: parseInt(msg.serverTimestamp, 10),
+ addedUserIDs: [getUserID(msg.actionTargetUserContext.fid)],
+ }: RawMessageInfo);
+ }
+
+ if (msg.type === 'text' && !!msg?.metadata?.medias) {
+ return ({
+ id: msg.messageId,
+ type: messageTypes.MULTIMEDIA,
+ threadID,
+ creatorID: getUserID(msg.senderFid),
+ time: parseInt(msg.serverTimestamp, 10),
+ media: msg?.metadata?.medias.map(
+ med =>
+ ({
+ id: uuid.v4(),
+ uri: med.staticRaster,
+ type: 'photo',
+ thumbHash: null,
+ dimensions: {
+ height: med.height,
+ width: med.width,
+ },
+ }: Media),
+ ),
+ }: RawMessageInfo);
+ }
+
+ if (msg.type === 'text') {
+ return ({
+ id: msg.messageId,
+ type: messageTypes.TEXT,
+ threadID,
+ creatorID: getUserID(msg.senderFid),
+ time: parseInt(msg.serverTimestamp, 10),
+ text: msg.message,
+ }: RawMessageInfo);
+ }
+
+ return null;
+ })
+ .filter(Boolean);
+
+ return {
+ threadID,
+ //TODO: confirm this
+ truncationStatus: messageTruncationStatus.EXHAUSTIVE,
+ rawMessageInfos,
+ };
+ })();
+ void utils.dispatchActionPromise(
+ fetchMessagesBeforeCursorActionTypes,
+ promise,
+ );
+ await promise;
},
createPendingThread: (): RawThreadInfo => {
diff --git a/lib/shared/threads/thread-spec.js b/lib/shared/threads/thread-spec.js
--- a/lib/shared/threads/thread-spec.js
+++ b/lib/shared/threads/thread-spec.js
@@ -84,6 +84,12 @@
OutboundComposableDMOperationSpecification,
OutboundDMOperationSpecification,
} from '../dm-ops/dm-op-types.js';
+import type {
+ FetchFarcasterMessageInput,
+ FetchFarcasterMessageResult,
+ SendFarcasterMessageResult,
+ SendFarcasterTextMessageInput,
+} from '../farcaster/farcaster-api.js';
import type { FetchThickMessagesType } from '../message-utils.js';
import type {
CreationSideEffectsFunc,
@@ -109,6 +115,9 @@
+sendKeyserverTextMessage: SendTextMessageInput => Promise<SendMessageResult>,
+sendComposableDMOperation: OutboundComposableDMOperationSpecification => Promise<ProcessOutboundP2PMessagesResult>,
+sideEffectsFunction: CreationSideEffectsFunc<RawTextMessageInfo>,
+ +sendFarcasterTextMessage: (
+ input: SendFarcasterTextMessageInput,
+ ) => Promise<SendFarcasterMessageResult>,
};
export type ProtocolSendMultimediaMessageInput = {
@@ -242,6 +251,9 @@
+keyserverFetchMessagesBeforeCursor: FetchMessagesBeforeCursorInput => Promise<FetchMessageInfosPayload>,
+keyserverFetchMostRecentMessages: FetchMostRecentMessagesInput => Promise<FetchMessageInfosPayload>,
+dispatchActionPromise: DispatchActionPromise,
+ +fetchFarcasterMessages: (
+ input: FetchFarcasterMessageInput,
+ ) => Promise<FetchFarcasterMessageResult>,
};
export type ProtocolCreatePendingThreadInput = {

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 7, 12:22 AM (2 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5841255
Default Alt Text
D15189.1765066972.diff (14 KB)

Event Timeline