mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	hotkey: Add 'narrow to next unread followed topic' hotkey.
This commit adds a 'Shift + N' keyboard shortcut, which is used to narrow to the next unread followed topic. Fixes part of #27323.
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							6819ecee92
						
					
				
				
					commit
					8e2264b585
				
			@@ -14,7 +14,8 @@
 | 
			
		||||
 | 
			
		||||
!!! keyboard_tip ""
 | 
			
		||||
 | 
			
		||||
    Use the <kbd>N</kbd> key to go to the next unread topic, or <kbd>P</kbd>
 | 
			
		||||
    to go to the next unread direct message conversation.
 | 
			
		||||
    Use the <kbd>N</kbd> key to go to the next unread topic, or <kbd>Shift</kbd> + <kbd>N</kbd>
 | 
			
		||||
    for the next unread [followed](/help/follow-a-topic) topic, or <kbd>P</kbd> for the next
 | 
			
		||||
    unread direct message conversation.
 | 
			
		||||
 | 
			
		||||
{end_tabs}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,8 @@ in the Zulip app to add more to your repertoire as needed.
 | 
			
		||||
 | 
			
		||||
* **Next unread topic**: <kbd>N</kbd>
 | 
			
		||||
 | 
			
		||||
* **Next unread followed topic**: <kbd>Shift</kbd> + <kbd>N</kbd>
 | 
			
		||||
 | 
			
		||||
* **Next unread direct message**: <kbd>P</kbd>
 | 
			
		||||
 | 
			
		||||
* **Search messages**: <kbd>/</kbd>
 | 
			
		||||
 
 | 
			
		||||
