mirror of https://github.com/zulip/zulip.git
ts: Migrate `user_status` module to TypeScript.
Also removed an defensive if check from `emoji.ts` since it is not needed now that we have `emoji.ts` converted to TypeScript.
This commit is contained in:
parent
951e31a154
commit
dcf45da09c
|
@ -270,7 +270,7 @@ EXEMPT_FILES = make_set(
|
||||||
"web/src/user_profile.js",
|
"web/src/user_profile.js",
|
||||||
"web/src/user_settings.ts",
|
"web/src/user_settings.ts",
|
||||||
"web/src/user_sort.js",
|
"web/src/user_sort.js",
|
||||||
"web/src/user_status.js",
|
"web/src/user_status.ts",
|
||||||
"web/src/user_status_ui.js",
|
"web/src/user_status_ui.js",
|
||||||
"web/src/user_topic_popover.js",
|
"web/src/user_topic_popover.js",
|
||||||
"web/src/user_topics.ts",
|
"web/src/user_topics.ts",
|
||||||
|
|
|
@ -316,13 +316,9 @@ export function get_emoji_details_by_name(emoji_name: string): EmojiRenderingDet
|
||||||
|
|
||||||
export function get_emoji_details_for_rendering(opts: {
|
export function get_emoji_details_for_rendering(opts: {
|
||||||
emoji_name: string;
|
emoji_name: string;
|
||||||
emoji_code: string | number;
|
emoji_code: string;
|
||||||
reaction_type: string;
|
reaction_type: string;
|
||||||
}): EmojiRenderingDetails {
|
}): EmojiRenderingDetails {
|
||||||
if (!opts.emoji_name || !opts.emoji_code || !opts.reaction_type) {
|
|
||||||
throw new Error("Invalid params.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.reaction_type !== "unicode_emoji") {
|
if (opts.reaction_type !== "unicode_emoji") {
|
||||||
const realm_emoji = all_realm_emojis.get(opts.emoji_code);
|
const realm_emoji = all_realm_emojis.get(opts.emoji_code);
|
||||||
if (!realm_emoji) {
|
if (!realm_emoji) {
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
import * as channel from "./channel";
|
|
||||||
import * as emoji from "./emoji";
|
|
||||||
import {user_settings} from "./user_settings";
|
|
||||||
|
|
||||||
const user_info = new Map();
|
|
||||||
const user_status_emoji_info = new Map();
|
|
||||||
|
|
||||||
export function server_update_status(opts) {
|
|
||||||
channel.post({
|
|
||||||
url: "/json/users/me/status",
|
|
||||||
data: {
|
|
||||||
status_text: opts.status_text,
|
|
||||||
emoji_name: opts.emoji_name,
|
|
||||||
emoji_code: opts.emoji_code,
|
|
||||||
reaction_type: opts.reaction_type,
|
|
||||||
},
|
|
||||||
success() {
|
|
||||||
if (opts.success) {
|
|
||||||
opts.success();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function server_invisible_mode_on() {
|
|
||||||
channel.patch({
|
|
||||||
url: "/json/settings",
|
|
||||||
data: {
|
|
||||||
presence_enabled: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function server_invisible_mode_off() {
|
|
||||||
channel.patch({
|
|
||||||
url: "/json/settings",
|
|
||||||
data: {
|
|
||||||
presence_enabled: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get_status_text(user_id) {
|
|
||||||
return user_info.get(user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function set_status_text(opts) {
|
|
||||||
if (!opts.status_text) {
|
|
||||||
user_info.delete(opts.user_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user_info.set(opts.user_id, opts.status_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get_status_emoji(user_id) {
|
|
||||||
return user_status_emoji_info.get(user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function set_status_emoji(opts) {
|
|
||||||
if (!opts.emoji_name) {
|
|
||||||
user_status_emoji_info.delete(opts.user_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user_status_emoji_info.set(opts.user_id, {
|
|
||||||
emoji_alt_code: user_settings.emojiset === "text",
|
|
||||||
...emoji.get_emoji_details_for_rendering({
|
|
||||||
emoji_name: opts.emoji_name,
|
|
||||||
emoji_code: opts.emoji_code,
|
|
||||||
reaction_type: opts.reaction_type,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initialize(params) {
|
|
||||||
user_info.clear();
|
|
||||||
|
|
||||||
for (const [str_user_id, dct] of Object.entries(params.user_status)) {
|
|
||||||
// JSON does not allow integer keys, so we
|
|
||||||
// convert them here.
|
|
||||||
const user_id = Number.parseInt(str_user_id, 10);
|
|
||||||
|
|
||||||
if (dct.status_text) {
|
|
||||||
user_info.set(user_id, dct.status_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dct.emoji_name) {
|
|
||||||
user_status_emoji_info.set(user_id, {
|
|
||||||
...emoji.get_emoji_details_for_rendering(dct),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
import {z} from "zod";
|
||||||
|
|
||||||
|
import * as channel from "./channel";
|
||||||
|
import * as emoji from "./emoji";
|
||||||
|
import type {EmojiRenderingDetails} from "./emoji";
|
||||||
|
import {user_settings} from "./user_settings";
|
||||||
|
|
||||||
|
export type UserStatus = z.infer<typeof user_status_schema>;
|
||||||
|
export type UserStatusEmojiInfo = EmojiRenderingDetails & {
|
||||||
|
emoji_alt_code?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserStatusEvent = z.infer<typeof user_status_event_schema>;
|
||||||
|
|
||||||
|
const user_status_event_schema = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
type: z.literal("user_status"),
|
||||||
|
user_id: z.number(),
|
||||||
|
away: z.boolean().optional(),
|
||||||
|
status_text: z.string(),
|
||||||
|
emoji_name: z.string(),
|
||||||
|
emoji_code: z.string(),
|
||||||
|
reaction_type: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const user_status_schema = z.union([
|
||||||
|
z.object({
|
||||||
|
status_text: z.string().optional(),
|
||||||
|
emoji_name: z.string(),
|
||||||
|
emoji_code: z.string(),
|
||||||
|
reaction_type: z.string(),
|
||||||
|
away: z.boolean().optional(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
emoji_name: z.undefined(),
|
||||||
|
status_text: z.string().optional(),
|
||||||
|
away: z.boolean().optional(),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const user_status_param_schema = z.record(z.coerce.number(), user_status_schema);
|
||||||
|
|
||||||
|
const user_info = new Map<number, string>();
|
||||||
|
const user_status_emoji_info = new Map<number, UserStatusEmojiInfo>();
|
||||||
|
|
||||||
|
export function server_update_status(opts: {
|
||||||
|
status_text: string;
|
||||||
|
emoji_name: string;
|
||||||
|
emoji_code: string;
|
||||||
|
reaction_type?: string;
|
||||||
|
success?: () => void;
|
||||||
|
}): void {
|
||||||
|
void channel.post({
|
||||||
|
url: "/json/users/me/status",
|
||||||
|
data: {
|
||||||
|
status_text: opts.status_text,
|
||||||
|
emoji_name: opts.emoji_name,
|
||||||
|
emoji_code: opts.emoji_code,
|
||||||
|
reaction_type: opts.reaction_type,
|
||||||
|
},
|
||||||
|
success() {
|
||||||
|
if (opts.success) {
|
||||||
|
opts.success();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function server_invisible_mode_on(): void {
|
||||||
|
void channel.patch({
|
||||||
|
url: "/json/settings",
|
||||||
|
data: {
|
||||||
|
presence_enabled: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function server_invisible_mode_off(): void {
|
||||||
|
void channel.patch({
|
||||||
|
url: "/json/settings",
|
||||||
|
data: {
|
||||||
|
presence_enabled: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get_status_text(user_id: number): string | undefined {
|
||||||
|
return user_info.get(user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function set_status_text(opts: {user_id: number; status_text: string}): void {
|
||||||
|
if (!opts.status_text) {
|
||||||
|
user_info.delete(opts.user_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user_info.set(opts.user_id, opts.status_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get_status_emoji(user_id: number): UserStatusEmojiInfo | undefined {
|
||||||
|
return user_status_emoji_info.get(user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function set_status_emoji(event: UserStatusEvent): void {
|
||||||
|
const opts = user_status_event_schema.parse(event);
|
||||||
|
|
||||||
|
if (!opts.emoji_name) {
|
||||||
|
user_status_emoji_info.delete(opts.user_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user_status_emoji_info.set(opts.user_id, {
|
||||||
|
emoji_alt_code: user_settings.emojiset === "text",
|
||||||
|
...emoji.get_emoji_details_for_rendering({
|
||||||
|
emoji_name: opts.emoji_name,
|
||||||
|
emoji_code: opts.emoji_code,
|
||||||
|
reaction_type: opts.reaction_type,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initialize(params: {user_status: object}): void {
|
||||||
|
user_info.clear();
|
||||||
|
|
||||||
|
const user_status = user_status_param_schema.parse(params.user_status);
|
||||||
|
|
||||||
|
for (const [str_user_id, dct] of Object.entries(user_status)) {
|
||||||
|
// JSON does not allow integer keys, so we
|
||||||
|
// convert them here.
|
||||||
|
const user_id = Number.parseInt(str_user_id, 10);
|
||||||
|
|
||||||
|
if (dct.status_text) {
|
||||||
|
user_info.set(user_id, dct.status_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dct.emoji_name) {
|
||||||
|
user_status_emoji_info.set(user_id, {
|
||||||
|
...emoji.get_emoji_details_for_rendering(dct),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -489,6 +489,7 @@ test("get_items_for_users", () => {
|
||||||
set_presence(alice.user_id, "offline");
|
set_presence(alice.user_id, "offline");
|
||||||
user_settings.emojiset = "google";
|
user_settings.emojiset = "google";
|
||||||
user_settings.user_list_style = 2;
|
user_settings.user_list_style = 2;
|
||||||
|
|
||||||
const status_emoji_info = {
|
const status_emoji_info = {
|
||||||
emoji_alt_code: false,
|
emoji_alt_code: false,
|
||||||
emoji_name: "car",
|
emoji_name: "car",
|
||||||
|
@ -496,9 +497,16 @@ test("get_items_for_users", () => {
|
||||||
reaction_type: "unicode_emoji",
|
reaction_type: "unicode_emoji",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const status_emoji_info_event = {
|
||||||
|
id: 1,
|
||||||
|
type: "user_status",
|
||||||
|
status_text: "",
|
||||||
|
...status_emoji_info,
|
||||||
|
};
|
||||||
|
|
||||||
const user_ids = [me.user_id, alice.user_id, fred.user_id];
|
const user_ids = [me.user_id, alice.user_id, fred.user_id];
|
||||||
for (const user_id of user_ids) {
|
for (const user_id of user_ids) {
|
||||||
user_status.set_status_emoji({user_id, ...status_emoji_info});
|
user_status.set_status_emoji({user_id, ...status_emoji_info_event});
|
||||||
}
|
}
|
||||||
|
|
||||||
const user_list_style = {
|
const user_list_style = {
|
||||||
|
|
|
@ -1152,9 +1152,10 @@ run_test("user_status", ({override}) => {
|
||||||
{
|
{
|
||||||
const stub = make_stub();
|
const stub = make_stub();
|
||||||
override(activity_ui, "redraw_user", stub.f);
|
override(activity_ui, "redraw_user", stub.f);
|
||||||
|
override(compose_pm_pill, "get_user_ids", () => [event.user_id]);
|
||||||
override(pm_list, "update_private_messages", noop);
|
override(pm_list, "update_private_messages", noop);
|
||||||
dispatch(event);
|
dispatch(event);
|
||||||
assert.equal(stub.num_calls, 1);
|
assert.equal(stub.num_calls, 2);
|
||||||
const args = stub.get_args("user_id");
|
const args = stub.get_args("user_id");
|
||||||
assert_same(args.user_id, test_user.user_id);
|
assert_same(args.user_id, test_user.user_id);
|
||||||
const emoji_info = user_status.get_status_emoji(test_user.user_id);
|
const emoji_info = user_status.get_status_emoji(test_user.user_id);
|
||||||
|
|
|
@ -1068,11 +1068,13 @@ exports.fixtures = {
|
||||||
},
|
},
|
||||||
|
|
||||||
user_status__set_status_emoji: {
|
user_status__set_status_emoji: {
|
||||||
|
id: 1,
|
||||||
type: "user_status",
|
type: "user_status",
|
||||||
user_id: test_user.user_id,
|
user_id: test_user.user_id,
|
||||||
emoji_name: "smiley",
|
emoji_name: "smiley",
|
||||||
emoji_code: "1f603",
|
emoji_code: "1f603",
|
||||||
reaction_type: "unicode_emoji",
|
reaction_type: "unicode_emoji",
|
||||||
|
status_text: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
user_status__set_status_text: {
|
user_status__set_status_text: {
|
||||||
|
|
|
@ -60,10 +60,13 @@ run_test("basics", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
user_status.set_status_emoji({
|
user_status.set_status_emoji({
|
||||||
|
id: 1,
|
||||||
user_id: 5,
|
user_id: 5,
|
||||||
|
type: "user_status",
|
||||||
emoji_code: "991",
|
emoji_code: "991",
|
||||||
emoji_name: "example_realm_emoji",
|
emoji_name: "example_realm_emoji",
|
||||||
reaction_type: "realm_emoji",
|
reaction_type: "realm_emoji",
|
||||||
|
status_text: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.deepEqual(user_status.get_status_emoji(5), {
|
assert.deepEqual(user_status.get_status_emoji(5), {
|
||||||
|
@ -78,8 +81,13 @@ run_test("basics", () => {
|
||||||
assert.equal(user_status.get_status_text(1), "in a meeting");
|
assert.equal(user_status.get_status_text(1), "in a meeting");
|
||||||
|
|
||||||
user_status.set_status_text({
|
user_status.set_status_text({
|
||||||
|
id: 2,
|
||||||
user_id: 2,
|
user_id: 2,
|
||||||
|
type: "user_status",
|
||||||
status_text: "out to lunch",
|
status_text: "out to lunch",
|
||||||
|
emoji_name: "",
|
||||||
|
emoji_code: "",
|
||||||
|
reaction_type: "",
|
||||||
});
|
});
|
||||||
assert.equal(user_status.get_status_text(2), "out to lunch");
|
assert.equal(user_status.get_status_text(2), "out to lunch");
|
||||||
|
|
||||||
|
@ -90,10 +98,13 @@ run_test("basics", () => {
|
||||||
assert.equal(user_status.get_status_text(2), undefined);
|
assert.equal(user_status.get_status_text(2), undefined);
|
||||||
|
|
||||||
user_status.set_status_emoji({
|
user_status.set_status_emoji({
|
||||||
|
id: 3,
|
||||||
user_id: 2,
|
user_id: 2,
|
||||||
|
type: "user_status",
|
||||||
emoji_name: "smiley",
|
emoji_name: "smiley",
|
||||||
emoji_code: "1f603",
|
emoji_code: "1f603",
|
||||||
reaction_type: "unicode_emoji",
|
reaction_type: "unicode_emoji",
|
||||||
|
status_text: "",
|
||||||
});
|
});
|
||||||
assert.deepEqual(user_status.get_status_emoji(2), {
|
assert.deepEqual(user_status.get_status_emoji(2), {
|
||||||
emoji_name: "smiley",
|
emoji_name: "smiley",
|
||||||
|
@ -103,10 +114,13 @@ run_test("basics", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
user_status.set_status_emoji({
|
user_status.set_status_emoji({
|
||||||
|
id: 4,
|
||||||
user_id: 2,
|
user_id: 2,
|
||||||
|
type: "user_status",
|
||||||
emoji_name: "",
|
emoji_name: "",
|
||||||
emoji_code: "",
|
emoji_code: "",
|
||||||
reaction_type: "",
|
reaction_type: "",
|
||||||
|
status_text: "",
|
||||||
});
|
});
|
||||||
assert.deepEqual(user_status.get_status_emoji(2), undefined);
|
assert.deepEqual(user_status.get_status_emoji(2), undefined);
|
||||||
});
|
});
|
||||||
|
@ -142,23 +156,28 @@ run_test("defensive checks", () => {
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() =>
|
() =>
|
||||||
user_status.set_status_emoji({
|
user_status.set_status_emoji({
|
||||||
|
id: 1,
|
||||||
|
status_text: "",
|
||||||
|
type: "user_status",
|
||||||
user_id: 5,
|
user_id: 5,
|
||||||
emoji_name: "emoji",
|
emoji_name: "emoji",
|
||||||
// no status code or reaction type.
|
// no status code or reaction type.
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "Error",
|
name: "ZodError",
|
||||||
message: "Invalid params.",
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() =>
|
() =>
|
||||||
user_status.set_status_emoji({
|
user_status.set_status_emoji({
|
||||||
|
id: 2,
|
||||||
|
type: "user_status",
|
||||||
user_id: 5,
|
user_id: 5,
|
||||||
reaction_type: "realm_emoji",
|
reaction_type: "realm_emoji",
|
||||||
emoji_name: "does_not_exist",
|
emoji_name: "does_not_exist",
|
||||||
emoji_code: "fake_code",
|
emoji_code: "fake_code",
|
||||||
|
status_text: "",
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "Error",
|
name: "Error",
|
||||||
|
|
Loading…
Reference in New Issue