Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32133853
D15526.1765026673.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
D15526.1765026673.diff
View Options
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
@@ -18,6 +18,7 @@
import { messageTypes } from '../types/message-types-enum.js';
import type {
RawMultimediaMessageInfo,
+ ReplyParameters,
SendMessagePayload,
SendMultimediaMessagePayload,
} from '../types/message-types.js';
@@ -31,6 +32,7 @@
parentThreadInfo: ?ThreadInfo,
sidebarCreation: boolean,
threadCreation: boolean,
+ reply?: ?ReplyParameters,
) => Promise<SendMessagePayload> {
const sendKeyserverTextMessage = useSendTextMessage();
const sendComposableDMOperation = useSendComposableDMOperation();
@@ -49,6 +51,7 @@
parentThreadInfo: ?ThreadInfo,
sidebarCreation: boolean,
threadCreation: boolean,
+ reply?: ?ReplyParameters,
) => {
invariant(viewerID, 'Viewer ID should be present');
return threadSpecs[threadInfo.type].protocol().sendTextMessage(
@@ -58,6 +61,7 @@
parentThreadInfo,
sidebarCreation,
threadCreation,
+ reply,
},
{
sendKeyserverTextMessage,
diff --git a/lib/shared/farcaster/farcaster-api.js b/lib/shared/farcaster/farcaster-api.js
--- a/lib/shared/farcaster/farcaster-api.js
+++ b/lib/shared/farcaster/farcaster-api.js
@@ -37,8 +37,9 @@
| {
+groupId: string,
+message: string,
+ +inReplyToMessageId?: string,
}
- | { +recipientFid: number, +message: string };
+ | { +recipientFid: number, +message: string, +inReplyToMessageId?: string };
type SendFarcasterMessageResultData = {
+messageId: string,
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
@@ -23,6 +23,7 @@
import {
defaultNumberPerThread,
messageTruncationStatus,
+ type ReplyParameters,
type SendMessagePayload,
type SendMultimediaMessagePayload,
} from '../../../types/message-types.js';
@@ -126,6 +127,7 @@
rawThreadInfos,
farcasterFetchConversation,
threadCreation,
+ reply,
}: {
threadInfo: ThreadInfo | RawThreadInfo,
viewerID: string,
@@ -137,9 +139,17 @@
conversationID: string,
) => Promise<?FarcasterConversation>,
threadCreation: boolean,
+ reply?: ?ReplyParameters,
}) {
const time = Date.now();
+ let messageText = text;
+ let targetMessageID = null;
+ if (reply && text.startsWith(reply.messagePrefix)) {
+ messageText = text.slice(reply.messagePrefix.length);
+ targetMessageID = reply.targetMessageID;
+ }
+
let request;
if (threadInfo.type === farcasterThreadTypes.FARCASTER_PERSONAL) {
const otherUser = getSingleOtherUser(threadInfo, viewerID);
@@ -158,13 +168,15 @@
const recipientFID = parseInt(targetFID, 10);
request = {
recipientFid: recipientFID,
- message: text,
+ message: messageText,
+ ...(targetMessageID ? { inReplyToMessageId: targetMessageID } : {}),
};
} else {
const conversationID = conversationIDFromFarcasterThreadID(threadInfo.id);
request = {
groupId: conversationID,
- message: text,
+ message: messageText,
+ ...(targetMessageID ? { inReplyToMessageId: targetMessageID } : {}),
};
}
@@ -193,7 +205,7 @@
farcasterFetchConversation,
rawThreadInfos,
} = utils;
- const { messageInfo, threadInfo, threadCreation } = message;
+ const { messageInfo, threadInfo, threadCreation, reply } = message;
const { localID } = messageInfo;
invariant(
localID !== null && localID !== undefined,
@@ -209,6 +221,7 @@
rawThreadInfos,
farcasterFetchConversation,
threadCreation,
+ reply,
});
return {
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
@@ -57,6 +57,7 @@
FetchPinnedMessagesResult,
RawMessageInfo,
MessageInfo,
+ ReplyParameters,
} from '../../types/message-types.js';
import type { RawTextMessageInfo } from '../../types/messages/text.js';
import type {
@@ -132,6 +133,7 @@
+parentThreadInfo: ?ThreadInfo,
+sidebarCreation: boolean,
+threadCreation: boolean,
+ +reply?: ?ReplyParameters,
};
export type SendTextMessageUtils = {
+sendKeyserverTextMessage: SendTextMessageInput => Promise<SendMessageResult>,
diff --git a/lib/types/message-types.js b/lib/types/message-types.js
--- a/lib/types/message-types.js
+++ b/lib/types/message-types.js
@@ -224,6 +224,11 @@
// Raw*MessageInfo = used by server, and contained in client's local store
// *MessageInfo = used by client in UI code
+export type ReplyParameters = {
+ +targetMessageID: string,
+ +messagePrefix: string,
+};
+
export type ValidRawSidebarSourceMessageInfo =
| RawTextMessageInfo
| RawCreateThreadMessageInfo
diff --git a/native/chat/composed-message.react.js b/native/chat/composed-message.react.js
--- a/native/chat/composed-message.react.js
+++ b/native/chat/composed-message.react.js
@@ -13,6 +13,7 @@
import { chatMessageItemHasEngagement } from 'lib/shared/chat-message-item-utils.js';
import { getMessageLabel } from 'lib/shared/edit-messages-utils.js';
+import { messageID } from 'lib/shared/id-utils.js';
import { createMessageReply } from 'lib/shared/markdown.js';
import { assertComposableMessageType } from 'lib/types/message-types.js';
@@ -159,15 +160,15 @@
sendFailed,
]);
- const editInputMessage = inputState?.editInputMessage;
const reply = React.useCallback(() => {
- invariant(editInputMessage, 'editInputMessage should be set in reply');
+ invariant(inputState, 'Input state should be set in reply');
invariant(item.messageInfo.text, 'text should be set in reply');
- editInputMessage({
- message: createMessageReply(item.messageInfo.text),
- mode: 'prepend',
+ const messagePrefix = createMessageReply(item.messageInfo.text);
+ inputState.replyToMessage({
+ targetMessageID: messageID(item.messageInfo),
+ messagePrefix,
});
- }, [editInputMessage, item.messageInfo.text]);
+ }, [inputState, item.messageInfo]);
const triggerReply =
swipeOptions === 'reply' || swipeOptions === 'both' ? reply : undefined;
diff --git a/native/chat/text-message-tooltip-modal.react.js b/native/chat/text-message-tooltip-modal.react.js
--- a/native/chat/text-message-tooltip-modal.react.js
+++ b/native/chat/text-message-tooltip-modal.react.js
@@ -4,6 +4,7 @@
import invariant from 'invariant';
import * as React from 'react';
+import { messageID } from 'lib/shared/id-utils.js';
import { createMessageReply } from 'lib/shared/markdown.js';
import { useDeleteMessage } from 'lib/utils/delete-message-utils.js';
@@ -51,11 +52,17 @@
'inputState should be set in TextMessageTooltipModal.onPressReply',
);
navigateToThread({ threadInfo });
- inputState.editInputMessage({
- message: createMessageReply(text),
- mode: 'prepend',
+ inputState.replyToMessage({
+ targetMessageID: messageID(route.params.item.messageInfo),
+ messagePrefix: createMessageReply(text),
});
- }, [inputState, navigateToThread, threadInfo, text]);
+ }, [
+ inputState,
+ navigateToThread,
+ route.params.item.messageInfo,
+ text,
+ threadInfo,
+ ]);
const renderReplyIcon = React.useCallback(
(style: TextStyle) => <CommIcon name="reply" style={style} size={12} />,
[],
diff --git a/native/input/input-state-container.react.js b/native/input/input-state-container.react.js
--- a/native/input/input-state-container.react.js
+++ b/native/input/input-state-container.react.js
@@ -73,6 +73,7 @@
import {
type RawMessageInfo,
type RawMultimediaMessageInfo,
+ type ReplyParameters,
type SendMessagePayload,
type SendMultimediaMessagePayload,
} from 'lib/types/message-types.js';
@@ -165,6 +166,7 @@
parentThreadInfo: ?ThreadInfo,
sidebarCreation: boolean,
threadCreation: boolean,
+ reply?: ?ReplyParameters,
) => Promise<SendMessagePayload>,
+newThinThread: (
request: ClientNewThinThreadRequest,
@@ -181,11 +183,13 @@
};
type State = {
+pendingUploads: PendingMultimediaUploads,
+ +reply: ?ReplyParameters,
};
class InputStateContainer extends React.PureComponent<Props, State> {
state: State = {
pendingUploads: {},
+ reply: null,
};
sendCallbacks: Array<() => void> = [];
activeURIs: Map<string, ActiveURI> = new Map();
@@ -436,6 +440,7 @@
sendTextMessage: this.sendTextMessage,
sendMultimediaMessage: this.sendMultimediaMessage,
editInputMessage: this.editInputMessage,
+ replyToMessage: this.replyToMessage,
addEditInputMessageListener: this.addEditInputMessageListener,
removeEditInputMessageListener: this.removeEditInputMessageListener,
messageHasUploadFailure: this.messageHasUploadFailure,
@@ -622,6 +627,8 @@
const sidebarCreation =
this.pendingSidebarCreationMessageLocalIDs.has(localID);
+ const reply = this.state.reply;
+
try {
const result = await this.props.sendTextMessage(
messageInfo,
@@ -629,8 +636,10 @@
parentThreadInfo,
sidebarCreation,
threadCreation,
+ reply,
);
this.pendingSidebarCreationMessageLocalIDs.delete(localID);
+ this.setState({ reply: null });
return result;
} catch (e) {
if (e instanceof SendMessageError) {
@@ -1371,6 +1380,16 @@
);
};
+ replyToMessage = (params: ReplyParameters) => {
+ this.editInputMessage({
+ message: params.messagePrefix,
+ mode: 'prepend',
+ });
+ this.setState({
+ reply: params,
+ });
+ };
+
addEditInputMessageListener = (
callbackEditInputBar: (params: EditInputBarMessageParameters) => void,
) => {
diff --git a/native/input/input-state.js b/native/input/input-state.js
--- a/native/input/input-state.js
+++ b/native/input/input-state.js
@@ -3,6 +3,7 @@
import * as React from 'react';
import type { NativeMediaSelection } from 'lib/types/media-types.js';
+import type { ReplyParameters } from 'lib/types/message-types.js';
import type { RawTextMessageInfo } from 'lib/types/messages/text.js';
import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
@@ -39,6 +40,7 @@
threadInfo: ThreadInfo,
) => Promise<void>,
+editInputMessage: (params: EditInputBarMessageParameters) => void,
+ +replyToMessage: (params: ReplyParameters) => void,
+addEditInputMessageListener: (
(params: EditInputBarMessageParameters) => void,
) => void,
diff --git a/web/input/input-state-container.react.js b/web/input/input-state-container.react.js
--- a/web/input/input-state-container.react.js
+++ b/web/input/input-state-container.react.js
@@ -74,6 +74,7 @@
type RawMultimediaMessageInfo,
type SendMessagePayload,
type SendMultimediaMessagePayload,
+ type ReplyParameters,
} from 'lib/types/message-types.js';
import type { RawImagesMessageInfo } from 'lib/types/messages/images.js';
import type { RawMediaMessageInfo } from 'lib/types/messages/media.js';
@@ -155,6 +156,7 @@
parentThreadInfo: ?ThreadInfo,
sidebarCreation: boolean,
threadCreation: boolean,
+ reply?: ?ReplyParameters,
) => Promise<SendMessagePayload>,
+newThinThread: (
request: ClientNewThinThreadRequest,
@@ -180,6 +182,7 @@
},
textCursorPositions: { [threadID: string]: number },
typeaheadState: TypeaheadState,
+ reply: ?ReplyParameters,
};
type State = $ReadOnly<WritableState>;
@@ -202,6 +205,7 @@
close: null,
accept: null,
},
+ reply: null,
};
replyCallbacks: Array<(message: string) => void> = [];
pendingThreadCreations: Map<
@@ -690,7 +694,7 @@
threadInfo,
assignedUploads[localMessageID],
),
- addReply: (message: string) => this.addReply(message),
+ addReply: (params: ReplyParameters) => this.addReply(params),
addReplyListener: this.addReplyListener,
removeReplyListener: this.removeReplyListener,
registerSendCallback: this.props.registerSendCallback,
@@ -1420,6 +1424,8 @@
const sidebarCreation =
this.pendingSidebarCreationMessageLocalIDs.has(localID);
+ const reply = this.state.reply;
+
try {
const result = await this.props.sendTextMessage(
messageInfo,
@@ -1427,8 +1433,10 @@
parentThreadInfo,
sidebarCreation,
threadCreation,
+ reply,
);
this.pendingSidebarCreationMessageLocalIDs.delete(localID);
+ this.setState({ reply: null });
return result;
} catch (e) {
if (e instanceof SendMessageError) {
@@ -1659,8 +1667,11 @@
void this.uploadFiles(threadInfo, uploadsToRetry);
}
- addReply = (message: string) => {
- this.replyCallbacks.forEach(addReplyCallback => addReplyCallback(message));
+ addReply = (params: ReplyParameters) => {
+ this.replyCallbacks.forEach(addReplyCallback =>
+ addReplyCallback(params.messagePrefix),
+ );
+ this.setState({ reply: params });
};
addReplyListener = (callbackReply: (message: string) => void) => {
diff --git a/web/input/input-state.js b/web/input/input-state.js
--- a/web/input/input-state.js
+++ b/web/input/input-state.js
@@ -8,6 +8,7 @@
type MediaMissionStep,
type MediaType,
} from 'lib/types/media-types.js';
+import type { ReplyParameters } from 'lib/types/message-types.js';
import type { RawTextMessageInfo } from 'lib/types/messages/text.js';
import type {
RelativeMemberInfo,
@@ -80,7 +81,7 @@
localMessageID: string,
threadInfo: ThreadInfo,
) => void,
- +addReply: (text: string) => void,
+ +addReply: (params: ReplyParameters) => void,
+addReplyListener: ((message: string) => void) => void,
+removeReplyListener: ((message: string) => void) => void,
+registerSendCallback: (() => mixed) => void,
diff --git a/web/tooltips/tooltip-action-utils.js b/web/tooltips/tooltip-action-utils.js
--- a/web/tooltips/tooltip-action-utils.js
+++ b/web/tooltips/tooltip-action-utils.js
@@ -17,6 +17,7 @@
chatMessageItemEngagementTargetMessageInfo,
} from 'lib/shared/chat-message-item-utils.js';
import { useCanEditMessage } from 'lib/shared/edit-messages-utils.js';
+import { messageID } from 'lib/shared/id-utils.js';
import { createMessageReply } from 'lib/shared/markdown.js';
import { useCanCreateReactionFromMessage } from 'lib/shared/reaction-utils.js';
import { useSidebarExistsOrCanBeCreated } from 'lib/shared/sidebar-utils.js';
@@ -209,7 +210,10 @@
if (!messageInfo.text) {
return;
}
- addReply(createMessageReply(messageInfo.text));
+ addReply({
+ targetMessageID: messageID(messageInfo),
+ messagePrefix: createMessageReply(messageInfo.text),
+ });
};
return {
actionButtonContent: buttonContent,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Dec 6, 1:11 PM (16 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5837863
Default Alt Text
D15526.1765026673.diff (14 KB)
Attached To
Mode
D15526: [lib] Create proper Farcaster protocol replies
Attached
Detach File
Event Timeline
Log In to Comment