mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	This commit adds typing indicators for message editing in stream as well as in dm, if the send typing notification for corresponding is enabled. Based on earlier work in #28585. Co-authored-by: Rohan Gudimetla <rohan.gudimetla07@gmail.com> Fixes #25719.
		
			
				
	
	
		
			667 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			667 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
"use strict";
 | 
						|
 | 
						|
const assert = require("node:assert/strict");
 | 
						|
 | 
						|
const {mock_esm, set_global, zrequire} = require("./lib/namespace.cjs");
 | 
						|
const {run_test} = require("./lib/test.cjs");
 | 
						|
 | 
						|
const compose_pm_pill = mock_esm("../src/compose_pm_pill");
 | 
						|
const compose_state = mock_esm("../src/compose_state");
 | 
						|
const stream_data = mock_esm("../src/stream_data");
 | 
						|
 | 
						|
const {set_realm} = zrequire("state_data");
 | 
						|
const typing = zrequire("typing");
 | 
						|
const typing_status = zrequire("../shared/src/typing_status");
 | 
						|
const {initialize_user_settings} = zrequire("user_settings");
 | 
						|
 | 
						|
initialize_user_settings({user_settings: {}});
 | 
						|
const realm = {};
 | 
						|
set_realm(realm);
 | 
						|
 | 
						|
const TYPING_STARTED_WAIT_PERIOD = 10000;
 | 
						|
const TYPING_STOPPED_WAIT_PERIOD = 5000;
 | 
						|
 | 
						|
function make_time(secs) {
 | 
						|
    // make times semi-realistic
 | 
						|
    return 1000000 + 1000 * secs;
 | 
						|
}
 | 
						|
 | 
						|
