Skip to content

Commit ec8c22d

Browse files
committed
api: Add getSingleMessage binding for GET messages/{message_id}
We'll use this for zulip#5306; see the plan in discussion: https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/.23M5306.20Follow.20.2Fnear.2F.20links.20through.20topic.20moves.2Frenames/near/1407930 In particular, we want the stream and topic for a stream message that might not be in our data structures. We'll use this endpoint to fetch that information.
1 parent 1fbd12a commit ec8c22d

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

src/api/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import deleteMessage from './messages/deleteMessage';
3030
import deleteTopic from './messages/deleteTopic';
3131
import getRawMessageContent from './messages/getRawMessageContent';
3232
import getMessages from './messages/getMessages';
33+
import getSingleMessage from './messages/getSingleMessage';
3334
import getMessageHistory from './messages/getMessageHistory';
3435
import messagesFlags from './messages/messagesFlags';
3536
import sendMessage from './messages/sendMessage';
@@ -78,6 +79,7 @@ export {
7879
deleteTopic,
7980
getRawMessageContent,
8081
getMessages,
82+
getSingleMessage,
8183
getMessageHistory,
8284
messagesFlags,
8385
sendMessage,

src/api/messages/getSingleMessage.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/* @flow strict-local */
2+
import invariant from 'invariant';
3+
4+
import type { Auth, ApiResponseSuccess } from '../transportTypes';
5+
import type { Identity, UserId } from '../../types';
6+
import type { Message } from '../apiTypes';
7+
import type { PmMessage, StreamMessage, MessageEdit, ReactionType } from '../modelTypes';
8+
import { apiGet } from '../apiFetch';
9+
import { identityOfAuth } from '../../account/accountMisc';
10+
import { AvatarURL } from '../../utils/avatar';
11+
12+
/**
13+
* The variant of `Reaction` found in the actual server response.
14+
*
15+
* Note that reaction events have a *different* variation; see their
16+
* handling in `eventToAction`.
17+
*/
18+
export type ServerReaction = {|
19+
+emoji_name: string,
20+
+emoji_code: string,
21+
+reaction_type: ReactionType,
22+
23+
// Our binding assumes FL 120 (see jsdoc), so we don't have to handle FLs
24+
// <2 that don't provide `user_id`.
25+
+user_id: UserId,
26+
27+
// Deprecated and "to be removed in a future release":
28+
// https://zulip.com/api/get-message
29+
-user: $ReadOnly<{|
30+
id: UserId,
31+
email: string,
32+
full_name: string,
33+
34+
// Not observed, so marked optional (doc says required)
35+
is_mirror_dummy?: boolean,
36+
|}>,
37+
|};
38+
39+
/**
40+
* The elements of Message.edit_history found in the actual server response.
41+
*/
42+
export type ServerMessageEdit = {|
43+
// Our binding assumes FL 120 (see jsdoc), so we don't have to support
44+
// the pre-FL 118 shape. This type is written accordingly.
45+
46+
+prev_content?: string,
47+
+prev_rendered_content?: string,
48+
+prev_rendered_content_version?: number,
49+
+prev_stream?: number,
50+
+prev_topic?: string,
51+
+stream?: number,
52+
+timestamp: number,
53+
+topic?: string,
54+
+user_id: UserId | null,
55+
|};
56+
57+
// How `ServerMessage` relates to `Message`, in a way that applies
58+
// uniformly to `Message`'s subtypes.
59+
type ServerMessageOf<M: Message> = {|
60+
...$Exact<M>,
61+
62+
// Can be null. The server hard-codes client_gravatar:
63+
// https://chat.zulip.org/#narrow/stream/378-api-design/topic/.60client_gravatar.60.20in.20.60messages.2F.7Bmessage_id.7D.60/near/1418337
64+
+avatar_url: string | null,
65+
66+
+reactions: $ReadOnlyArray<ServerReaction>,
67+
68+
// Unlike Message['edit_history'], this can't be `null`.
69+
+edit_history?: $ReadOnlyArray<ServerMessageEdit>,
70+
|};
71+
72+
export type ServerMessage = ServerMessageOf<PmMessage> | ServerMessageOf<StreamMessage>;
73+
74+
// The actual response from the server. We convert the message to a proper
75+
// Message before returning it to application code.
76+
type ServerApiResponseSingleMessage = {|
77+
...$Exact<ApiResponseSuccess>,
78+
-raw_content: string, // deprecated
79+
+message: ServerMessage,
80+
|};
81+
82+
/** Exported for tests only. */
83+
export const migrateMessage = (
84+
message: ServerMessage,
85+
identity: Identity,
86+
allowEditHistory: boolean,
87+
): Message => ({
88+
...message,
89+
avatar_url: AvatarURL.fromUserOrBotData({
90+
rawAvatarUrl: message.avatar_url,
91+
email: message.sender_email,
92+
userId: message.sender_id,
93+
realm: identity.realm,
94+
}),
95+
reactions: message.reactions.map(reaction => {
96+
const { user_id, emoji_name, reaction_type, emoji_code } = reaction;
97+
return { user_id, emoji_name, reaction_type, emoji_code };
98+
}),
99+
100+
// Why condition on allowEditHistory? See MessageBase['edit_history'].
101+
edit_history: allowEditHistory
102+
? (message.edit_history: $ReadOnlyArray<MessageEdit> | void)
103+
: null,
104+
});
105+
106+
/**
107+
* See https://zulip.com/api/get-message
108+
*
109+
* Defined to assume FL 120+. Don't call unless supported.
110+
*/
111+
// TODO(server-5.0): Simplify away "FL 120+" notes (in jsdoc and helpers)
112+
export default async (
113+
auth: Auth,
114+
args: {|
115+
+message_id: number,
116+
+apply_markdown: boolean,
117+
|},
118+
119+
// TODO(#4659): Don't get this from callers.
120+
zulipFeatureLevel: number,
121+
122+
// TODO(#4659): Don't get this from callers?
123+
allowEditHistory: boolean,
124+
): Promise<Message> => {
125+
invariant(zulipFeatureLevel >= 120, 'api.getSingleMessage assumes FL 120+');
126+
127+
const { message_id } = args;
128+
const response: ServerApiResponseSingleMessage = await apiGet(auth, `messages/${message_id}`, {
129+
apply_markdown: true,
130+
client_gravatar: true,
131+
});
132+
return migrateMessage(response.message, identityOfAuth(auth), allowEditHistory);
133+
};

0 commit comments

Comments
 (0)