mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-30 19:43:47 +00:00 
			
		
		
		
	reactions: Convert module to typescript.
This commit is contained in:
		| @@ -1,5 +1,6 @@ | |||||||
| import * as blueslip from "./blueslip"; | import * as blueslip from "./blueslip"; | ||||||
| import * as people from "./people"; | import * as people from "./people"; | ||||||
|  | import type {RawReaction} from "./reactions"; | ||||||
| import type {Submessage, TopicLink} from "./types"; | import type {Submessage, TopicLink} from "./types"; | ||||||
|  |  | ||||||
| const stored_messages = new Map(); | const stored_messages = new Map(); | ||||||
| @@ -106,6 +107,8 @@ export type Message = ( | |||||||
|     | Omit<MessageWithBooleans & {type: "private"}, "reactions"> |     | Omit<MessageWithBooleans & {type: "private"}, "reactions"> | ||||||
|     | Omit<MessageWithBooleans & {type: "stream"}, "reactions"> |     | Omit<MessageWithBooleans & {type: "stream"}, "reactions"> | ||||||
| ) & { | ) & { | ||||||
|  |     // Replaced by `clean_reactions` in `reactions.set_clean_reactions`. | ||||||
|  |     reactions?: RawReaction[]; | ||||||
|     // Added in `reactions.set_clean_reactions`. |     // Added in `reactions.set_clean_reactions`. | ||||||
|     clean_reactions: Map<string, MessageCleanReaction>; |     clean_reactions: Map<string, MessageCleanReaction>; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,33 +1,48 @@ | |||||||
| import $ from "jquery"; | import $ from "jquery"; | ||||||
|  | import assert from "minimalistic-assert"; | ||||||
| 
 | 
 | ||||||
| import render_message_reaction from "../templates/message_reaction.hbs"; | import render_message_reaction from "../templates/message_reaction.hbs"; | ||||||
| 
 | 
 | ||||||
| import * as blueslip from "./blueslip"; | import * as blueslip from "./blueslip"; | ||||||
| import * as channel from "./channel"; | import * as channel from "./channel"; | ||||||
| import * as emoji from "./emoji"; | import * as emoji from "./emoji"; | ||||||
|  | import type {EmojiRenderingDetails} from "./emoji"; | ||||||
| import {$t} from "./i18n"; | import {$t} from "./i18n"; | ||||||
| import * as message_lists from "./message_lists"; | import * as message_lists from "./message_lists"; | ||||||
| import * as message_store from "./message_store"; | import * as message_store from "./message_store"; | ||||||
|  | import type {Message, MessageCleanReaction} from "./message_store"; | ||||||
| import {page_params} from "./page_params"; | import {page_params} from "./page_params"; | ||||||
| import * as people from "./people"; | import * as people from "./people"; | ||||||
| import * as spectators from "./spectators"; | import * as spectators from "./spectators"; | ||||||
| import {current_user} from "./state_data"; | import {current_user} from "./state_data"; | ||||||
| import {user_settings} from "./user_settings"; | import {user_settings} from "./user_settings"; | ||||||
| 
 | 
 | ||||||
| const waiting_for_server_request_ids = new Set(); | const waiting_for_server_request_ids = new Set<string>(); | ||||||
| 
 | 
 | ||||||
| export function get_local_reaction_id(rendering_details) { | type ReactionEvent = { | ||||||
|  |     message_id: number; | ||||||
|  |     user_id: number; | ||||||
|  |     local_id: string; | ||||||
|  |     reaction_type: string; | ||||||
|  |     emoji_name: string; | ||||||
|  |     emoji_code: string; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function get_local_reaction_id(rendering_details: EmojiRenderingDetails): string { | ||||||
|     return [rendering_details.reaction_type, rendering_details.emoji_code].join(","); |     return [rendering_details.reaction_type, rendering_details.emoji_code].join(","); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function current_user_has_reacted_to_emoji(message, local_id) { | export function current_user_has_reacted_to_emoji(message: Message, local_id: string): boolean { | ||||||
|     set_clean_reactions(message); |     set_clean_reactions(message); | ||||||
| 
 | 
 | ||||||
|     const clean_reaction_object = message.clean_reactions.get(local_id); |     const clean_reaction_object = message.clean_reactions.get(local_id); | ||||||
|     return clean_reaction_object && clean_reaction_object.user_ids.includes(current_user.user_id); |     return ( | ||||||
|  |         clean_reaction_object !== undefined && | ||||||
|  |         clean_reaction_object.user_ids.includes(current_user.user_id) | ||||||
|  |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function get_message(message_id) { | function get_message(message_id: number): Message | undefined { | ||||||
|     const message = message_store.get(message_id); |     const message = message_store.get(message_id); | ||||||
|     if (!message) { |     if (!message) { | ||||||
|         blueslip.error("reactions: Bad message id", {message_id}); |         blueslip.error("reactions: Bad message id", {message_id}); | ||||||
| @@ -38,7 +53,17 @@ function get_message(message_id) { | |||||||
|     return message; |     return message; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function create_reaction(message_id, rendering_details) { | export type RawReaction = { | ||||||
|  |     emoji_name: string; | ||||||
|  |     reaction_type: string; | ||||||
|  |     emoji_code: string; | ||||||
|  |     user_id: number; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | function create_reaction( | ||||||
|  |     message_id: number, | ||||||
|  |     rendering_details: EmojiRenderingDetails, | ||||||
|  | ): ReactionEvent { | ||||||
|     return { |     return { | ||||||
|         message_id, |         message_id, | ||||||
|         user_id: current_user.user_id, |         user_id: current_user.user_id, | ||||||
| @@ -49,7 +74,10 @@ function create_reaction(message_id, rendering_details) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function update_ui_and_send_reaction_ajax(message_id, rendering_details) { | function update_ui_and_send_reaction_ajax( | ||||||
|  |     message_id: number, | ||||||
|  |     rendering_details: EmojiRenderingDetails, | ||||||
|  | ): void { | ||||||
|     if (page_params.is_spectator) { |     if (page_params.is_spectator) { | ||||||
|         // Spectators can't react, since they don't have accounts.  We
 |         // Spectators can't react, since they don't have accounts.  We
 | ||||||
|         // stop here to avoid a confusing reaction local echo.
 |         // stop here to avoid a confusing reaction local echo.
 | ||||||
| @@ -58,6 +86,9 @@ function update_ui_and_send_reaction_ajax(message_id, rendering_details) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const message = get_message(message_id); |     const message = get_message(message_id); | ||||||
|  |     if (message === undefined) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|     const local_id = get_local_reaction_id(rendering_details); |     const local_id = get_local_reaction_id(rendering_details); | ||||||
|     const has_reacted = current_user_has_reacted_to_emoji(message, local_id); |     const has_reacted = current_user_has_reacted_to_emoji(message, local_id); | ||||||
|     const operation = has_reacted ? "remove" : "add"; |     const operation = has_reacted ? "remove" : "add"; | ||||||
| @@ -83,7 +114,7 @@ function update_ui_and_send_reaction_ajax(message_id, rendering_details) { | |||||||
|         success() { |         success() { | ||||||
|             waiting_for_server_request_ids.delete(reaction_request_id); |             waiting_for_server_request_ids.delete(reaction_request_id); | ||||||
|         }, |         }, | ||||||
|         error(xhr) { |         error(xhr: JQuery.jqXHR) { | ||||||
|             waiting_for_server_request_ids.delete(reaction_request_id); |             waiting_for_server_request_ids.delete(reaction_request_id); | ||||||
|             if (xhr.readyState !== 0) { |             if (xhr.readyState !== 0) { | ||||||
|                 if ( |                 if ( | ||||||
| @@ -101,13 +132,13 @@ function update_ui_and_send_reaction_ajax(message_id, rendering_details) { | |||||||
| 
 | 
 | ||||||
|     waiting_for_server_request_ids.add(reaction_request_id); |     waiting_for_server_request_ids.add(reaction_request_id); | ||||||
|     if (operation === "add") { |     if (operation === "add") { | ||||||
|         channel.post(args); |         void channel.post(args); | ||||||
|     } else if (operation === "remove") { |     } else if (operation === "remove") { | ||||||
|         channel.del(args); |         void channel.del(args); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function toggle_emoji_reaction(message_id, emoji_name) { | export function toggle_emoji_reaction(message_id: number, emoji_name: string): void { | ||||||
|     // This codepath doesn't support toggling a deactivated realm emoji.
 |     // This codepath doesn't support toggling a deactivated realm emoji.
 | ||||||
|     // Since a user can interact with a deactivated realm emoji only by
 |     // Since a user can interact with a deactivated realm emoji only by
 | ||||||
|     // clicking on a reaction and that is handled by `process_reaction_click()`
 |     // clicking on a reaction and that is handled by `process_reaction_click()`
 | ||||||
| @@ -118,7 +149,7 @@ export function toggle_emoji_reaction(message_id, emoji_name) { | |||||||
|     update_ui_and_send_reaction_ajax(message_id, rendering_details); |     update_ui_and_send_reaction_ajax(message_id, rendering_details); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function process_reaction_click(message_id, local_id) { | export function process_reaction_click(message_id: number, local_id: string): void { | ||||||
|     const message = get_message(message_id); |     const message = get_message(message_id); | ||||||
| 
 | 
 | ||||||
|     if (!message) { |     if (!message) { | ||||||
| @@ -142,26 +173,33 @@ export function process_reaction_click(message_id, local_id) { | |||||||
|     update_ui_and_send_reaction_ajax(message_id, rendering_details); |     update_ui_and_send_reaction_ajax(message_id, rendering_details); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function generate_title(emoji_name, user_ids) { | function generate_title(emoji_name: string, user_ids: number[]): string { | ||||||
|     const usernames = people.get_display_full_names( |     const usernames = people.get_display_full_names( | ||||||
|         user_ids.filter((user_id) => user_id !== current_user.user_id), |         user_ids.filter((user_id) => user_id !== current_user.user_id), | ||||||
|     ); |     ); | ||||||
|     const current_user_reacted = user_ids.length !== usernames.length; |     const current_user_reacted = user_ids.length !== usernames.length; | ||||||
| 
 | 
 | ||||||
|     const context = { |     const colon_emoji_name = ":" + emoji_name + ":"; | ||||||
|         emoji_name: ":" + emoji_name + ":", |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     if (user_ids.length === 1) { |     if (user_ids.length === 1) { | ||||||
|         if (current_user_reacted) { |         if (current_user_reacted) { | ||||||
|  |             const context = { | ||||||
|  |                 emoji_name: colon_emoji_name, | ||||||
|  |             }; | ||||||
|             return $t({defaultMessage: "You (click to remove) reacted with {emoji_name}"}, context); |             return $t({defaultMessage: "You (click to remove) reacted with {emoji_name}"}, context); | ||||||
|         } |         } | ||||||
|         context.username = usernames[0]; |         const context = { | ||||||
|  |             emoji_name: colon_emoji_name, | ||||||
|  |             username: usernames[0], | ||||||
|  |         }; | ||||||
|         return $t({defaultMessage: "{username} reacted with {emoji_name}"}, context); |         return $t({defaultMessage: "{username} reacted with {emoji_name}"}, context); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (user_ids.length === 2 && current_user_reacted) { |     if (user_ids.length === 2 && current_user_reacted) { | ||||||
|         context.other_username = usernames[0]; |         const context = { | ||||||
|  |             emoji_name: colon_emoji_name, | ||||||
|  |             other_username: usernames[0], | ||||||
|  |         }; | ||||||
|         return $t( |         return $t( | ||||||
|             { |             { | ||||||
|                 defaultMessage: |                 defaultMessage: | ||||||
| @@ -171,8 +209,11 @@ function generate_title(emoji_name, user_ids) { | |||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     context.comma_separated_usernames = usernames.slice(0, -1).join(", "); |     const context = { | ||||||
|     context.last_username = usernames.at(-1); |         emoji_name: colon_emoji_name, | ||||||
|  |         comma_separated_usernames: usernames.slice(0, -1).join(", "), | ||||||
|  |         last_username: usernames.at(-1), | ||||||
|  |     }; | ||||||
|     if (current_user_reacted) { |     if (current_user_reacted) { | ||||||
|         return $t( |         return $t( | ||||||
|             { |             { | ||||||
| @@ -192,10 +233,13 @@ function generate_title(emoji_name, user_ids) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Add a tooltip showing who reacted to a message.
 | // Add a tooltip showing who reacted to a message.
 | ||||||
| export function get_reaction_title_data(message_id, local_id) { | export function get_reaction_title_data(message_id: number, local_id: string): string { | ||||||
|     const message = get_message(message_id); |     const message = get_message(message_id); | ||||||
|  |     assert(message !== undefined); | ||||||
| 
 | 
 | ||||||
|     const clean_reaction_object = message.clean_reactions.get(local_id); |     const clean_reaction_object = message.clean_reactions.get(local_id); | ||||||
|  |     assert(clean_reaction_object !== undefined); | ||||||
|  | 
 | ||||||
|     const user_list = clean_reaction_object.user_ids; |     const user_list = clean_reaction_object.user_ids; | ||||||
|     const emoji_name = clean_reaction_object.emoji_name; |     const emoji_name = clean_reaction_object.emoji_name; | ||||||
|     const title = generate_title(emoji_name, user_list); |     const title = generate_title(emoji_name, user_list); | ||||||
| @@ -203,29 +247,29 @@ export function get_reaction_title_data(message_id, local_id) { | |||||||
|     return title; |     return title; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function get_reaction_sections(message_id) { | export function get_reaction_sections(message_id: number): JQuery { | ||||||
|     const $rows = message_lists.all_rendered_row_for_message_id(message_id); |     const $rows = message_lists.all_rendered_row_for_message_id(message_id); | ||||||
|     return $rows.find(".message_reactions"); |     return $rows.find(".message_reactions"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function find_reaction(message_id, local_id) { | export function find_reaction(message_id: number, local_id: string): JQuery { | ||||||
|     const $reaction_section = get_reaction_sections(message_id); |     const $reaction_section = get_reaction_sections(message_id); | ||||||
|     const $reaction = $reaction_section.find(`[data-reaction-id='${CSS.escape(local_id)}']`); |     const $reaction = $reaction_section.find(`[data-reaction-id='${CSS.escape(local_id)}']`); | ||||||
|     return $reaction; |     return $reaction; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function get_add_reaction_button(message_id) { | export function get_add_reaction_button(message_id: number): JQuery { | ||||||
|     const $reaction_section = get_reaction_sections(message_id); |     const $reaction_section = get_reaction_sections(message_id); | ||||||
|     const $add_button = $reaction_section.find(".reaction_button"); |     const $add_button = $reaction_section.find(".reaction_button"); | ||||||
|     return $add_button; |     return $add_button; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function set_reaction_vote_text($reaction, vote_text) { | export function set_reaction_vote_text($reaction: JQuery, vote_text: string): void { | ||||||
|     const $count_element = $reaction.find(".message_reaction_count"); |     const $count_element = $reaction.find(".message_reaction_count"); | ||||||
|     $count_element.text(vote_text); |     $count_element.text(vote_text); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function add_reaction(event) { | export function add_reaction(event: ReactionEvent): void { | ||||||
|     const message_id = event.message_id; |     const message_id = event.message_id; | ||||||
|     const message = message_store.get(message_id); |     const message = message_store.get(message_id); | ||||||
| 
 | 
 | ||||||
| @@ -271,7 +315,11 @@ export function add_reaction(event) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function update_existing_reaction(clean_reaction_object, message, acting_user_id) { | export function update_existing_reaction( | ||||||
|  |     clean_reaction_object: MessageCleanReaction, | ||||||
|  |     message: Message, | ||||||
|  |     acting_user_id: number, | ||||||
|  | ): void { | ||||||
|     // Our caller ensures that this message already has a reaction
 |     // Our caller ensures that this message already has a reaction
 | ||||||
|     // for this emoji and sets up our user_list.  This function
 |     // for this emoji and sets up our user_list.  This function
 | ||||||
|     // simply updates the DOM.
 |     // simply updates the DOM.
 | ||||||
| @@ -291,34 +339,38 @@ export function update_existing_reaction(clean_reaction_object, message, acting_ | |||||||
|     update_vote_text_on_message(message); |     update_vote_text_on_message(message); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function insert_new_reaction(clean_reaction_object, message, user_id) { | export function insert_new_reaction( | ||||||
|  |     clean_reaction_object: MessageCleanReaction, | ||||||
|  |     message: Message, | ||||||
|  |     user_id: number, | ||||||
|  | ): void { | ||||||
|     // Our caller ensures we are the first user to react to this
 |     // Our caller ensures we are the first user to react to this
 | ||||||
|     // message with this emoji. We then render the emoji/title/count
 |     // message with this emoji. We then render the emoji/title/count
 | ||||||
|     // and insert it before the add button.
 |     // and insert it before the add button.
 | ||||||
| 
 | 
 | ||||||
|     const context = { |     const emoji_details = emoji.get_emoji_details_for_rendering(clean_reaction_object); | ||||||
|         message_id: message.id, |  | ||||||
|         ...emoji.get_emoji_details_for_rendering(clean_reaction_object), |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     const new_label = generate_title( |     const new_label = generate_title( | ||||||
|         clean_reaction_object.emoji_name, |         clean_reaction_object.emoji_name, | ||||||
|         clean_reaction_object.user_ids, |         clean_reaction_object.user_ids, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     context.count = 1; |     const is_realm_emoji = | ||||||
|     context.label = new_label; |         emoji_details.reaction_type === "realm_emoji" || | ||||||
|     context.local_id = get_local_reaction_id(clean_reaction_object); |         emoji_details.reaction_type === "zulip_extra_emoji"; | ||||||
|     context.emoji_alt_code = user_settings.emojiset === "text"; |     const reaction_class = | ||||||
|     context.is_realm_emoji = |         user_id === current_user.user_id ? "message_reaction reacted" : "message_reaction"; | ||||||
|         context.reaction_type === "realm_emoji" || context.reaction_type === "zulip_extra_emoji"; |  | ||||||
|     context.vote_text = ""; // Updated below
 |  | ||||||
| 
 | 
 | ||||||
|     if (user_id === current_user.user_id) { |     const context = { | ||||||
|         context.class = "message_reaction reacted"; |         message_id: message.id, | ||||||
|     } else { |         ...emoji_details, | ||||||
|         context.class = "message_reaction"; |         count: 1, | ||||||
|     } |         label: new_label, | ||||||
|  |         local_id: get_local_reaction_id(clean_reaction_object), | ||||||
|  |         emoji_alt_code: user_settings.emojiset === "text", | ||||||
|  |         is_realm_emoji, | ||||||
|  |         vote_text: "", // Updated below
 | ||||||
|  |         class: reaction_class, | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     const $new_reaction = $(render_message_reaction(context)); |     const $new_reaction = $(render_message_reaction(context)); | ||||||
| 
 | 
 | ||||||
| @@ -329,7 +381,7 @@ export function insert_new_reaction(clean_reaction_object, message, user_id) { | |||||||
|     update_vote_text_on_message(message); |     update_vote_text_on_message(message); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function remove_reaction(event) { | export function remove_reaction(event: ReactionEvent): void { | ||||||
|     const message_id = event.message_id; |     const message_id = event.message_id; | ||||||
|     const user_id = event.user_id; |     const user_id = event.user_id; | ||||||
|     const message = message_store.get(message_id); |     const message = message_store.get(message_id); | ||||||
| @@ -366,7 +418,11 @@ export function remove_reaction(event) { | |||||||
|     remove_reaction_from_view(clean_reaction_object, message, user_id); |     remove_reaction_from_view(clean_reaction_object, message, user_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function remove_reaction_from_view(clean_reaction_object, message, user_id) { | export function remove_reaction_from_view( | ||||||
|  |     clean_reaction_object: MessageCleanReaction, | ||||||
|  |     message: Message, | ||||||
|  |     user_id: number, | ||||||
|  | ): void { | ||||||
|     const local_id = get_local_reaction_id(clean_reaction_object); |     const local_id = get_local_reaction_id(clean_reaction_object); | ||||||
|     const $reaction = find_reaction(message.id, local_id); |     const $reaction = find_reaction(message.id, local_id); | ||||||
|     const reaction_count = clean_reaction_object.user_ids.length; |     const reaction_count = clean_reaction_object.user_ids.length; | ||||||
| @@ -394,9 +450,11 @@ export function remove_reaction_from_view(clean_reaction_object, message, user_i | |||||||
|     update_vote_text_on_message(message); |     update_vote_text_on_message(message); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function get_emojis_used_by_user_for_message_id(message_id) { | export function get_emojis_used_by_user_for_message_id(message_id: number): string[] { | ||||||
|     const user_id = current_user.user_id; |     const user_id = current_user.user_id; | ||||||
|  |     assert(user_id !== undefined); | ||||||
|     const message = message_store.get(message_id); |     const message = message_store.get(message_id); | ||||||
|  |     assert(message !== undefined); | ||||||
|     set_clean_reactions(message); |     set_clean_reactions(message); | ||||||
| 
 | 
 | ||||||
|     const names = []; |     const names = []; | ||||||
| @@ -409,12 +467,12 @@ export function get_emojis_used_by_user_for_message_id(message_id) { | |||||||
|     return names; |     return names; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function get_message_reactions(message) { | export function get_message_reactions(message: Message): MessageCleanReaction[] { | ||||||
|     set_clean_reactions(message); |     set_clean_reactions(message); | ||||||
|     return [...message.clean_reactions.values()]; |     return [...message.clean_reactions.values()]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function set_clean_reactions(message) { | export function set_clean_reactions(message: Message): void { | ||||||
|     /* |     /* | ||||||
|       set_clean_reactions processes the raw message.reactions object, |       set_clean_reactions processes the raw message.reactions object, | ||||||
|       which will contain one object for each individual reaction, even |       which will contain one object for each individual reaction, even | ||||||
| @@ -441,8 +499,9 @@ export function set_clean_reactions(message) { | |||||||
|     // This first loop creates a temporary distinct_reactions data
 |     // This first loop creates a temporary distinct_reactions data
 | ||||||
|     // structure, which will accumulate the set of users who have
 |     // structure, which will accumulate the set of users who have
 | ||||||
|     // reacted with each distinct reaction.
 |     // reacted with each distinct reaction.
 | ||||||
|     const distinct_reactions = new Map(); |     assert(message.reactions !== undefined); | ||||||
|     const user_map = new Map(); |     const distinct_reactions = new Map<string, RawReaction>(); | ||||||
|  |     const user_map = new Map<string, number[]>(); | ||||||
|     for (const reaction of message.reactions) { |     for (const reaction of message.reactions) { | ||||||
|         const local_id = get_local_reaction_id(reaction); |         const local_id = get_local_reaction_id(reaction); | ||||||
|         const user_id = reaction.user_id; |         const user_id = reaction.user_id; | ||||||
| @@ -452,7 +511,7 @@ export function set_clean_reactions(message) { | |||||||
|             user_map.set(local_id, []); |             user_map.set(local_id, []); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const user_ids = user_map.get(local_id); |         const user_ids = user_map.get(local_id)!; | ||||||
| 
 | 
 | ||||||
|         if (user_ids.includes(user_id)) { |         if (user_ids.includes(user_id)) { | ||||||
|             blueslip.error("server sent duplicate reactions", {user_id, local_id}); |             blueslip.error("server sent duplicate reactions", {user_id, local_id}); | ||||||
| @@ -471,6 +530,7 @@ export function set_clean_reactions(message) { | |||||||
| 
 | 
 | ||||||
|     const reaction_counts_and_user_ids = [...distinct_reactions.keys()].map((local_id) => { |     const reaction_counts_and_user_ids = [...distinct_reactions.keys()].map((local_id) => { | ||||||
|         const user_ids = user_map.get(local_id); |         const user_ids = user_map.get(local_id); | ||||||
|  |         assert(user_ids !== undefined); | ||||||
|         return { |         return { | ||||||
|             count: user_ids.length, |             count: user_ids.length, | ||||||
|             user_ids, |             user_ids, | ||||||
| @@ -480,7 +540,9 @@ export function set_clean_reactions(message) { | |||||||
| 
 | 
 | ||||||
|     for (const local_id of distinct_reactions.keys()) { |     for (const local_id of distinct_reactions.keys()) { | ||||||
|         const reaction = distinct_reactions.get(local_id); |         const reaction = distinct_reactions.get(local_id); | ||||||
|  |         assert(reaction !== undefined); | ||||||
|         const user_ids = user_map.get(local_id); |         const user_ids = user_map.get(local_id); | ||||||
|  |         assert(user_ids !== undefined); | ||||||
| 
 | 
 | ||||||
|         message.clean_reactions.set( |         message.clean_reactions.set( | ||||||
|             local_id, |             local_id, | ||||||
| @@ -502,7 +564,14 @@ function make_clean_reaction({ | |||||||
|     emoji_code, |     emoji_code, | ||||||
|     reaction_type, |     reaction_type, | ||||||
|     should_display_reactors, |     should_display_reactors, | ||||||
| }) { | }: { | ||||||
|  |     local_id: string; | ||||||
|  |     user_ids: number[]; | ||||||
|  |     emoji_name: string; | ||||||
|  |     emoji_code: string; | ||||||
|  |     reaction_type: string; | ||||||
|  |     should_display_reactors: boolean; | ||||||
|  | }): MessageCleanReaction { | ||||||
|     const emoji_details = emoji.get_emoji_details_for_rendering({ |     const emoji_details = emoji.get_emoji_details_for_rendering({ | ||||||
|         emoji_name, |         emoji_name, | ||||||
|         emoji_code, |         emoji_code, | ||||||
| @@ -537,7 +606,10 @@ function make_clean_reaction({ | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function update_user_fields(clean_reaction_object, should_display_reactors) { | export function update_user_fields( | ||||||
|  |     clean_reaction_object: MessageCleanReaction, | ||||||
|  |     should_display_reactors: boolean, | ||||||
|  | ): void { | ||||||
|     // update_user_fields needs to be called whenever the set of users
 |     // update_user_fields needs to be called whenever the set of users
 | ||||||
|     // who reacted on a message might have changed, including due to
 |     // who reacted on a message might have changed, including due to
 | ||||||
|     // upvote/downvotes on ANY reaction in the message, because those
 |     // upvote/downvotes on ANY reaction in the message, because those
 | ||||||
| @@ -547,6 +619,7 @@ export function update_user_fields(clean_reaction_object, should_display_reactor | |||||||
|         clean_reaction_object.emoji_name, |         clean_reaction_object.emoji_name, | ||||||
|         clean_reaction_object.user_ids, |         clean_reaction_object.user_ids, | ||||||
|     ); |     ); | ||||||
|  | 
 | ||||||
|     if (clean_reaction_object.user_ids.includes(current_user.user_id)) { |     if (clean_reaction_object.user_ids.includes(current_user.user_id)) { | ||||||
|         clean_reaction_object.class = "message_reaction reacted"; |         clean_reaction_object.class = "message_reaction reacted"; | ||||||
|     } else { |     } else { | ||||||
| @@ -567,33 +640,40 @@ export function update_user_fields(clean_reaction_object, should_display_reactor | |||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function get_reaction_counts_and_user_ids(message) { | type ReactionUserIdAndCount = { | ||||||
|  |     count?: number; | ||||||
|  |     user_ids: number[]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | function get_reaction_counts_and_user_ids(message: Message): ReactionUserIdAndCount[] { | ||||||
|     return [...message.clean_reactions.values()].map((reaction) => ({ |     return [...message.clean_reactions.values()].map((reaction) => ({ | ||||||
|         count: reaction.count, |         count: reaction.count, | ||||||
|         user_ids: reaction.user_ids, |         user_ids: reaction.user_ids, | ||||||
|     })); |     })); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function get_vote_text(user_ids, should_display_reactors) { | export function get_vote_text(user_ids: number[], should_display_reactors: boolean): string { | ||||||
|     if (should_display_reactors) { |     if (should_display_reactors) { | ||||||
|         return comma_separated_usernames(user_ids); |         return comma_separated_usernames(user_ids); | ||||||
|     } |     } | ||||||
|     return `${user_ids.length}`; |     return `${user_ids.length}`; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function check_should_display_reactors(reaction_counts_and_user_ids) { | function check_should_display_reactors( | ||||||
|  |     reaction_counts_and_user_ids: ReactionUserIdAndCount[], | ||||||
|  | ): boolean { | ||||||
|     if (!user_settings.display_emoji_reaction_users) { |     if (!user_settings.display_emoji_reaction_users) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let total_reactions = 0; |     let total_reactions = 0; | ||||||
|     for (const {count, user_ids} of reaction_counts_and_user_ids) { |     for (const {count, user_ids} of reaction_counts_and_user_ids) { | ||||||
|         total_reactions += count || user_ids.length; |         total_reactions += count ?? user_ids.length; | ||||||
|     } |     } | ||||||
|     return total_reactions <= 3; |     return total_reactions <= 3; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function comma_separated_usernames(user_list) { | function comma_separated_usernames(user_list: number[]): string { | ||||||
|     const usernames = people.get_display_full_names(user_list); |     const usernames = people.get_display_full_names(user_list); | ||||||
|     const current_user_has_reacted = user_list.includes(current_user.user_id); |     const current_user_has_reacted = user_list.includes(current_user.user_id); | ||||||
| 
 | 
 | ||||||
| @@ -607,7 +687,7 @@ function comma_separated_usernames(user_list) { | |||||||
|     return comma_separated_usernames; |     return comma_separated_usernames; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function update_vote_text_on_message(message) { | export function update_vote_text_on_message(message: Message): void { | ||||||
|     // Because whether we display a count or the names of reacting
 |     // Because whether we display a count or the names of reacting
 | ||||||
|     // users depends on total reactions on the message, we need to
 |     // users depends on total reactions on the message, we need to
 | ||||||
|     // recalculate this whenever adjusting reaction rendering on a
 |     // recalculate this whenever adjusting reaction rendering on a
 | ||||||
| @@ -618,7 +698,9 @@ export function update_vote_text_on_message(message) { | |||||||
|     for (const [reaction, clean_reaction] of message.clean_reactions.entries()) { |     for (const [reaction, clean_reaction] of message.clean_reactions.entries()) { | ||||||
|         const reaction_elem = find_reaction(message.id, clean_reaction.local_id); |         const reaction_elem = find_reaction(message.id, clean_reaction.local_id); | ||||||
|         const vote_text = get_vote_text(clean_reaction.user_ids, should_display_reactors); |         const vote_text = get_vote_text(clean_reaction.user_ids, should_display_reactors); | ||||||
|         message.clean_reactions.get(reaction).vote_text = vote_text; |         const message_clean_reaction = message.clean_reactions.get(reaction); | ||||||
|  |         assert(message_clean_reaction !== undefined); | ||||||
|  |         message_clean_reaction.vote_text = vote_text; | ||||||
|         set_reaction_vote_text(reaction_elem, vote_text); |         set_reaction_vote_text(reaction_elem, vote_text); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1181,7 +1181,7 @@ test("remove_reaction_from_view (last person)", () => { | |||||||
|     assert.ok(removed); |     assert.ok(removed); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| test("error_handling", ({override, override_rewire}) => { | test("error_handling", ({override}) => { | ||||||
|     override(message_store, "get", noop); |     override(message_store, "get", noop); | ||||||
|  |  | ||||||
|     blueslip.expect("error", "reactions: Bad message id"); |     blueslip.expect("error", "reactions: Bad message id"); | ||||||
| @@ -1193,7 +1193,6 @@ test("error_handling", ({override, override_rewire}) => { | |||||||
|         emoji_code: "991", |         emoji_code: "991", | ||||||
|         user_id: 99, |         user_id: 99, | ||||||
|     }; |     }; | ||||||
|     override_rewire(reactions, "current_user_has_reacted_to_emoji", () => true); |  | ||||||
|     reactions.toggle_emoji_reaction(55, bogus_event.emoji_name); |     reactions.toggle_emoji_reaction(55, bogus_event.emoji_name); | ||||||
|  |  | ||||||
|     reactions.add_reaction(bogus_event); |     reactions.add_reaction(bogus_event); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user