mirror of https://github.com/zulip/zulip.git
message_store: Migrate message_store to typescript.
This commit is contained in:
parent
5dc1d36f73
commit
24dc2e783d
|
@ -119,7 +119,12 @@ js_rules = RuleList(
|
||||||
rules=[
|
rules=[
|
||||||
{
|
{
|
||||||
"pattern": "subject|SUBJECT",
|
"pattern": "subject|SUBJECT",
|
||||||
"exclude": {"web/src/types.ts", "web/src/util.ts", "web/tests/"},
|
"exclude": {
|
||||||
|
"web/src/message_store.ts",
|
||||||
|
"web/src/types.ts",
|
||||||
|
"web/src/util.ts",
|
||||||
|
"web/tests/",
|
||||||
|
},
|
||||||
"exclude_pattern": "emails",
|
"exclude_pattern": "emails",
|
||||||
"description": "avoid subject in JS code",
|
"description": "avoid subject in JS code",
|
||||||
"good_lines": ["topic_name"],
|
"good_lines": ["topic_name"],
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
|
import type {Message} from "./message_store";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
import type {Message} from "./types";
|
|
||||||
|
|
||||||
// For simplicity, we use a list for our internal
|
// For simplicity, we use a list for our internal
|
||||||
// data, since that matches what the server sends us.
|
// data, since that matches what the server sends us.
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import assert from "minimalistic-assert";
|
import assert from "minimalistic-assert";
|
||||||
|
|
||||||
|
import type {Message} from "./message_store";
|
||||||
import * as stream_data from "./stream_data";
|
import * as stream_data from "./stream_data";
|
||||||
import * as sub_store from "./sub_store";
|
import * as sub_store from "./sub_store";
|
||||||
import type {Message} from "./types";
|
|
||||||
import type {Recipient} from "./util";
|
import type {Recipient} from "./util";
|
||||||
import * as util from "./util";
|
import * as util from "./util";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as message_feed_loading from "./message_feed_loading";
|
import * as message_feed_loading from "./message_feed_loading";
|
||||||
import type {RawMessage} from "./types";
|
import type {RawMessage} from "./message_store";
|
||||||
|
|
||||||
function max_id_for_messages(messages: RawMessage[]): number {
|
function max_id_for_messages(messages: RawMessage[]): number {
|
||||||
let max_id = 0;
|
let max_id = 0;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import * as internal_url from "../shared/src/internal_url";
|
import * as internal_url from "../shared/src/internal_url";
|
||||||
|
|
||||||
|
import type {Message} from "./message_store";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
import * as stream_data from "./stream_data";
|
import * as stream_data from "./stream_data";
|
||||||
import * as sub_store from "./sub_store";
|
import * as sub_store from "./sub_store";
|
||||||
import type {StreamSubscription} from "./sub_store";
|
import type {StreamSubscription} from "./sub_store";
|
||||||
import type {Message} from "./types";
|
|
||||||
import type {UserGroup} from "./user_groups";
|
import type {UserGroup} from "./user_groups";
|
||||||
|
|
||||||
type Operator = {operator: string; operand: string; negated?: boolean};
|
type Operator = {operator: string; operand: string; negated?: boolean};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
|
import type {Message} from "./message_store";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
import type {Message} from "./types";
|
|
||||||
|
|
||||||
const huddle_timestamps = new Map<string, number>();
|
const huddle_timestamps = new Map<string, number>();
|
||||||
|
|
||||||
|
|
|
@ -66,23 +66,23 @@ export function update_starred_view(message_id, new_value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update_stream_name(stream_id, new_name) {
|
export function update_stream_name(stream_id, new_name) {
|
||||||
message_store.update_property("stream_name", new_name, {stream_id});
|
message_store.update_stream_name(stream_id, new_name);
|
||||||
rerender_messages_view();
|
rerender_messages_view();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update_user_full_name(user_id, full_name) {
|
export function update_user_full_name(user_id, full_name) {
|
||||||
message_store.update_property("sender_full_name", full_name, {user_id});
|
message_store.update_sender_full_name(user_id, full_name);
|
||||||
rerender_messages_view_for_user(user_id);
|
rerender_messages_view_for_user(user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update_avatar(user_id, avatar_url) {
|
export function update_avatar(user_id, avatar_url) {
|
||||||
let url = avatar_url;
|
let url = avatar_url;
|
||||||
url = people.format_small_avatar_url(url);
|
url = people.format_small_avatar_url(url);
|
||||||
message_store.update_property("small_avatar_url", url, {user_id});
|
message_store.update_small_avatar_url(user_id, url);
|
||||||
rerender_messages_view_for_user(user_id);
|
rerender_messages_view_for_user(user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update_user_status_emoji(user_id, status_emoji_info) {
|
export function update_user_status_emoji(user_id, status_emoji_info) {
|
||||||
message_store.update_property("status_emoji_info", status_emoji_info, {user_id});
|
message_store.update_status_emoji_info(user_id, status_emoji_info);
|
||||||
rerender_messages_view_for_user(user_id);
|
rerender_messages_view_for_user(user_id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
// We only use jquery for parsing.
|
// We only use jquery for parsing.
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
|
|
||||||
// TODO: Move this to message_store when it is
|
import type {Message} from "./message_store";
|
||||||
// converted to TypeScript.
|
|
||||||
type Message = {
|
|
||||||
content: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// We need to check if the message content contains the specified HTML
|
// We need to check if the message content contains the specified HTML
|
||||||
// elements. We wrap the message.content in a <div>; this is
|
// elements. We wrap the message.content in a <div>; this is
|
||||||
|
|
|
@ -1,137 +0,0 @@
|
||||||
import * as blueslip from "./blueslip";
|
|
||||||
import * as people from "./people";
|
|
||||||
|
|
||||||
const stored_messages = new Map();
|
|
||||||
|
|
||||||
export function update_message_cache(message) {
|
|
||||||
// You should only call this from message_helper (or in tests).
|
|
||||||
stored_messages.set(message.id, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get_cached_message(message_id) {
|
|
||||||
// You should only call this from message_helper.
|
|
||||||
// Use the get() wrapper below for most other use cases.
|
|
||||||
return stored_messages.get(message_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clear_for_testing() {
|
|
||||||
stored_messages.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get(message_id) {
|
|
||||||
if (message_id === undefined || message_id === null) {
|
|
||||||
blueslip.error("message_store.get got bad value", {message_id});
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof message_id !== "number") {
|
|
||||||
blueslip.error("message_store got non-number", {message_id});
|
|
||||||
|
|
||||||
// Try to soldier on, assuming the caller treats message
|
|
||||||
// ids as strings.
|
|
||||||
message_id = Number.parseFloat(message_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stored_messages.get(message_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get_pm_emails(message) {
|
|
||||||
const user_ids = people.pm_with_user_ids(message);
|
|
||||||
const emails = user_ids
|
|
||||||
.map((user_id) => {
|
|
||||||
const person = people.maybe_get_user_by_id(user_id);
|
|
||||||
if (!person) {
|
|
||||||
blueslip.error("Unknown user id", {user_id});
|
|
||||||
return "?";
|
|
||||||
}
|
|
||||||
return person.email;
|
|
||||||
})
|
|
||||||
.sort();
|
|
||||||
|
|
||||||
return emails.join(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get_pm_full_names(message) {
|
|
||||||
const user_ids = people.pm_with_user_ids(message);
|
|
||||||
const names = people.get_display_full_names(user_ids).sort();
|
|
||||||
|
|
||||||
return names.join(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function set_message_booleans(message) {
|
|
||||||
const flags = message.flags || [];
|
|
||||||
|
|
||||||
function convert_flag(flag_name) {
|
|
||||||
return flags.includes(flag_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
message.unread = !convert_flag("read");
|
|
||||||
message.historical = convert_flag("historical");
|
|
||||||
message.starred = convert_flag("starred");
|
|
||||||
message.mentioned =
|
|
||||||
convert_flag("mentioned") ||
|
|
||||||
convert_flag("stream_wildcard_mentioned") ||
|
|
||||||
convert_flag("topic_wildcard_mentioned");
|
|
||||||
message.mentioned_me_directly = convert_flag("mentioned");
|
|
||||||
message.stream_wildcard_mentioned = convert_flag("stream_wildcard_mentioned");
|
|
||||||
message.topic_wildcard_mentioned = convert_flag("topic_wildcard_mentioned");
|
|
||||||
message.collapsed = convert_flag("collapsed");
|
|
||||||
message.alerted = convert_flag("has_alert_word");
|
|
||||||
|
|
||||||
// Once we have set boolean flags here, the `flags` attribute is
|
|
||||||
// just a distraction, so we delete it. (All the downstream code
|
|
||||||
// uses booleans.)
|
|
||||||
delete message.flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function update_booleans(message, flags) {
|
|
||||||
// When we get server flags for local echo or message edits,
|
|
||||||
// we are vulnerable to race conditions, so only update flags
|
|
||||||
// that are driven by message content.
|
|
||||||
function convert_flag(flag_name) {
|
|
||||||
return flags.includes(flag_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
message.mentioned =
|
|
||||||
convert_flag("mentioned") ||
|
|
||||||
convert_flag("stream_wildcard_mentioned") ||
|
|
||||||
convert_flag("topic_wildcard_mentioned");
|
|
||||||
message.mentioned_me_directly = convert_flag("mentioned");
|
|
||||||
message.stream_wildcard_mentioned = convert_flag("stream_wildcard_mentioned");
|
|
||||||
message.topic_wildcard_mentioned = convert_flag("topic_wildcard_mentioned");
|
|
||||||
message.alerted = convert_flag("has_alert_word");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function update_property(property, value, info) {
|
|
||||||
switch (property) {
|
|
||||||
case "sender_full_name":
|
|
||||||
case "small_avatar_url":
|
|
||||||
for (const msg of stored_messages.values()) {
|
|
||||||
if (msg.sender_id && msg.sender_id === info.user_id) {
|
|
||||||
msg[property] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "stream_name":
|
|
||||||
for (const msg of stored_messages.values()) {
|
|
||||||
if (msg.stream_id && msg.stream_id === info.stream_id) {
|
|
||||||
msg.display_recipient = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "status_emoji_info":
|
|
||||||
for (const msg of stored_messages.values()) {
|
|
||||||
if (msg.sender_id && msg.sender_id === info.user_id) {
|
|
||||||
msg[property] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function reify_message_id({old_id, new_id}) {
|
|
||||||
if (stored_messages.has(old_id)) {
|
|
||||||
stored_messages.set(new_id, stored_messages.get(old_id));
|
|
||||||
stored_messages.delete(old_id);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,277 @@
|
||||||
|
import * as blueslip from "./blueslip";
|
||||||
|
import * as people from "./people";
|
||||||
|
import type {Submessage, TopicLink} from "./types";
|
||||||
|
|
||||||
|
const stored_messages = new Map();
|
||||||
|
|
||||||
|
export type MatchedMessage = {
|
||||||
|
match_content?: string;
|
||||||
|
match_subject?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MessageReactionType = "unicode_emoji" | "realm_emoji" | "zulip_extra_emoji";
|
||||||
|
|
||||||
|
export type DisplayRecipientUser = {
|
||||||
|
email: string;
|
||||||
|
full_name: string;
|
||||||
|
id: number;
|
||||||
|
is_mirror_dummy: boolean;
|
||||||
|
unknown_local_echo_user?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DisplayRecipient = string | DisplayRecipientUser[];
|
||||||
|
|
||||||
|
export type MessageEditHistoryEntry = {
|
||||||
|
user_id: number | null;
|
||||||
|
timestamp: number;
|
||||||
|
prev_content?: string;
|
||||||
|
prev_rendered_content?: string;
|
||||||
|
prev_rendered_content_version?: number;
|
||||||
|
prev_stream?: number;
|
||||||
|
prev_topic?: string;
|
||||||
|
stream?: number;
|
||||||
|
topic?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MessageReaction = {
|
||||||
|
emoji_name: string;
|
||||||
|
emoji_code: string;
|
||||||
|
reaction_type: MessageReactionType;
|
||||||
|
user_id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RawMessage = {
|
||||||
|
avatar_url: string | null;
|
||||||
|
client: string;
|
||||||
|
content: string;
|
||||||
|
content_type: "text/html";
|
||||||
|
display_recipient: DisplayRecipient;
|
||||||
|
edit_history?: MessageEditHistoryEntry[];
|
||||||
|
id: number;
|
||||||
|
is_me_message: boolean;
|
||||||
|
last_edit_timestamp?: number;
|
||||||
|
reactions: MessageReaction[];
|
||||||
|
recipient_id: number;
|
||||||
|
sender_email: string;
|
||||||
|
sender_full_name: string;
|
||||||
|
sender_id: number;
|
||||||
|
sender_realm_str: string;
|
||||||
|
submessages: Submessage[];
|
||||||
|
timestamp: number;
|
||||||
|
flags: string[];
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
type: "private";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "stream";
|
||||||
|
stream_id: number;
|
||||||
|
subject: string;
|
||||||
|
topic_links: TopicLink[];
|
||||||
|
}
|
||||||
|
) &
|
||||||
|
MatchedMessage;
|
||||||
|
|
||||||
|
// We add these boolean properties to Raw message in `message_store.set_message_booleans` method.
|
||||||
|
export type MessageWithBooleans = (
|
||||||
|
| Omit<RawMessage & {type: "private"}, "flags">
|
||||||
|
| Omit<RawMessage & {type: "stream"}, "flags">
|
||||||
|
) & {
|
||||||
|
unread: boolean;
|
||||||
|
historical: boolean;
|
||||||
|
starred: boolean;
|
||||||
|
mentioned: boolean;
|
||||||
|
mentioned_me_directly: boolean;
|
||||||
|
stream_wildcard_mentioned: boolean;
|
||||||
|
topic_wildcard_mentioned: boolean;
|
||||||
|
collapsed: boolean;
|
||||||
|
alerted: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MessageCleanReaction = {
|
||||||
|
class: string;
|
||||||
|
count: number;
|
||||||
|
emoji_alt_code: boolean;
|
||||||
|
emoji_code: string;
|
||||||
|
emoji_name: string;
|
||||||
|
is_realm_emoji: boolean;
|
||||||
|
label: string;
|
||||||
|
local_id: string;
|
||||||
|
reaction_type: string;
|
||||||
|
user_ids: number[];
|
||||||
|
vote_text: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Message = (
|
||||||
|
| Omit<MessageWithBooleans & {type: "private"}, "reactions">
|
||||||
|
| Omit<MessageWithBooleans & {type: "stream"}, "reactions">
|
||||||
|
) & {
|
||||||
|
// Added in `reactions.set_clean_reactions`.
|
||||||
|
clean_reactions: Map<string, MessageCleanReaction>;
|
||||||
|
|
||||||
|
// Added in `message_helper.process_new_message`.
|
||||||
|
sent_by_me: boolean;
|
||||||
|
reply_to: string;
|
||||||
|
display_reply_to?: string;
|
||||||
|
|
||||||
|
// These properties are used in `message_list_view.js`.
|
||||||
|
starred_status: string;
|
||||||
|
message_reactions: MessageCleanReaction[];
|
||||||
|
url: string;
|
||||||
|
|
||||||
|
// Used in `markdown.js`, `server_events.js`, and `set_message_booleans`
|
||||||
|
flags?: string[];
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
type: "private";
|
||||||
|
is_private: true;
|
||||||
|
is_stream: false;
|
||||||
|
pm_with_url: string;
|
||||||
|
to_user_ids: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "stream";
|
||||||
|
is_private: false;
|
||||||
|
is_stream: true;
|
||||||
|
stream: string;
|
||||||
|
topic: string;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export function update_message_cache(message: Message): void {
|
||||||
|
// You should only call this from message_helper (or in tests).
|
||||||
|
stored_messages.set(message.id, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get_cached_message(message_id: number): Message {
|
||||||
|
// You should only call this from message_helper.
|
||||||
|
// Use the get() wrapper below for most other use cases.
|
||||||
|
return stored_messages.get(message_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clear_for_testing(): void {
|
||||||
|
stored_messages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get(message_id: number): Message | undefined {
|
||||||
|
if (message_id === undefined || message_id === null) {
|
||||||
|
blueslip.error("message_store.get got bad value", {message_id});
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof message_id !== "number") {
|
||||||
|
blueslip.error("message_store got non-number", {message_id});
|
||||||
|
|
||||||
|
// Try to soldier on, assuming the caller treats message
|
||||||
|
// ids as strings.
|
||||||
|
message_id = Number.parseFloat(message_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stored_messages.get(message_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get_pm_emails(message: Message): string {
|
||||||
|
const user_ids = people.pm_with_user_ids(message) ?? [];
|
||||||
|
const emails = user_ids
|
||||||
|
.map((user_id) => {
|
||||||
|
const person = people.maybe_get_user_by_id(user_id);
|
||||||
|
if (!person) {
|
||||||
|
blueslip.error("Unknown user id", {user_id});
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
return person.email;
|
||||||
|
})
|
||||||
|
.sort();
|
||||||
|
|
||||||
|
return emails.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get_pm_full_names(message: Message): string {
|
||||||
|
const user_ids = people.pm_with_user_ids(message) ?? [];
|
||||||
|
const names = people.get_display_full_names(user_ids).sort();
|
||||||
|
|
||||||
|
return names.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function set_message_booleans(message: Message): void {
|
||||||
|
const flags = message.flags || [];
|
||||||
|
|
||||||
|
function convert_flag(flag_name: string): boolean {
|
||||||
|
return flags.includes(flag_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
message.unread = !convert_flag("read");
|
||||||
|
message.historical = convert_flag("historical");
|
||||||
|
message.starred = convert_flag("starred");
|
||||||
|
message.mentioned =
|
||||||
|
convert_flag("mentioned") ||
|
||||||
|
convert_flag("stream_wildcard_mentioned") ||
|
||||||
|
convert_flag("topic_wildcard_mentioned");
|
||||||
|
message.mentioned_me_directly = convert_flag("mentioned");
|
||||||
|
message.stream_wildcard_mentioned = convert_flag("stream_wildcard_mentioned");
|
||||||
|
message.topic_wildcard_mentioned = convert_flag("topic_wildcard_mentioned");
|
||||||
|
message.collapsed = convert_flag("collapsed");
|
||||||
|
message.alerted = convert_flag("has_alert_word");
|
||||||
|
|
||||||
|
// Once we have set boolean flags here, the `flags` attribute is
|
||||||
|
// just a distraction, so we delete it. (All the downstream code
|
||||||
|
// uses booleans.)
|
||||||
|
delete message.flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update_booleans(message: Message, flags: string[]): void {
|
||||||
|
// When we get server flags for local echo or message edits,
|
||||||
|
// we are vulnerable to race conditions, so only update flags
|
||||||
|
// that are driven by message content.
|
||||||
|
function convert_flag(flag_name: string): boolean {
|
||||||
|
return flags.includes(flag_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
message.mentioned =
|
||||||
|
convert_flag("mentioned") ||
|
||||||
|
convert_flag("stream_wildcard_mentioned") ||
|
||||||
|
convert_flag("topic_wildcard_mentioned");
|
||||||
|
message.mentioned_me_directly = convert_flag("mentioned");
|
||||||
|
message.stream_wildcard_mentioned = convert_flag("stream_wildcard_mentioned");
|
||||||
|
message.topic_wildcard_mentioned = convert_flag("topic_wildcard_mentioned");
|
||||||
|
message.alerted = convert_flag("has_alert_word");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update_sender_full_name(user_id: number, new_name: string): void {
|
||||||
|
for (const msg of stored_messages.values()) {
|
||||||
|
if (msg.sender_id && msg.sender_id === user_id) {
|
||||||
|
msg.sender_full_name = new_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update_small_avatar_url(user_id: number, new_url: string): void {
|
||||||
|
for (const msg of stored_messages.values()) {
|
||||||
|
if (msg.sender_id && msg.sender_id === user_id) {
|
||||||
|
msg.small_avatar_url = new_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update_stream_name(stream_id: number, new_name: string): void {
|
||||||
|
for (const msg of stored_messages.values()) {
|
||||||
|
if (msg.stream_id && msg.stream_id === stream_id) {
|
||||||
|
msg.display_recipient = new_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update_status_emoji_info(user_id: number, new_info: string): void {
|
||||||
|
for (const msg of stored_messages.values()) {
|
||||||
|
if (msg.sender_id && msg.sender_id === user_id) {
|
||||||
|
msg.status_emoji_info = new_info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reify_message_id({old_id, new_id}: {old_id: number; new_id: number}): void {
|
||||||
|
if (stored_messages.has(old_id)) {
|
||||||
|
stored_messages.set(new_id, stored_messages.get(old_id));
|
||||||
|
stored_messages.delete(old_id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import * as typeahead from "../shared/src/typeahead";
|
||||||
import * as blueslip from "./blueslip";
|
import * as blueslip from "./blueslip";
|
||||||
import {FoldDict} from "./fold_dict";
|
import {FoldDict} from "./fold_dict";
|
||||||
import {$t} from "./i18n";
|
import {$t} from "./i18n";
|
||||||
|
import type {DisplayRecipientUser, Message, MessageWithBooleans} from "./message_store";
|
||||||
import * as message_user_ids from "./message_user_ids";
|
import * as message_user_ids from "./message_user_ids";
|
||||||
import * as muted_users from "./muted_users";
|
import * as muted_users from "./muted_users";
|
||||||
import {page_params} from "./page_params";
|
import {page_params} from "./page_params";
|
||||||
|
@ -13,7 +14,6 @@ import * as reload_state from "./reload_state";
|
||||||
import * as settings_config from "./settings_config";
|
import * as settings_config from "./settings_config";
|
||||||
import * as settings_data from "./settings_data";
|
import * as settings_data from "./settings_data";
|
||||||
import * as timerender from "./timerender";
|
import * as timerender from "./timerender";
|
||||||
import type {DisplayRecipientUser, Message, MessageWithBooleans} from "./types";
|
|
||||||
import {user_settings} from "./user_settings";
|
import {user_settings} from "./user_settings";
|
||||||
import * as util from "./util";
|
import * as util from "./util";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {FoldDict} from "./fold_dict";
|
import {FoldDict} from "./fold_dict";
|
||||||
|
import type {Message} from "./message_store";
|
||||||
import * as muted_users from "./muted_users";
|
import * as muted_users from "./muted_users";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
import type {Message} from "./types";
|
|
||||||
|
|
||||||
type PMConversation = {
|
type PMConversation = {
|
||||||
user_ids_string: string;
|
user_ids_string: string;
|
||||||
|
|
|
@ -13,8 +13,8 @@ import render_widgets_poll_widget_results from "../templates/widgets/poll_widget
|
||||||
import * as blueslip from "./blueslip";
|
import * as blueslip from "./blueslip";
|
||||||
import {$t} from "./i18n";
|
import {$t} from "./i18n";
|
||||||
import * as keydown_util from "./keydown_util";
|
import * as keydown_util from "./keydown_util";
|
||||||
|
import type {Message} from "./message_store";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
import type {Message} from "./types";
|
|
||||||
|
|
||||||
type Event = {sender_id: number; data: InboundData};
|
type Event = {sender_id: number; data: InboundData};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import type {Message} from "./message_store";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
import {get_key_from_message} from "./recent_view_util";
|
import {get_key_from_message} from "./recent_view_util";
|
||||||
import type {Message} from "./types";
|
|
||||||
|
|
||||||
export type ConversationData = {
|
export type ConversationData = {
|
||||||
last_msg_id: number;
|
last_msg_id: number;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type {Message} from "./types";
|
import type {Message} from "./message_store";
|
||||||
|
|
||||||
let is_view_visible = false;
|
let is_view_visible = false;
|
||||||
|
|
||||||
|
|
138
web/src/types.ts
138
web/src/types.ts
|
@ -1,43 +1,3 @@
|
||||||
// TODO/typescript: Move this to message_store
|
|
||||||
export type MatchedMessage = {
|
|
||||||
match_content?: string;
|
|
||||||
match_subject?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO/typescript: Move this to message_store
|
|
||||||
export type MessageReactionType = "unicode_emoji" | "realm_emoji" | "zulip_extra_emoji";
|
|
||||||
|
|
||||||
// TODO/typescript: Move these types to message_store
|
|
||||||
|
|
||||||
export type DisplayRecipientUser = {
|
|
||||||
email: string;
|
|
||||||
full_name: string;
|
|
||||||
id: number;
|
|
||||||
is_mirror_dummy: boolean;
|
|
||||||
unknown_local_echo_user?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DisplayRecipient = string | DisplayRecipientUser[];
|
|
||||||
|
|
||||||
export type MessageEditHistoryEntry = {
|
|
||||||
user_id: number | null;
|
|
||||||
timestamp: number;
|
|
||||||
prev_content?: string;
|
|
||||||
prev_rendered_content?: string;
|
|
||||||
prev_rendered_content_version?: number;
|
|
||||||
prev_stream?: number;
|
|
||||||
prev_topic?: string;
|
|
||||||
stream?: number;
|
|
||||||
topic?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type MessageReaction = {
|
|
||||||
emoji_name: string;
|
|
||||||
emoji_code: string;
|
|
||||||
reaction_type: MessageReactionType;
|
|
||||||
user_id: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO/typescript: Move this to submessage.js
|
// TODO/typescript: Move this to submessage.js
|
||||||
export type Submessage = {
|
export type Submessage = {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -53,104 +13,6 @@ export type TopicLink = {
|
||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO/typescript: Move this to message_store
|
|
||||||
export type RawMessage = {
|
|
||||||
avatar_url: string | null;
|
|
||||||
client: string;
|
|
||||||
content: string;
|
|
||||||
content_type: "text/html";
|
|
||||||
display_recipient: DisplayRecipient;
|
|
||||||
edit_history?: MessageEditHistoryEntry[];
|
|
||||||
id: number;
|
|
||||||
is_me_message: boolean;
|
|
||||||
last_edit_timestamp?: number;
|
|
||||||
reactions: MessageReaction[];
|
|
||||||
recipient_id: number;
|
|
||||||
sender_email: string;
|
|
||||||
sender_full_name: string;
|
|
||||||
sender_id: number;
|
|
||||||
sender_realm_str: string;
|
|
||||||
submessages: Submessage[];
|
|
||||||
timestamp: number;
|
|
||||||
flags: string[];
|
|
||||||
} & (
|
|
||||||
| {
|
|
||||||
type: "private";
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "stream";
|
|
||||||
stream_id: number;
|
|
||||||
subject: string;
|
|
||||||
topic_links: TopicLink[];
|
|
||||||
}
|
|
||||||
) &
|
|
||||||
MatchedMessage;
|
|
||||||
|
|
||||||
// We add these boolean properties to Raw message in `message_store.set_message_booleans` method.
|
|
||||||
export type MessageWithBooleans = (
|
|
||||||
| Omit<RawMessage & {type: "private"}, "flags">
|
|
||||||
| Omit<RawMessage & {type: "stream"}, "flags">
|
|
||||||
) & {
|
|
||||||
unread: boolean;
|
|
||||||
historical: boolean;
|
|
||||||
starred: boolean;
|
|
||||||
mentioned: boolean;
|
|
||||||
mentioned_me_directly: boolean;
|
|
||||||
stream_wildcard_mentioned: boolean;
|
|
||||||
topic_wildcard_mentioned: boolean;
|
|
||||||
collapsed: boolean;
|
|
||||||
alerted: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO/typescript: Move this to message_store
|
|
||||||
export type MessageCleanReaction = {
|
|
||||||
class: string;
|
|
||||||
count: number;
|
|
||||||
emoji_alt_code: boolean;
|
|
||||||
emoji_code: string;
|
|
||||||
emoji_name: string;
|
|
||||||
is_realm_emoji: boolean;
|
|
||||||
label: string;
|
|
||||||
local_id: string;
|
|
||||||
reaction_type: string;
|
|
||||||
user_ids: number[];
|
|
||||||
vote_text: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO/typescript: Move this to message_store
|
|
||||||
export type Message = (
|
|
||||||
| Omit<MessageWithBooleans & {type: "private"}, "reactions">
|
|
||||||
| Omit<MessageWithBooleans & {type: "stream"}, "reactions">
|
|
||||||
) & {
|
|
||||||
// Added in `reactions.set_clean_reactions`.
|
|
||||||
clean_reactions: Map<string, MessageCleanReaction>;
|
|
||||||
|
|
||||||
// Added in `message_helper.process_new_message`.
|
|
||||||
sent_by_me: boolean;
|
|
||||||
reply_to: string;
|
|
||||||
display_reply_to?: string;
|
|
||||||
|
|
||||||
// These properties are used in `message_list_view.js`.
|
|
||||||
starred_status: string;
|
|
||||||
message_reactions: MessageCleanReaction[];
|
|
||||||
url: string;
|
|
||||||
} & (
|
|
||||||
| {
|
|
||||||
type: "private";
|
|
||||||
is_private: true;
|
|
||||||
is_stream: false;
|
|
||||||
pm_with_url: string;
|
|
||||||
to_user_ids: string;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "stream";
|
|
||||||
is_private: false;
|
|
||||||
is_stream: true;
|
|
||||||
stream: string;
|
|
||||||
topic: string;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO/typescript: Move this to server_events_dispatch
|
// TODO/typescript: Move this to server_events_dispatch
|
||||||
export type UserGroupUpdateEvent = {
|
export type UserGroupUpdateEvent = {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
|
@ -2,7 +2,8 @@ import _ from "lodash";
|
||||||
|
|
||||||
import * as blueslip from "./blueslip";
|
import * as blueslip from "./blueslip";
|
||||||
import {$t} from "./i18n";
|
import {$t} from "./i18n";
|
||||||
import type {MatchedMessage, Message, RawMessage, UpdateMessageEvent} from "./types";
|
import type {MatchedMessage, Message, RawMessage} from "./message_store";
|
||||||
|
import type {UpdateMessageEvent} from "./types";
|
||||||
|
|
||||||
// From MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/random
|
// From MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/random
|
||||||
export function random_int(min: number, max: number): number {
|
export function random_int(min: number, max: number): number {
|
||||||
|
|
|
@ -336,13 +336,13 @@ test("update_property", () => {
|
||||||
|
|
||||||
assert.equal(message1.sender_full_name, alice.full_name);
|
assert.equal(message1.sender_full_name, alice.full_name);
|
||||||
assert.equal(message2.sender_full_name, bob.full_name);
|
assert.equal(message2.sender_full_name, bob.full_name);
|
||||||
message_store.update_property("sender_full_name", "Bobby", {user_id: bob.user_id});
|
message_store.update_sender_full_name(bob.user_id, "Bobby");
|
||||||
assert.equal(message1.sender_full_name, alice.full_name);
|
assert.equal(message1.sender_full_name, alice.full_name);
|
||||||
assert.equal(message2.sender_full_name, "Bobby");
|
assert.equal(message2.sender_full_name, "Bobby");
|
||||||
|
|
||||||
assert.equal(message1.small_avatar_url, "alice_url");
|
assert.equal(message1.small_avatar_url, "alice_url");
|
||||||
assert.equal(message2.small_avatar_url, "bob_url");
|
assert.equal(message2.small_avatar_url, "bob_url");
|
||||||
message_store.update_property("small_avatar_url", "bobby_url", {user_id: bob.user_id});
|
message_store.update_small_avatar_url(bob.user_id, "bobby_url");
|
||||||
assert.equal(message1.small_avatar_url, "alice_url");
|
assert.equal(message1.small_avatar_url, "alice_url");
|
||||||
assert.equal(message2.small_avatar_url, "bobby_url");
|
assert.equal(message2.small_avatar_url, "bobby_url");
|
||||||
|
|
||||||
|
@ -350,7 +350,7 @@ test("update_property", () => {
|
||||||
assert.equal(message1.display_recipient, devel.name);
|
assert.equal(message1.display_recipient, devel.name);
|
||||||
assert.equal(message2.stream_id, denmark.stream_id);
|
assert.equal(message2.stream_id, denmark.stream_id);
|
||||||
assert.equal(message2.display_recipient, denmark.name);
|
assert.equal(message2.display_recipient, denmark.name);
|
||||||
message_store.update_property("stream_name", "Prod", {stream_id: devel.stream_id});
|
message_store.update_stream_name(devel.stream_id, "Prod");
|
||||||
assert.equal(message1.stream_id, devel.stream_id);
|
assert.equal(message1.stream_id, devel.stream_id);
|
||||||
assert.equal(message1.display_recipient, "Prod");
|
assert.equal(message1.display_recipient, "Prod");
|
||||||
assert.equal(message2.stream_id, denmark.stream_id);
|
assert.equal(message2.stream_id, denmark.stream_id);
|
||||||
|
|
Loading…
Reference in New Issue