function returns_time(secs) {
 | 
						|
    return function () {
 | 
						|
        return make_time(secs);
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
run_test("basics", ({override, override_rewire}) => {
 | 
						|
    override(realm, "realm_mandatory_topics", true);
 | 
						|
    override(realm, "server_typing_started_wait_period_milliseconds", TYPING_STARTED_WAIT_PERIOD);
 | 
						|
    override(realm, "server_typing_stopped_wait_period_milliseconds", TYPING_STOPPED_WAIT_PERIOD);
 | 
						|
 | 
						|
    assert.equal(typing_status.state, null);
 | 
						|
    // invalid conversation basically does nothing
 | 
						|
    let worker = {};
 | 
						|
    typing_status.update(
 | 
						|
        worker,
 | 
						|
        null,
 | 
						|
        realm.server_typing_started_wait_period_milliseconds,
 | 
						|
        realm.server_typing_stopped_wait_period_milliseconds,
 | 
						|
    );
 | 
						|
 | 
						|
    // Start setting up more testing state.
 | 
						|
    const events = {};
 | 
						|
 | 
						|
    function set_timeout(f, delay) {
 | 
						|
        assert.equal(delay, 5000);
 | 
						|
        events.idle_callback = f;
 | 
						|
        return "idle_timer_stub";
 | 
						|
    }
 | 
						|
 | 
						|
    function clear_timeout() {
 | 
						|
        events.timer_cleared = true;
 | 
						|
    }
 | 
						|
 | 
						|
    set_global("setTimeout", set_timeout);
 | 
						|
    set_global("clearTimeout", clear_timeout);
 | 
						|
 | 
						|
    function notify_server_start(recipient) {
 | 
						|
        assert.deepStrictEqual(recipient, {
 | 
						|
            message_type: "direct",
 | 
						|
            notification_event_type: "typing",
 | 
						|
            ids: [1, 2],
 | 
						|
        });
 | 
						|
        events.started = true;
 | 
						|
    }
 | 
						|
 | 
						|
    function notify_server_stop(recipient) {
 | 
						|
        assert.deepStrictEqual(recipient, {
 | 
						|
            message_type: "direct",
 | 
						|
            notification_event_type: "typing",
 | 
						|
            ids: [1, 2],
 | 
						|
        });
 | 
						|
        events.stopped = true;
 | 
						|
    }
 | 
						|
 | 
						|
    function clear_events() {
 | 
						|
        events.idle_callback = undefined;
 | 
						|
        events.started = false;
 | 
						|
        events.stopped = false;
 | 
						|
        events.timer_cleared = false;
 | 
						|
    }
 | 
						|
 | 
						|
    function call_handler(new_recipient) {
 | 
						|
        clear_events();
 | 
						|
        typing_status.update(
 | 
						|
            worker,
 | 
						|
            new_recipient,
 | 
						|
            realm.server_typing_started_wait_period_milliseconds,
 | 
						|
            realm.server_typing_stopped_wait_period_milliseconds,
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    worker = {
 | 
						|
        get_current_time: returns_time(5),
 | 
						|
        notify_server_start,
 | 
						|
        notify_server_stop,
 | 
						|
    };
 | 
						|
 | 
						|
    // Start talking to users having ids - 1, 2.
 | 
						|
    call_handler({message_type: "direct", notification_event_type: "typing", ids: [1, 2]});
 | 
						|
    assert.deepEqual(typing_status.state, {
 | 
						|
        next_send_start_time: make_time(5 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {message_type: "direct", notification_event_type: "typing", ids: [1, 2]},
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: true,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: false,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    // type again 3 seconds later
 | 
						|
    worker.get_current_time = returns_time(8);
 | 
						|
    call_handler({message_type: "direct", notification_event_type: "typing", ids: [1, 2]});
 | 
						|
    assert.deepEqual(typing_status.state, {
 | 
						|
        next_send_start_time: make_time(5 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {message_type: "direct", notification_event_type: "typing", ids: [1, 2]},
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: false,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    // type after 15 secs, so that we can notify the server
 | 
						|
    // again
 | 
						|
    worker.get_current_time = returns_time(18);
 | 
						|
    call_handler({message_type: "direct", notification_event_type: "typing", ids: [1, 2]});
 | 
						|
    assert.deepEqual(typing_status.state, {
 | 
						|
        next_send_start_time: make_time(18 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {message_type: "direct", notification_event_type: "typing", ids: [1, 2]},
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: true,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
 | 
						|
    // Now call recipients idle callback that we captured earlier.
 | 
						|
    const callback = events.idle_callback;
 | 
						|
    clear_events();
 | 
						|
    callback();
 | 
						|
    assert.deepEqual(typing_status.state, null);
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: undefined,
 | 
						|
        started: false,
 | 
						|
        stopped: true,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
 | 
						|
    // Call stop with nothing going on.
 | 
						|
    call_handler(null);
 | 
						|
    assert.deepEqual(typing_status.state, null);
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: undefined,
 | 
						|
        started: false,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: false,
 | 
						|
    });
 | 
						|
 | 
						|
    // Start talking to users again.
 | 
						|
    worker.get_current_time = returns_time(50);
 | 
						|
    call_handler({message_type: "direct", notification_event_type: "typing", ids: [1, 2]});
 | 
						|
    assert.deepEqual(typing_status.state, {
 | 
						|
        next_send_start_time: make_time(50 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {message_type: "direct", notification_event_type: "typing", ids: [1, 2]},
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: true,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: false,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    // Explicitly stop users.
 | 
						|
    call_handler(null);
 | 
						|
    assert.deepEqual(typing_status.state, null);
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: undefined,
 | 
						|
        started: false,
 | 
						|
        stopped: true,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
 | 
						|
    // Start talking to users again.
 | 
						|
    worker.get_current_time = returns_time(80);
 | 
						|
    call_handler({message_type: "direct", notification_event_type: "typing", ids: [1, 2]});
 | 
						|
    assert.deepEqual(typing_status.state, {
 | 
						|
        next_send_start_time: make_time(80 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {message_type: "direct", notification_event_type: "typing", ids: [1, 2]},
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: true,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: false,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    // Switch to an invalid conversation.
 | 
						|
    call_handler(null);
 | 
						|
    assert.deepEqual(typing_status.state, null);
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: undefined,
 | 
						|
        started: false,
 | 
						|
        stopped: true,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
 | 
						|
    // Switch to another invalid conversation.
 | 
						|
    call_handler(null);
 | 
						|
    assert.deepEqual(typing_status.state, null);
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: undefined,
 | 
						|
        started: false,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: false,
 | 
						|
    });
 | 
						|
 | 
						|
    // Start talking to users again.
 | 
						|
    worker.get_current_time = returns_time(170);
 | 
						|
    call_handler({message_type: "direct", notification_event_type: "typing", ids: [1, 2]});
 | 
						|
    assert.deepEqual(typing_status.state, {
 | 
						|
        next_send_start_time: make_time(170 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {message_type: "direct", notification_event_type: "typing", ids: [1, 2]},
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: true,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: false,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    // Switch to new users now.
 | 
						|
    worker.get_current_time = returns_time(171);
 | 
						|
 | 
						|
    worker.notify_server_start = (recipient) => {
 | 
						|
        assert.deepStrictEqual(recipient, {
 | 
						|
            message_type: "direct",
 | 
						|
            notification_event_type: "typing",
 | 
						|
            ids: [3, 4],
 | 
						|
        });
 | 
						|
        events.started = true;
 | 
						|
    };
 | 
						|
 | 
						|
    call_handler({message_type: "direct", notification_event_type: "typing", ids: [3, 4]});
 | 
						|
    assert.deepEqual(typing_status.state, {
 | 
						|
        next_send_start_time: make_time(171 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {message_type: "direct", notification_event_type: "typing", ids: [3, 4]},
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: true,
 | 
						|
        stopped: true,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    // If realm requires topics for channel messages and
 | 
						|
    // topic is an empty string, no typing recipient is set
 | 
						|
    override(compose_state, "get_message_type", () => "stream");
 | 
						|
    override(compose_state, "stream_name", () => "Verona");
 | 
						|
    override(stream_data, "get_stream_id", () => "2");
 | 
						|
    override(compose_state, "topic", () => "");
 | 
						|
    assert.equal(typing.get_recipient(), null);
 | 
						|
 | 
						|
    // test that we correctly detect if worker.get_recipient
 | 
						|
    // and typing_status.state.current_recipient are the same
 | 
						|
 | 
						|
    override(compose_pm_pill, "get_user_ids_string", () => "1,2,3");
 | 
						|
    override(compose_state, "get_message_type", () => "private");
 | 
						|
    typing_status.state.current_recipient = typing.get_recipient();
 | 
						|
 | 
						|
    const call_count = {
 | 
						|
        maybe_ping_server: 0,
 | 
						|
        actually_ping_server: 0,
 | 
						|
        start_or_extend_idle_timer: 0,
 | 
						|
        stop_last_notification: 0,
 | 
						|
    };
 | 
						|
 | 
						|
    // stub functions to see how may time they are called
 | 
						|
    for (const method of Object.keys(call_count)) {
 | 
						|
        override_rewire(typing_status, method, () => {
 | 
						|
            call_count[method] += 1;
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
    // User ids of people in compose narrow doesn't change and is same as state.current_recipient
 | 
						|
    // so counts of function should increase except stop_last_notification
 | 
						|
    typing_status.update(
 | 
						|
        worker,
 | 
						|
        typing.get_recipient(),
 | 
						|
        realm.server_typing_started_wait_period_milliseconds,
 | 
						|
        realm.server_typing_stopped_wait_period_milliseconds,
 | 
						|
    );
 | 
						|
    assert.deepEqual(call_count.maybe_ping_server, 1);
 | 
						|
    assert.deepEqual(call_count.start_or_extend_idle_timer, 1);
 | 
						|
    assert.deepEqual(call_count.stop_last_notification, 0);
 | 
						|
 | 
						|
    typing_status.update(
 | 
						|
        worker,
 | 
						|
        typing.get_recipient(),
 | 
						|
        realm.server_typing_started_wait_period_milliseconds,
 | 
						|
        realm.server_typing_stopped_wait_period_milliseconds,
 | 
						|
    );
 | 
						|
    assert.deepEqual(call_count.maybe_ping_server, 2);
 | 
						|
    assert.deepEqual(call_count.start_or_extend_idle_timer, 2);
 | 
						|
    assert.deepEqual(call_count.stop_last_notification, 0);
 | 
						|
 | 
						|
    // change in recipient and new_recipient should make us
 | 
						|
    // call typing_status.stop_last_notification
 | 
						|
    override(compose_pm_pill, "get_user_ids_string", () => "2,3,4");
 | 
						|
    typing_status.update(
 | 
						|
        worker,
 | 
						|
        typing.get_recipient(),
 | 
						|
        realm.server_typing_started_wait_period_milliseconds,
 | 
						|
        realm.server_typing_stopped_wait_period_milliseconds,
 | 
						|
    );
 | 
						|
    assert.deepEqual(call_count.maybe_ping_server, 2);
 | 
						|
    assert.deepEqual(call_count.start_or_extend_idle_timer, 3);
 | 
						|
    assert.deepEqual(call_count.stop_last_notification, 1);
 | 
						|
 | 
						|
    // Stream messages
 | 
						|
    override(compose_state, "get_message_type", () => "stream");
 | 
						|
    override(compose_state, "stream_name", () => "Verona");
 | 
						|
    override(stream_data, "get_stream_id", () => "2");
 | 
						|
    override(compose_state, "topic", () => "test topic");
 | 
						|
    typing_status.update(
 | 
						|
        worker,
 | 
						|
        typing.get_recipient(),
 | 
						|
        realm.server_typing_started_wait_period_milliseconds,
 | 
						|
        realm.server_typing_stopped_wait_period_milliseconds,
 | 
						|
    );
 | 
						|
    assert.deepEqual(call_count.maybe_ping_server, 2);
 | 
						|
    assert.deepEqual(call_count.start_or_extend_idle_timer, 4);
 | 
						|
    assert.deepEqual(call_count.stop_last_notification, 2);
 | 
						|
});
 | 
						|
 | 
						|
run_test("stream_messages", ({override, override_rewire}) => {
 | 
						|
    override(realm, "server_typing_started_wait_period_milliseconds", TYPING_STARTED_WAIT_PERIOD);
 | 
						|
    override(realm, "server_typing_stopped_wait_period_milliseconds", TYPING_STOPPED_WAIT_PERIOD);
 | 
						|
    override_rewire(typing_status, "state", null);
 | 
						|
 | 
						|
    let worker = {};
 | 
						|
    const events = {};
 | 
						|
 | 
						|
    function set_timeout(f, delay) {
 | 
						|
        assert.equal(delay, 5000);
 | 
						|
        events.idle_callback = f;
 | 
						|
        return "idle_timer_stub";
 | 
						|
    }
 | 
						|
 | 
						|
    function clear_timeout() {
 | 
						|
        events.timer_cleared = true;
 | 
						|
    }
 | 
						|
 | 
						|
    set_global("setTimeout", set_timeout);
 | 
						|
    set_global("clearTimeout", clear_timeout);
 | 
						|
 | 
						|
    function notify_server_start(recipient) {
 | 
						|
        assert.deepStrictEqual(recipient, {
 | 
						|
            message_type: "stream",
 | 
						|
            notification_event_type: "typing",
 | 
						|
            stream_id: 3,
 | 
						|
            topic: "test",
 | 
						|
        });
 | 
						|
        events.started = true;
 | 
						|
    }
 | 
						|
 | 
						|
    function notify_server_stop(recipient) {
 | 
						|
        assert.deepStrictEqual(recipient, {
 | 
						|
            message_type: "stream",
 | 
						|
            notification_event_type: "typing",
 | 
						|
            stream_id: 3,
 | 
						|
            topic: "test",
 | 
						|
        });
 | 
						|
        events.stopped = true;
 | 
						|
    }
 | 
						|
 | 
						|
    function clear_events() {
 | 
						|
        events.idle_callback = undefined;
 | 
						|
        events.started = false;
 | 
						|
        events.stopped = false;
 | 
						|
        events.timer_cleared = false;
 | 
						|
    }
 | 
						|
 | 
						|
    function call_handler(new_recipient) {
 | 
						|
        clear_events();
 | 
						|
        typing_status.update(
 | 
						|
            worker,
 | 
						|
            new_recipient,
 | 
						|
            realm.server_typing_started_wait_period_milliseconds,
 | 
						|
            realm.server_typing_stopped_wait_period_milliseconds,
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    worker = {
 | 
						|
        get_current_time: returns_time(5),
 | 
						|
        notify_server_start,
 | 
						|
        notify_server_stop,
 | 
						|
    };
 | 
						|
 | 
						|
    // Start typing stream message
 | 
						|
    call_handler({
 | 
						|
        message_type: "stream",
 | 
						|
        notification_event_type: "typing",
 | 
						|
        stream_id: 3,
 | 
						|
        topic: "test",
 | 
						|
    });
 | 
						|
    assert.deepEqual(typing_status.state, {
 | 
						|
        next_send_start_time: make_time(5 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {
 | 
						|
            message_type: "stream",
 | 
						|
            notification_event_type: "typing",
 | 
						|
            stream_id: 3,
 | 
						|
            topic: "test",
 | 
						|
        },
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: true,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: false,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    // type again 3 seconds later. Covers 'same_stream_and_topic' codepath.
 | 
						|
    worker.get_current_time = returns_time(8);
 | 
						|
    call_handler({
 | 
						|
        message_type: "stream",
 | 
						|
        notification_event_type: "typing",
 | 
						|
        stream_id: 3,
 | 
						|
        topic: "test",
 | 
						|
    });
 | 
						|
    assert.deepEqual(typing_status.state, {
 | 
						|
        next_send_start_time: make_time(5 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {
 | 
						|
            message_type: "stream",
 | 
						|
            notification_event_type: "typing",
 | 
						|
            stream_id: 3,
 | 
						|
            topic: "test",
 | 
						|
        },
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: false,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    // Explicitly stop.
 | 
						|
    call_handler(null);
 | 
						|
    assert.deepEqual(typing_status.state, null);
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: undefined,
 | 
						|
        started: false,
 | 
						|
        stopped: true,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
run_test("edit_messages", ({override_rewire}) => {
 | 
						|
    override_rewire(typing_status, "state", null);
 | 
						|
 | 
						|
    let worker = {};
 | 
						|
    const events = {};
 | 
						|
    const message_id = 7;
 | 
						|
 | 
						|
    function set_timeout(f, delay) {
 | 
						|
        assert.equal(delay, 5000);
 | 
						|
        events.idle_callback = f;
 | 
						|
        return "idle_timer_stub";
 | 
						|
    }
 | 
						|
 | 
						|
    function clear_timeout() {
 | 
						|
        events.timer_cleared = true;
 | 
						|
    }
 | 
						|
 | 
						|
    set_global("setTimeout", set_timeout);
 | 
						|
    set_global("clearTimeout", clear_timeout);
 | 
						|
 | 
						|
    function notify_server_editing_start(recipient) {
 | 
						|
        assert.deepStrictEqual(recipient, {
 | 
						|
            notification_event_type: "typing_message_edit",
 | 
						|
            message_id,
 | 
						|
        });
 | 
						|
        events.started = true;
 | 
						|
    }
 | 
						|
 | 
						|
    function notify_server_editing_stop(recipient) {
 | 
						|
        assert.deepStrictEqual(recipient, {
 | 
						|
            notification_event_type: "typing_message_edit",
 | 
						|
            message_id,
 | 
						|
        });
 | 
						|
        events.stopped = true;
 | 
						|
    }
 | 
						|
 | 
						|
    function clear_events() {
 | 
						|
        events.idle_callback = undefined;
 | 
						|
        events.started = false;
 | 
						|
        events.stopped = false;
 | 
						|
        events.timer_cleared = false;
 | 
						|
    }
 | 
						|
 | 
						|
    function call_handler_start(new_recipient) {
 | 
						|
        clear_events();
 | 
						|
        typing_status.update_editing_status(
 | 
						|
            worker,
 | 
						|
            new_recipient,
 | 
						|
            "start",
 | 
						|
            TYPING_STARTED_WAIT_PERIOD,
 | 
						|
            TYPING_STOPPED_WAIT_PERIOD,
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    function call_handler_stop(new_recipient) {
 | 
						|
        clear_events();
 | 
						|
        typing_status.update_editing_status(
 | 
						|
            worker,
 | 
						|
            new_recipient,
 | 
						|
            "stop",
 | 
						|
            TYPING_STARTED_WAIT_PERIOD,
 | 
						|
            TYPING_STOPPED_WAIT_PERIOD,
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    worker = {
 | 
						|
        get_current_time: returns_time(5),
 | 
						|
        notify_server_editing_start,
 | 
						|
        notify_server_editing_stop,
 | 
						|
    };
 | 
						|
 | 
						|
    // Start typing stream message
 | 
						|
    call_handler_start({
 | 
						|
        notification_event_type: "typing_message_edit",
 | 
						|
        message_id,
 | 
						|
    });
 | 
						|
    assert.deepEqual(typing_status.editing_state.get(message_id), {
 | 
						|
        next_send_start_time: make_time(5 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {
 | 
						|
            notification_event_type: "typing_message_edit",
 | 
						|
            message_id,
 | 
						|
        },
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: true,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: false,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    worker.get_current_time = returns_time(8);
 | 
						|
    call_handler_start({
 | 
						|
        notification_event_type: "typing_message_edit",
 | 
						|
        message_id,
 | 
						|
    });
 | 
						|
    assert.deepEqual(typing_status.editing_state.get(message_id), {
 | 
						|
        next_send_start_time: make_time(5 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {
 | 
						|
            notification_event_type: "typing_message_edit",
 | 
						|
            message_id,
 | 
						|
        },
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: false,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    worker.get_current_time = returns_time(18);
 | 
						|
    call_handler_start({
 | 
						|
        notification_event_type: "typing_message_edit",
 | 
						|
        message_id,
 | 
						|
    });
 | 
						|
    assert.deepEqual(typing_status.editing_state.get(message_id), {
 | 
						|
        next_send_start_time: make_time(18 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {
 | 
						|
            notification_event_type: "typing_message_edit",
 | 
						|
            message_id,
 | 
						|
        },
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: true,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    // Now call recipients idle callback that we captured earlier.
 | 
						|
    const callback = events.idle_callback;
 | 
						|
    clear_events();
 | 
						|
    callback();
 | 
						|
    assert.deepEqual(typing_status.editing_state.get(message_id), undefined);
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: undefined,
 | 
						|
        started: false,
 | 
						|
        stopped: true,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
 | 
						|
    // Start editing message again.
 | 
						|
    worker.get_current_time = returns_time(50);
 | 
						|
    call_handler_start({
 | 
						|
        notification_event_type: "typing_message_edit",
 | 
						|
        message_id,
 | 
						|
    });
 | 
						|
    assert.deepEqual(typing_status.editing_state.get(message_id), {
 | 
						|
        next_send_start_time: make_time(50 + 10),
 | 
						|
        idle_timer: "idle_timer_stub",
 | 
						|
        current_recipient: {
 | 
						|
            notification_event_type: "typing_message_edit",
 | 
						|
            message_id,
 | 
						|
        },
 | 
						|
    });
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: events.idle_callback,
 | 
						|
        started: true,
 | 
						|
        stopped: false,
 | 
						|
        timer_cleared: false,
 | 
						|
    });
 | 
						|
    assert.ok(events.idle_callback);
 | 
						|
 | 
						|
    // Explicitly stop.
 | 
						|
    call_handler_stop({
 | 
						|
        notification_event_type: "typing_message_edit",
 | 
						|
        message_id,
 | 
						|
    });
 | 
						|
    assert.deepEqual(typing_status.editing_state.get(message_id), undefined);
 | 
						|
    assert.deepEqual(events, {
 | 
						|
        idle_callback: undefined,
 | 
						|
        started: false,
 | 
						|
        stopped: true,
 | 
						|
        timer_cleared: true,
 | 
						|
    });
 | 
						|
});
 |