@@ -88,6 +88,7 @@ const keydown_shift_mappings = {
 | 
			
		||||
    38: {name: "up_arrow", message_view_only: false}, // up arrow
 | 
			
		||||
    40: {name: "down_arrow", message_view_only: false}, // down arrow
 | 
			
		||||
    72: {name: "view_edit_history", message_view_only: true}, // 'H'
 | 
			
		||||
    78: {name: "narrow_to_next_unread_followed_topic", message_view_only: false}, // 'N'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const keydown_unshift_mappings = {
 | 
			
		||||
@@ -904,7 +905,10 @@ export function process_hotkey(e, hotkey) {
 | 
			
		||||
            narrow.stream_cycle_forward();
 | 
			
		||||
            return true;
 | 
			
		||||
        case "n_key":
 | 
			
		||||
            narrow.narrow_to_next_topic({trigger: "hotkey"});
 | 
			
		||||
            narrow.narrow_to_next_topic({trigger: "hotkey", only_followed_topics: false});
 | 
			
		||||
            return true;
 | 
			
		||||
        case "narrow_to_next_unread_followed_topic":
 | 
			
		||||
            narrow.narrow_to_next_topic({trigger: "hotkey", only_followed_topics: true});
 | 
			
		||||
            return true;
 | 
			
		||||
        case "p_key":
 | 
			
		||||
            narrow.narrow_to_next_pm_string({trigger: "hotkey"});
 | 
			
		||||
 
 | 
			
		||||
@@ -770,7 +770,11 @@ export function narrow_to_next_topic(opts = {}) {
 | 
			
		||||
        topic: narrow_state.topic(),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const next_narrow = topic_generator.get_next_topic(curr_info.stream, curr_info.topic);
 | 
			
		||||
    const next_narrow = topic_generator.get_next_topic(
 | 
			
		||||
        curr_info.stream,
 | 
			
		||||
        curr_info.topic,
 | 
			
		||||
        opts.only_followed_topics,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (!next_narrow) {
 | 
			
		||||
        return;
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ export function next_topic(streams, get_topics, has_unread_messages, curr_stream
 | 
			
		||||
    return undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function get_next_topic(curr_stream, curr_topic) {
 | 
			
		||||
export function get_next_topic(curr_stream, curr_topic, only_followed_topics) {
 | 
			
		||||
    let my_streams = stream_list_sort.get_streams();
 | 
			
		||||
 | 
			
		||||
    my_streams = my_streams.filter((stream_name) => {
 | 
			
		||||
@@ -71,11 +71,28 @@ export function get_next_topic(curr_stream, curr_topic) {
 | 
			
		||||
        return topics;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function get_followed_topics(stream_name) {
 | 
			
		||||
        const stream_id = stream_data.get_stream_id(stream_name);
 | 
			
		||||
        let topics = stream_topic_history.get_recent_topic_names(stream_id);
 | 
			
		||||
        topics = topics.filter((topic) => user_topics.is_topic_followed(stream_id, topic));
 | 
			
		||||
        return topics;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function has_unread_messages(stream_name, topic) {
 | 
			
		||||
        const stream_id = stream_data.get_stream_id(stream_name);
 | 
			
		||||
        return unread.topic_has_any_unread(stream_id, topic);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (only_followed_topics) {
 | 
			
		||||
        return next_topic(
 | 
			
		||||
            my_streams,
 | 
			
		||||
            get_followed_topics,
 | 
			
		||||
            has_unread_messages,
 | 
			
		||||
            curr_stream,
 | 
			
		||||
            curr_topic,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return next_topic(my_streams, get_unmuted_topics, has_unread_messages, curr_stream, curr_topic);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,10 @@
 | 
			
		||||
                    <td class="definition">{{t 'Next unread topic' }}</td>
 | 
			
		||||
                    <td><span class="hotkey"><kbd>N</kbd></span></td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td class="definition">{{t 'Next unread followed topic' }}</td>
 | 
			
		||||
                    <td><span class="hotkey"><kbd>Shift</kbd> + <kbd>N</kbd></span></td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td class="definition">{{t 'Next unread direct message' }}</td>
 | 
			
		||||
                    <td><span class="hotkey"><kbd>P</kbd></span></td>
 | 
			
		||||
 
 | 
			
		||||
@@ -182,6 +182,7 @@ run_test("mappings", () => {
 | 
			
		||||
    assert.equal(map_down(13).name, "enter");
 | 
			
		||||
    assert.equal(map_down(46).name, "delete");
 | 
			
		||||
    assert.equal(map_down(13, true).name, "enter");
 | 
			
		||||
    assert.equal(map_down(78, true).name, "narrow_to_next_unread_followed_topic");
 | 
			
		||||
 | 
			
		||||
    assert.equal(map_press(47).name, "search"); // slash
 | 
			
		||||
    assert.equal(map_press(106).name, "vim_down"); // j
 | 
			
		||||
@@ -231,11 +232,15 @@ run_test("mappings", () => {
 | 
			
		||||
    navigator.platform = "";
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function process(s) {
 | 
			
		||||
function process(s, shiftKey, keydown = false) {
 | 
			
		||||
    const e = {
 | 
			
		||||
        which: s.codePointAt(0),
 | 
			
		||||
        shiftKey,
 | 
			
		||||
    };
 | 
			
		||||
    try {
 | 
			
		||||
        if (keydown) {
 | 
			
		||||
            return hotkey.process_keydown(e);
 | 
			
		||||
        }
 | 
			
		||||
        return hotkey.process_keypress(e);
 | 
			
		||||
    } catch (error) /* istanbul ignore next */ {
 | 
			
		||||
        // An exception will be thrown here if a different
 | 
			
		||||
@@ -247,9 +252,9 @@ function process(s) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function assert_mapping(c, module, func_name, shiftKey) {
 | 
			
		||||
function assert_mapping(c, module, func_name, shiftKey, keydown) {
 | 
			
		||||
    stubbing(module, func_name, (stub) => {
 | 
			
		||||
        assert.ok(process(c, shiftKey));
 | 
			
		||||
        assert.ok(process(c, shiftKey, keydown));
 | 
			
		||||
        assert.equal(stub.num_calls, 1);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
@@ -442,6 +447,10 @@ run_test("n/p keys", () => {
 | 
			
		||||
    assert_mapping("n", narrow, "narrow_to_next_topic");
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
run_test("narrow next unread followed topic", () => {
 | 
			
		||||
    assert_mapping("N", narrow, "narrow_to_next_topic", true, true);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
run_test("motion_keys", () => {
 | 
			
		||||
    const codes = {
 | 
			
		||||
        down_arrow: 40,
 | 
			
		||||
 
 | 
			
		||||
@@ -89,9 +89,9 @@ run_test("topics", ({override}) => {
 | 
			
		||||
    override(stream_topic_history, "get_recent_topic_names", (stream_id) => {
 | 
			
		||||
        switch (stream_id) {
 | 
			
		||||
            case muted_stream_id:
 | 
			
		||||
                return ["ms-topic1", "ms-topic2"];
 | 
			
		||||
                return ["ms-topic1", "ms-topic2", "followed"];
 | 
			
		||||
            case devel_stream_id:
 | 
			
		||||
                return ["muted", "python"];
 | 
			
		||||
                return ["muted", "python", "followed"];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [];
 | 
			
		||||
@@ -107,12 +107,26 @@ run_test("topics", ({override}) => {
 | 
			
		||||
 | 
			
		||||
    override(user_topics, "is_topic_muted", (_stream_name, topic) => topic === "muted");
 | 
			
		||||
 | 
			
		||||
    override(user_topics, "is_topic_followed", (_stream_name, topic) => topic === "followed");
 | 
			
		||||
 | 
			
		||||
    let next_item = tg.get_next_topic("announce", "whatever");
 | 
			
		||||
    assert.deepEqual(next_item, {
 | 
			
		||||
        stream: "devel",
 | 
			
		||||
        topic: "python",
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    next_item = tg.get_next_topic("devel", "python");
 | 
			
		||||
    assert.deepEqual(next_item, {
 | 
			
		||||
        stream: "devel",
 | 
			
		||||
        topic: "followed",
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    next_item = tg.get_next_topic("muted", "whatever", true);
 | 
			
		||||
    assert.deepEqual(next_item, {
 | 
			
		||||
        stream: "muted",
 | 
			
		||||
        topic: "followed",
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    next_item = tg.get_next_topic("muted", undefined);
 | 
			
		||||
    assert.deepEqual(next_item, {
 | 
			
		||||
        stream: "muted",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user