mirror of https://github.com/zulip/zulip.git
ts: Convert `activity.js` module to TypeScript.
This commit is contained in:
parent
0ca6d58e23
commit
b86022ea52
|
@ -1,10 +1,34 @@
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
|
import assert from "minimalistic-assert";
|
||||||
|
import {z} from "zod";
|
||||||
|
|
||||||
import * as channel from "./channel";
|
import * as channel from "./channel";
|
||||||
import {page_params} from "./page_params";
|
import {page_params} from "./page_params";
|
||||||
import * as presence from "./presence";
|
import * as presence from "./presence";
|
||||||
import * as watchdog from "./watchdog";
|
import * as watchdog from "./watchdog";
|
||||||
|
|
||||||
|
const post_presence_response_schema = z.object({
|
||||||
|
msg: z.string(),
|
||||||
|
result: z.string(),
|
||||||
|
server_timestamp: z.number().optional(),
|
||||||
|
zephyr_mirror_active: z.boolean().optional(),
|
||||||
|
presences: z
|
||||||
|
.record(
|
||||||
|
z.string(),
|
||||||
|
z.object({
|
||||||
|
active_timestamp: z.number(),
|
||||||
|
idle_timestamp: z.number(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Keep in sync with views.py:update_active_status_backend() */
|
||||||
|
export enum ActivityState {
|
||||||
|
ACTIVE = "active",
|
||||||
|
IDLE = "idle",
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Helpers for detecting user activity and managing user idle states
|
Helpers for detecting user activity and managing user idle states
|
||||||
*/
|
*/
|
||||||
|
@ -12,11 +36,6 @@ import * as watchdog from "./watchdog";
|
||||||
/* Broadcast "idle" to server after 5 minutes of local inactivity */
|
/* Broadcast "idle" to server after 5 minutes of local inactivity */
|
||||||
const DEFAULT_IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
const DEFAULT_IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
||||||
|
|
||||||
/* Keep in sync with views.py:update_active_status_backend() */
|
|
||||||
export const ACTIVE = "active";
|
|
||||||
|
|
||||||
export const IDLE = "idle";
|
|
||||||
|
|
||||||
// When you open Zulip in a new browser window, client_is_active
|
// When you open Zulip in a new browser window, client_is_active
|
||||||
// should be true. When a server-initiated reload happens, however,
|
// should be true. When a server-initiated reload happens, however,
|
||||||
// it should be initialized to false. We handle this with a check for
|
// it should be initialized to false. We handle this with a check for
|
||||||
|
@ -30,22 +49,22 @@ export let client_is_active = document.hasFocus && document.hasFocus();
|
||||||
// server-initiated reload as user activity.
|
// server-initiated reload as user activity.
|
||||||
export let new_user_input = true;
|
export let new_user_input = true;
|
||||||
|
|
||||||
export function set_new_user_input(value) {
|
export function set_new_user_input(value: boolean): void {
|
||||||
new_user_input = value;
|
new_user_input = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clear_for_testing() {
|
export function clear_for_testing(): void {
|
||||||
client_is_active = false;
|
client_is_active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mark_client_idle() {
|
export function mark_client_idle(): void {
|
||||||
// When we become idle, we don't immediately send anything to the
|
// When we become idle, we don't immediately send anything to the
|
||||||
// server; instead, we wait for our next periodic update, since
|
// server; instead, we wait for our next periodic update, since
|
||||||
// this data is fundamentally not timely.
|
// this data is fundamentally not timely.
|
||||||
client_is_active = false;
|
client_is_active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compute_active_status() {
|
export function compute_active_status(): ActivityState {
|
||||||
// The overall algorithm intent for the `status` field is to send
|
// The overall algorithm intent for the `status` field is to send
|
||||||
// `ACTIVE` (aka green circle) if we know the user is at their
|
// `ACTIVE` (aka green circle) if we know the user is at their
|
||||||
// computer, and IDLE (aka orange circle) if the user might not
|
// computer, and IDLE (aka orange circle) if the user might not
|
||||||
|
@ -62,18 +81,18 @@ export function compute_active_status() {
|
||||||
window.electron_bridge.get_idle_on_system !== undefined
|
window.electron_bridge.get_idle_on_system !== undefined
|
||||||
) {
|
) {
|
||||||
if (window.electron_bridge.get_idle_on_system()) {
|
if (window.electron_bridge.get_idle_on_system()) {
|
||||||
return IDLE;
|
return ActivityState.IDLE;
|
||||||
}
|
}
|
||||||
return ACTIVE;
|
return ActivityState.ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_is_active) {
|
if (client_is_active) {
|
||||||
return ACTIVE;
|
return ActivityState.ACTIVE;
|
||||||
}
|
}
|
||||||
return IDLE;
|
return ActivityState.IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function send_presence_to_server(redraw) {
|
export function send_presence_to_server(redraw?: () => void): void {
|
||||||
// Zulip has 2 data feeds coming from the server to the client:
|
// Zulip has 2 data feeds coming from the server to the client:
|
||||||
// The server_events data, and this presence feed. Data from
|
// The server_events data, and this presence feed. Data from
|
||||||
// server_events is nicely serialized, but if we've been offline
|
// server_events is nicely serialized, but if we've been offline
|
||||||
|
@ -96,7 +115,7 @@ export function send_presence_to_server(redraw) {
|
||||||
|
|
||||||
watchdog.check_for_unsuspend();
|
watchdog.check_for_unsuspend();
|
||||||
|
|
||||||
channel.post({
|
void channel.post({
|
||||||
url: "/json/users/me/presence",
|
url: "/json/users/me/presence",
|
||||||
data: {
|
data: {
|
||||||
status: compute_active_status(),
|
status: compute_active_status(),
|
||||||
|
@ -104,7 +123,9 @@ export function send_presence_to_server(redraw) {
|
||||||
new_user_input,
|
new_user_input,
|
||||||
slim_presence: true,
|
slim_presence: true,
|
||||||
},
|
},
|
||||||
success(data) {
|
success(response) {
|
||||||
|
const data = post_presence_response_schema.parse(response);
|
||||||
|
|
||||||
// Update Zephyr mirror activity warning
|
// Update Zephyr mirror activity warning
|
||||||
if (data.zephyr_mirror_active === false) {
|
if (data.zephyr_mirror_active === false) {
|
||||||
$("#zephyr-mirror-error").addClass("show");
|
$("#zephyr-mirror-error").addClass("show");
|
||||||
|
@ -115,6 +136,14 @@ export function send_presence_to_server(redraw) {
|
||||||
new_user_input = false;
|
new_user_input = false;
|
||||||
|
|
||||||
if (redraw) {
|
if (redraw) {
|
||||||
|
assert(
|
||||||
|
data.presences !== undefined,
|
||||||
|
"Presences should be present if not a ping only presence request",
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
data.server_timestamp !== undefined,
|
||||||
|
"Server timestamp should be present if not a ping only presence request",
|
||||||
|
);
|
||||||
presence.set_info(data.presences, data.server_timestamp);
|
presence.set_info(data.presences, data.server_timestamp);
|
||||||
redraw();
|
redraw();
|
||||||
}
|
}
|
||||||
|
@ -122,7 +151,7 @@ export function send_presence_to_server(redraw) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mark_client_active() {
|
export function mark_client_active(): void {
|
||||||
// exported for testing
|
// exported for testing
|
||||||
if (!client_is_active) {
|
if (!client_is_active) {
|
||||||
client_is_active = true;
|
client_is_active = true;
|
||||||
|
@ -130,7 +159,7 @@ export function mark_client_active() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initialize() {
|
export function initialize(): void {
|
||||||
$("html").on("mousemove", () => {
|
$("html").on("mousemove", () => {
|
||||||
new_user_input = true;
|
new_user_input = true;
|
||||||
});
|
});
|
|
@ -14,6 +14,14 @@ type JQueryCaretRange = {
|
||||||
text: string;
|
text: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type JQueryIdleOptions = Partial<{
|
||||||
|
idle: number;
|
||||||
|
events: string;
|
||||||
|
onIdle: () => void;
|
||||||
|
onActive: () => void;
|
||||||
|
keepTracking: boolean;
|
||||||
|
}>;
|
||||||
|
|
||||||
declare namespace JQueryValidation {
|
declare namespace JQueryValidation {
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||||
interface ValidationOptions {
|
interface ValidationOptions {
|
||||||
|
@ -37,6 +45,12 @@ interface JQuery {
|
||||||
range(text: string): this;
|
range(text: string): this;
|
||||||
selectAll(): this;
|
selectAll(): this;
|
||||||
deselectAll(): this;
|
deselectAll(): this;
|
||||||
|
|
||||||
|
// Types for jquery-idle plugin
|
||||||
|
idle(opts: JQueryIdleOptions): {
|
||||||
|
cancel: () => void;
|
||||||
|
reset: () => void;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const ZULIP_VERSION: string;
|
declare const ZULIP_VERSION: string;
|
||||||
|
|
|
@ -167,7 +167,7 @@ export function update_info_from_event(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function set_info(
|
export function set_info(
|
||||||
presences: Map<number, Omit<RawPresence, "server_timestamp">>,
|
presences: Record<number, Omit<RawPresence, "server_timestamp">>,
|
||||||
server_timestamp: number,
|
server_timestamp: number,
|
||||||
): void {
|
): void {
|
||||||
/*
|
/*
|
||||||
|
@ -278,7 +278,7 @@ export function last_active_date(user_id: number): Date | undefined {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initialize(params: {
|
export function initialize(params: {
|
||||||
presences: Map<number, Omit<RawPresence, "server_timestamp">>;
|
presences: Record<number, Omit<RawPresence, "server_timestamp">>;
|
||||||
server_timestamp: number;
|
server_timestamp: number;
|
||||||
}): void {
|
}): void {
|
||||||
set_info(params.presences, params.server_timestamp);
|
set_info(params.presences, params.server_timestamp);
|
||||||
|
|
|
@ -687,6 +687,9 @@ test("initialize", ({override, mock_template}) => {
|
||||||
payload.success({
|
payload.success({
|
||||||
zephyr_mirror_active: true,
|
zephyr_mirror_active: true,
|
||||||
presences: {},
|
presences: {},
|
||||||
|
msg: "",
|
||||||
|
result: "success",
|
||||||
|
server_timestamp: 0,
|
||||||
});
|
});
|
||||||
$(window).trigger("focus");
|
$(window).trigger("focus");
|
||||||
clear();
|
clear();
|
||||||
|
@ -708,6 +711,9 @@ test("initialize", ({override, mock_template}) => {
|
||||||
payload.success({
|
payload.success({
|
||||||
zephyr_mirror_active: false,
|
zephyr_mirror_active: false,
|
||||||
presences: {},
|
presences: {},
|
||||||
|
msg: "",
|
||||||
|
result: "success",
|
||||||
|
server_timestamp: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.ok($("#zephyr-mirror-error").hasClass("show"));
|
assert.ok($("#zephyr-mirror-error").hasClass("show"));
|
||||||
|
|
Loading…
Reference in New Issue