mirror of https://github.com/zulip/zulip.git
inbox: Add new narrow.
This commit is contained in:
parent
3d7b9e622d
commit
6ef0753a51
|
@ -237,6 +237,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="inbox-view">
|
||||||
|
<div class="inbox-container">
|
||||||
|
<div id="inbox-pane"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="message_feed_container">
|
<div id="message_feed_container">
|
||||||
<div class="message-feed" id="main_div">
|
<div class="message-feed" id="main_div">
|
||||||
<div class="top-messages-logo">
|
<div class="top-messages-logo">
|
||||||
|
|
|
@ -98,6 +98,8 @@ EXEMPT_FILES = make_set(
|
||||||
"web/src/hbs.d.ts",
|
"web/src/hbs.d.ts",
|
||||||
"web/src/hotkey.js",
|
"web/src/hotkey.js",
|
||||||
"web/src/hotspots.js",
|
"web/src/hotspots.js",
|
||||||
|
"web/src/inbox_ui.js",
|
||||||
|
"web/src/inbox_util.js",
|
||||||
"web/src/info_overlay.js",
|
"web/src/info_overlay.js",
|
||||||
"web/src/invite.js",
|
"web/src/invite.js",
|
||||||
"web/src/lightbox.js",
|
"web/src/lightbox.js",
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 178 B |
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -58,6 +58,7 @@ import "../../styles/dark_theme.css";
|
||||||
import "../../styles/user_status.css";
|
import "../../styles/user_status.css";
|
||||||
import "../../styles/widgets.css";
|
import "../../styles/widgets.css";
|
||||||
import "../../styles/print.css";
|
import "../../styles/print.css";
|
||||||
|
import "../../styles/inbox.css";
|
||||||
|
|
||||||
// This should be last.
|
// This should be last.
|
||||||
import "../ui_init";
|
import "../ui_init";
|
||||||
|
|
|
@ -7,6 +7,8 @@ import * as browser_history from "./browser_history";
|
||||||
import * as drafts from "./drafts";
|
import * as drafts from "./drafts";
|
||||||
import * as hash_util from "./hash_util";
|
import * as hash_util from "./hash_util";
|
||||||
import {$t_html} from "./i18n";
|
import {$t_html} from "./i18n";
|
||||||
|
import * as inbox_ui from "./inbox_ui";
|
||||||
|
import * as inbox_util from "./inbox_util";
|
||||||
import * as info_overlay from "./info_overlay";
|
import * as info_overlay from "./info_overlay";
|
||||||
import * as left_sidebar_navigation_area from "./left_sidebar_navigation_area";
|
import * as left_sidebar_navigation_area from "./left_sidebar_navigation_area";
|
||||||
import * as message_lists from "./message_lists";
|
import * as message_lists from "./message_lists";
|
||||||
|
@ -91,6 +93,14 @@ function maybe_hide_recent_view() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function maybe_hide_inbox() {
|
||||||
|
if (inbox_util.is_visible()) {
|
||||||
|
inbox_ui.hide();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
export function changehash(newhash) {
|
export function changehash(newhash) {
|
||||||
if (browser_history.state.changing_hash) {
|
if (browser_history.state.changing_hash) {
|
||||||
return;
|
return;
|
||||||
|
@ -109,8 +119,9 @@ export function save_narrow(operators) {
|
||||||
|
|
||||||
function show_all_message_view() {
|
function show_all_message_view() {
|
||||||
const coming_from_recent_view = maybe_hide_recent_view();
|
const coming_from_recent_view = maybe_hide_recent_view();
|
||||||
|
const coming_from_inbox = maybe_hide_inbox();
|
||||||
const is_actively_scrolling = message_scroll.is_actively_scrolling();
|
const is_actively_scrolling = message_scroll.is_actively_scrolling();
|
||||||
narrow.deactivate(!coming_from_recent_view, is_actively_scrolling);
|
narrow.deactivate(!(coming_from_recent_view || coming_from_inbox), is_actively_scrolling);
|
||||||
left_sidebar_navigation_area.handle_narrow_deactivated();
|
left_sidebar_navigation_area.handle_narrow_deactivated();
|
||||||
// We need to maybe scroll to the selected message
|
// We need to maybe scroll to the selected message
|
||||||
// once we have the proper viewport set up
|
// once we have the proper viewport set up
|
||||||
|
@ -167,6 +178,7 @@ function do_hashchange_normal(from_reload) {
|
||||||
switch (hash[0]) {
|
switch (hash[0]) {
|
||||||
case "#narrow": {
|
case "#narrow": {
|
||||||
maybe_hide_recent_view();
|
maybe_hide_recent_view();
|
||||||
|
maybe_hide_inbox();
|
||||||
let operators;
|
let operators;
|
||||||
try {
|
try {
|
||||||
// TODO: Show possible valid URLs to the user.
|
// TODO: Show possible valid URLs to the user.
|
||||||
|
@ -225,8 +237,13 @@ function do_hashchange_normal(from_reload) {
|
||||||
window.location.replace("#recent");
|
window.location.replace("#recent");
|
||||||
break;
|
break;
|
||||||
case "#recent":
|
case "#recent":
|
||||||
|
maybe_hide_inbox();
|
||||||
recent_view_ui.show();
|
recent_view_ui.show();
|
||||||
break;
|
break;
|
||||||
|
case "#inbox":
|
||||||
|
maybe_hide_recent_view();
|
||||||
|
inbox_ui.show();
|
||||||
|
break;
|
||||||
case "#all_messages":
|
case "#all_messages":
|
||||||
show_all_message_view();
|
show_all_message_view();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -21,6 +21,8 @@ import * as giphy from "./giphy";
|
||||||
import * as hash_util from "./hash_util";
|
import * as hash_util from "./hash_util";
|
||||||
import * as hashchange from "./hashchange";
|
import * as hashchange from "./hashchange";
|
||||||
import * as hotspots from "./hotspots";
|
import * as hotspots from "./hotspots";
|
||||||
|
import * as inbox_ui from "./inbox_ui";
|
||||||
|
import * as inbox_util from "./inbox_util";
|
||||||
import * as lightbox from "./lightbox";
|
import * as lightbox from "./lightbox";
|
||||||
import * as list_util from "./list_util";
|
import * as list_util from "./list_util";
|
||||||
import * as message_edit from "./message_edit";
|
import * as message_edit from "./message_edit";
|
||||||
|
@ -147,6 +149,7 @@ const keypress_mappings = {
|
||||||
82: {name: "respond_to_author", message_view_only: true}, // 'R'
|
82: {name: "respond_to_author", message_view_only: true}, // 'R'
|
||||||
83: {name: "toggle_stream_subscription", message_view_only: true}, // 'S'
|
83: {name: "toggle_stream_subscription", message_view_only: true}, // 'S'
|
||||||
85: {name: "mark_unread", message_view_only: true}, // 'U'
|
85: {name: "mark_unread", message_view_only: true}, // 'U'
|
||||||
|
84: {name: "open_inbox", message_view_only: true}, // 'T'
|
||||||
86: {name: "view_selected_stream", message_view_only: false}, // 'V'
|
86: {name: "view_selected_stream", message_view_only: false}, // 'V'
|
||||||
97: {name: "all_messages", message_view_only: true}, // 'a'
|
97: {name: "all_messages", message_view_only: true}, // 'a'
|
||||||
99: {name: "compose", message_view_only: true}, // 'c'
|
99: {name: "compose", message_view_only: true}, // 'c'
|
||||||
|
@ -251,6 +254,10 @@ export function process_escape_key(e) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inbox_util.is_in_focus() && inbox_ui.change_focused_element($(e.target), "escape")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (feedback_widget.is_open()) {
|
if (feedback_widget.is_open()) {
|
||||||
feedback_widget.dismiss();
|
feedback_widget.dismiss();
|
||||||
return true;
|
return true;
|
||||||
|
@ -637,6 +644,19 @@ export function process_hotkey(e, hotkey) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (event_name) {
|
||||||
|
case "up_arrow":
|
||||||
|
case "down_arrow":
|
||||||
|
case "left_arrow":
|
||||||
|
case "right_arrow":
|
||||||
|
case "tab":
|
||||||
|
case "shift_tab":
|
||||||
|
case "escape":
|
||||||
|
if (inbox_util.is_in_focus()) {
|
||||||
|
return inbox_ui.change_focused_element(event_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We handle the most complex keys in their own functions.
|
// We handle the most complex keys in their own functions.
|
||||||
switch (event_name) {
|
switch (event_name) {
|
||||||
case "escape":
|
case "escape":
|
||||||
|
@ -881,6 +901,9 @@ export function process_hotkey(e, hotkey) {
|
||||||
case "open_recent_view":
|
case "open_recent_view":
|
||||||
browser_history.go_to_location("#recent");
|
browser_history.go_to_location("#recent");
|
||||||
return true;
|
return true;
|
||||||
|
case "open_inbox":
|
||||||
|
browser_history.go_to_location("#inbox");
|
||||||
|
return true;
|
||||||
case "all_messages":
|
case "all_messages":
|
||||||
browser_history.go_to_location("#all_messages");
|
browser_history.go_to_location("#all_messages");
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -0,0 +1,878 @@
|
||||||
|
import $ from "jquery";
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
import render_inbox_row from "../templates/inbox_view/inbox_row.hbs";
|
||||||
|
import render_inbox_stream_container from "../templates/inbox_view/inbox_stream_container.hbs";
|
||||||
|
import render_inbox_view from "../templates/inbox_view/inbox_view.hbs";
|
||||||
|
|
||||||
|
import * as buddy_data from "./buddy_data";
|
||||||
|
import * as compose_closed_ui from "./compose_closed_ui";
|
||||||
|
import * as hash_util from "./hash_util";
|
||||||
|
import * as hashchange from "./hashchange";
|
||||||
|
import {is_visible, set_visible} from "./inbox_util";
|
||||||
|
import * as keydown_util from "./keydown_util";
|
||||||
|
import * as left_sidebar_navigation_area from "./left_sidebar_navigation_area";
|
||||||
|
import {localstorage} from "./localstorage";
|
||||||
|
import * as message_store from "./message_store";
|
||||||
|
import * as message_view_header from "./message_view_header";
|
||||||
|
import * as narrow from "./narrow";
|
||||||
|
import * as narrow_state from "./narrow_state";
|
||||||
|
import * as navigate from "./navigate";
|
||||||
|
import * as people from "./people";
|
||||||
|
import * as pm_list from "./pm_list";
|
||||||
|
import * as search from "./search";
|
||||||
|
import * as stream_color from "./stream_color";
|
||||||
|
import * as stream_data from "./stream_data";
|
||||||
|
import * as stream_list from "./stream_list";
|
||||||
|
import * as sub_store from "./sub_store";
|
||||||
|
import * as unread from "./unread";
|
||||||
|
import * as unread_ops from "./unread_ops";
|
||||||
|
import * as unread_ui from "./unread_ui";
|
||||||
|
import * as user_topics from "./user_topics";
|
||||||
|
import * as util from "./util";
|
||||||
|
|
||||||
|
let dms_dict = {};
|
||||||
|
let topics_dict = {};
|
||||||
|
let streams_dict = {};
|
||||||
|
let update_triggered_by_user = false;
|
||||||
|
|
||||||
|
const COLUMNS = {
|
||||||
|
COLLAPSE_BUTTON: 0,
|
||||||
|
RECIPIENT: 1,
|
||||||
|
UNREAD_COUNT: 2,
|
||||||
|
};
|
||||||
|
let col_focus = COLUMNS.RECIPIENT;
|
||||||
|
let row_focus = 0;
|
||||||
|
|
||||||
|
const ls_key = "inbox_filters";
|
||||||
|
const ls = localstorage();
|
||||||
|
let filters = new Set();
|
||||||
|
|
||||||
|
let search_keyword = "";
|
||||||
|
const INBOX_SEARCH_ID = "inbox-search";
|
||||||
|
const MUTED_FILTER_ID = "include_muted";
|
||||||
|
export let current_focus_id = INBOX_SEARCH_ID;
|
||||||
|
|
||||||
|
const STREAM_HEADER_PREFIX = "inbox-stream-header-";
|
||||||
|
const CONVERSATION_ID_PREFIX = "inbox-row-conversation-";
|
||||||
|
|
||||||
|
function get_row_from_conversation_key(key) {
|
||||||
|
return $(`#${CONVERSATION_ID_PREFIX}` + CSS.escape(`${key}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
function save_filter() {
|
||||||
|
ls.set(ls_key, [...filters]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function should_include_muted() {
|
||||||
|
return filters.has(MUTED_FILTER_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function show() {
|
||||||
|
// hashchange library is expected to hide other views.
|
||||||
|
if (narrow.has_shown_message_list_view) {
|
||||||
|
narrow.save_pre_narrow_offset_for_reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_visible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
left_sidebar_navigation_area.highlight_inbox_view();
|
||||||
|
stream_list.handle_narrow_deactivated();
|
||||||
|
|
||||||
|
$("#message_feed_container").hide();
|
||||||
|
$("#inbox-view").show();
|
||||||
|
set_visible(true);
|
||||||
|
|
||||||
|
unread_ui.hide_unread_banner();
|
||||||
|
narrow_state.reset_current_filter();
|
||||||
|
message_view_header.render_title_area();
|
||||||
|
narrow.handle_middle_pane_transition();
|
||||||
|
|
||||||
|
narrow_state.reset_current_filter();
|
||||||
|
narrow.update_narrow_title(narrow_state.filter());
|
||||||
|
message_view_header.render_title_area();
|
||||||
|
pm_list.handle_narrow_deactivated();
|
||||||
|
search.clear_search_form();
|
||||||
|
complete_rerender();
|
||||||
|
compose_closed_ui.set_standard_text_for_reply_button();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hide() {
|
||||||
|
const $focused_element = $(document.activeElement);
|
||||||
|
|
||||||
|
if ($("#inbox-view").has($focused_element)) {
|
||||||
|
$focused_element.trigger("blur");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#message_feed_container").show();
|
||||||
|
$("#inbox-view").hide();
|
||||||
|
set_visible(false);
|
||||||
|
|
||||||
|
// This solves a bug with message_view_header
|
||||||
|
// being broken sometimes when we narrow
|
||||||
|
// to a filter and back to recent topics
|
||||||
|
// before it completely re-rerenders.
|
||||||
|
message_view_header.render_title_area();
|
||||||
|
|
||||||
|
// Fire our custom event
|
||||||
|
$("#message_feed_container").trigger("message_feed_shown");
|
||||||
|
|
||||||
|
// This makes sure user lands on the selected message
|
||||||
|
// and not always at the top of the narrow.
|
||||||
|
navigate.plan_scroll_to_selected();
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_topic_key(stream_id, topic) {
|
||||||
|
return stream_id + ":" + topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_stream_key(stream_id) {
|
||||||
|
return "stream_" + stream_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_stream_container(stream_key) {
|
||||||
|
return $(`#${CSS.escape(stream_key)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_topics_container(stream_id) {
|
||||||
|
const $topics_container = get_stream_header_row(stream_id)
|
||||||
|
.next(".inbox-topic-container")
|
||||||
|
.expectOne();
|
||||||
|
return $topics_container;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_stream_header_row(stream_id) {
|
||||||
|
const $stream_header_row = $(`#${CSS.escape(STREAM_HEADER_PREFIX + stream_id)}`);
|
||||||
|
return $stream_header_row;
|
||||||
|
}
|
||||||
|
|
||||||
|
function load_filter() {
|
||||||
|
filters = new Set(ls.get(ls_key));
|
||||||
|
update_filters();
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_filters() {
|
||||||
|
const $mute_checkbox = $("#inbox-filters #inbox_filter_mute_toggle");
|
||||||
|
const $mute_filter = $("#inbox-filters .btn-inbox-filter");
|
||||||
|
if (should_include_muted()) {
|
||||||
|
$mute_checkbox.removeClass("fa-square-o");
|
||||||
|
$mute_checkbox.addClass("fa-check-square-o");
|
||||||
|
$mute_filter.addClass("btn-inbox-selected");
|
||||||
|
} else {
|
||||||
|
$mute_checkbox.removeClass("fa-check-square-o");
|
||||||
|
$mute_checkbox.addClass("fa-square-o");
|
||||||
|
$mute_filter.removeClass("btn-inbox-selected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggle_muted_filter() {
|
||||||
|
const $mute_filter = $("#inbox-filters .btn-inbox-filter");
|
||||||
|
if ($mute_filter.hasClass("btn-inbox-selected")) {
|
||||||
|
filters.delete(MUTED_FILTER_ID);
|
||||||
|
} else {
|
||||||
|
filters.add(MUTED_FILTER_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
update_filters();
|
||||||
|
save_filter();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function format_dm(user_ids_string, unread_count) {
|
||||||
|
const recipient_ids = people.user_ids_string_to_ids_array(user_ids_string);
|
||||||
|
if (!recipient_ids.length) {
|
||||||
|
// Self DM
|
||||||
|
recipient_ids.push(people.my_current_user_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
const reply_to = people.user_ids_string_to_emails_string(user_ids_string);
|
||||||
|
const recipients_info = [];
|
||||||
|
|
||||||
|
for (const user_id of recipient_ids) {
|
||||||
|
const recipient_user_obj = people.get_by_user_id(user_id);
|
||||||
|
if (!recipient_user_obj.is_bot) {
|
||||||
|
const user_circle_class = buddy_data.get_user_circle_class(user_id);
|
||||||
|
recipient_user_obj.user_circle_class = user_circle_class;
|
||||||
|
}
|
||||||
|
recipients_info.push(recipient_user_obj);
|
||||||
|
}
|
||||||
|
const context = {
|
||||||
|
conversation_key: user_ids_string,
|
||||||
|
is_direct: true,
|
||||||
|
recipients_info,
|
||||||
|
dm_url: hash_util.pm_with_url(reply_to),
|
||||||
|
user_ids_string,
|
||||||
|
unread_count,
|
||||||
|
is_hidden: filter_should_hide_row({dm_key: user_ids_string}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rerender_dm_inbox_row_if_needed(new_dm_data, old_dm_data) {
|
||||||
|
if (old_dm_data === undefined) {
|
||||||
|
// This row is not rendered yet.
|
||||||
|
$("#inbox-direct-messages-container").append(render_inbox_row(new_dm_data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const property in new_dm_data) {
|
||||||
|
if (new_dm_data[property] !== old_dm_data[property]) {
|
||||||
|
const $rendered_row = get_row_from_conversation_key(new_dm_data.conversation_key);
|
||||||
|
$rendered_row.replaceWith(render_inbox_row(new_dm_data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function format_stream(stream_id, unread_count_info) {
|
||||||
|
const stream_info = sub_store.get(stream_id);
|
||||||
|
let unread_count = unread_count_info.unmuted_count;
|
||||||
|
if (should_include_muted()) {
|
||||||
|
unread_count += unread_count_info.muted_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
is_stream: true,
|
||||||
|
invite_only: stream_info.invite_only,
|
||||||
|
is_web_public: stream_info.is_web_public,
|
||||||
|
stream_name: stream_info.name,
|
||||||
|
stream_color: stream_color.get_stream_privacy_icon_color(stream_info.color),
|
||||||
|
stream_header_color: stream_color.get_recipient_bar_color(stream_info.color),
|
||||||
|
unread_count,
|
||||||
|
stream_url: hash_util.by_stream_url(stream_id),
|
||||||
|
stream_id,
|
||||||
|
// Will be displayed if any topic is visible.
|
||||||
|
is_hidden: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function rerender_stream_inbox_header_if_needed(new_stream_data, old_stream_data) {
|
||||||
|
for (const property in new_stream_data) {
|
||||||
|
if (new_stream_data[property] !== old_stream_data[property]) {
|
||||||
|
const $rendered_row = get_stream_header_row(new_stream_data.stream_id);
|
||||||
|
$rendered_row.replaceWith(render_inbox_row(new_stream_data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function format_topic(stream_id, topic, topic_unread_count) {
|
||||||
|
const context = {
|
||||||
|
is_topic: true,
|
||||||
|
stream_id,
|
||||||
|
topic_name: topic,
|
||||||
|
unread_count: topic_unread_count,
|
||||||
|
conversation_key: get_topic_key(stream_id, topic),
|
||||||
|
topic_url: hash_util.by_stream_topic_url(stream_id, topic),
|
||||||
|
is_hidden: filter_should_hide_row({stream_id, topic}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
function insert_stream(stream_id, topic_dict, stream_unread) {
|
||||||
|
const stream_data = format_stream(stream_id, stream_unread);
|
||||||
|
const stream_key = get_stream_key(stream_id);
|
||||||
|
topics_dict[stream_key] = {};
|
||||||
|
for (const [topic, topic_unread_count] of topic_dict) {
|
||||||
|
const topic_key = get_topic_key(stream_id, topic);
|
||||||
|
if (topic_unread_count) {
|
||||||
|
const topic_data = format_topic(stream_id, topic, topic_unread_count);
|
||||||
|
topics_dict[stream_key][topic_key] = topic_data;
|
||||||
|
if (!topic_data.is_hidden) {
|
||||||
|
stream_data.is_hidden = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
streams_dict[stream_key] = stream_data;
|
||||||
|
|
||||||
|
const sorted_stream_keys = get_sorted_stream_keys();
|
||||||
|
const stream_index = sorted_stream_keys.indexOf(stream_key);
|
||||||
|
const rendered_stream = render_inbox_stream_container({
|
||||||
|
topics_dict: {
|
||||||
|
[stream_key]: topics_dict[stream_key],
|
||||||
|
},
|
||||||
|
streams_dict,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (stream_index === 0) {
|
||||||
|
$("#inbox-streams-container").prepend(rendered_stream);
|
||||||
|
} else {
|
||||||
|
const previous_stream_key = sorted_stream_keys[stream_index - 1];
|
||||||
|
$(rendered_stream).insertAfter(get_stream_container(previous_stream_key));
|
||||||
|
}
|
||||||
|
return get_stream_container(stream_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rerender_topic_inbox_row_if_needed(new_topic_data, old_topic_data) {
|
||||||
|
// This row is not rendered yet.
|
||||||
|
if (old_topic_data === undefined) {
|
||||||
|
const stream_key = get_stream_key(new_topic_data.stream_id);
|
||||||
|
const $topic_container = get_stream_container(stream_key).find(".inbox-topic-container");
|
||||||
|
$topic_container.prepend(render_inbox_row(new_topic_data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const property in new_topic_data) {
|
||||||
|
if (new_topic_data[property] !== old_topic_data[property]) {
|
||||||
|
const $rendered_row = get_row_from_conversation_key(new_topic_data.conversation_key);
|
||||||
|
$rendered_row.replaceWith(render_inbox_row(new_topic_data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_sorted_stream_keys() {
|
||||||
|
function compare_function(a, b) {
|
||||||
|
const stream_a = streams_dict[a];
|
||||||
|
const stream_b = streams_dict[b];
|
||||||
|
const stream_name_a = stream_a ? stream_a.stream_name : "";
|
||||||
|
const stream_name_b = stream_b ? stream_b.stream_name : "";
|
||||||
|
return util.strcmp(stream_name_a, stream_name_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(topics_dict).sort(compare_function);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_sorted_stream_topic_dict() {
|
||||||
|
const sorted_stream_keys = get_sorted_stream_keys();
|
||||||
|
const sorted_topic_dict = {};
|
||||||
|
for (const sorted_stream_key of sorted_stream_keys) {
|
||||||
|
sorted_topic_dict[sorted_stream_key] = topics_dict[sorted_stream_key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted_topic_dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset_data() {
|
||||||
|
dms_dict = {};
|
||||||
|
topics_dict = {};
|
||||||
|
streams_dict = {};
|
||||||
|
|
||||||
|
const unread_dms = unread.get_unread_pm();
|
||||||
|
const unread_dms_count = unread_dms.total_count;
|
||||||
|
const unread_dms_dict = unread_dms.pm_dict;
|
||||||
|
|
||||||
|
const unread_stream_message = unread.get_unread_topics();
|
||||||
|
const unread_stream_msg_count = unread_stream_message.stream_unread_messages;
|
||||||
|
const unread_streams_dict = unread_stream_message.stream_count;
|
||||||
|
|
||||||
|
let has_dms_post_filter = false;
|
||||||
|
if (unread_dms_count) {
|
||||||
|
for (const [key, value] of unread_dms_dict) {
|
||||||
|
if (value) {
|
||||||
|
const dm_data = format_dm(key, value);
|
||||||
|
dms_dict[key] = dm_data;
|
||||||
|
if (!dm_data.is_hidden) {
|
||||||
|
has_dms_post_filter = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const has_unread = unread_dms_count + unread_stream_msg_count > 0;
|
||||||
|
|
||||||
|
if (unread_stream_msg_count) {
|
||||||
|
for (const [stream_id, topic_dict] of unread_streams_dict) {
|
||||||
|
const stream_unread = unread.num_unread_for_stream(stream_id);
|
||||||
|
const stream_unread_count = stream_unread.unmuted_count + stream_unread.muted_count;
|
||||||
|
const stream_key = get_stream_key(stream_id);
|
||||||
|
if (stream_unread_count > 0) {
|
||||||
|
topics_dict[stream_key] = {};
|
||||||
|
const stream_data = format_stream(stream_id, stream_unread);
|
||||||
|
for (const [topic, topic_unread_count] of topic_dict) {
|
||||||
|
if (topic_unread_count) {
|
||||||
|
const topic_key = get_topic_key(stream_id, topic);
|
||||||
|
const topic_data = format_topic(stream_id, topic, topic_unread_count);
|
||||||
|
topics_dict[stream_key][topic_key] = topic_data;
|
||||||
|
if (!topic_data.is_hidden) {
|
||||||
|
stream_data.is_hidden = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
streams_dict[stream_key] = stream_data;
|
||||||
|
} else {
|
||||||
|
delete topics_dict[stream_key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
topics_dict = get_sorted_stream_topic_dict();
|
||||||
|
|
||||||
|
return {
|
||||||
|
unread_dms_count,
|
||||||
|
has_dms_post_filter,
|
||||||
|
has_unread,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function complete_rerender() {
|
||||||
|
if (!is_visible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
load_filter();
|
||||||
|
const additional_context = reset_data();
|
||||||
|
$("#inbox-pane").html(
|
||||||
|
render_inbox_view({
|
||||||
|
search_val: $("#inbox_search").val() || "",
|
||||||
|
include_muted: should_include_muted(),
|
||||||
|
INBOX_SEARCH_ID,
|
||||||
|
MUTED_FILTER_ID,
|
||||||
|
dms_dict,
|
||||||
|
topics_dict,
|
||||||
|
streams_dict,
|
||||||
|
...additional_context,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
update_filters();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// We don't want to focus on simplebar ever.
|
||||||
|
$("#inbox-list .simplebar-content-wrapper").attr("tabindex", "-1");
|
||||||
|
revive_current_focus();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function search_and_update() {
|
||||||
|
search_keyword = $("#inbox-search").val() || "";
|
||||||
|
current_focus_id = INBOX_SEARCH_ID;
|
||||||
|
update_triggered_by_user = true;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function row_in_search_results(keyword, text) {
|
||||||
|
if (keyword === "") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const search_words = keyword.toLowerCase().split(/\s+/);
|
||||||
|
return search_words.every((word) => text.includes(word));
|
||||||
|
}
|
||||||
|
|
||||||
|
function filter_should_hide_row({stream_id, topic, dm_key}) {
|
||||||
|
let text;
|
||||||
|
if (dm_key !== undefined) {
|
||||||
|
const recipients_string = people.get_recipients(dm_key);
|
||||||
|
text = recipients_string.toLowerCase();
|
||||||
|
} else {
|
||||||
|
const sub = sub_store.get(stream_id);
|
||||||
|
if (sub === undefined || !sub.subscribed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!should_include_muted() &&
|
||||||
|
(stream_data.is_muted(stream_id) || user_topics.is_topic_muted(stream_id, topic))
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
text = (sub.name + " " + topic).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!row_in_search_results(search_keyword, text)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function collapse_or_expand(container_id) {
|
||||||
|
let $toggle_icon;
|
||||||
|
let $container;
|
||||||
|
if (container_id === "inbox-dm-header") {
|
||||||
|
$container = $(`#inbox-direct-messages-container`);
|
||||||
|
$container.children().toggleClass("collapsed_container");
|
||||||
|
$toggle_icon = $("#inbox-dm-header .toggle-inbox-header-icon");
|
||||||
|
} else {
|
||||||
|
const stream_id = container_id.slice(STREAM_HEADER_PREFIX.length);
|
||||||
|
$container = get_topics_container(stream_id);
|
||||||
|
$container.children().toggleClass("collapsed_container");
|
||||||
|
$toggle_icon = $(
|
||||||
|
`#${CSS.escape(STREAM_HEADER_PREFIX + stream_id)} .toggle-inbox-header-icon`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$toggle_icon.toggleClass("icon_collapsed_state");
|
||||||
|
}
|
||||||
|
|
||||||
|
function focus_current_id() {
|
||||||
|
$(`#${current_focus_id}`).trigger("focus");
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_default_focus() {
|
||||||
|
current_focus_id = INBOX_SEARCH_ID;
|
||||||
|
focus_current_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_list_focused() {
|
||||||
|
return ![INBOX_SEARCH_ID, MUTED_FILTER_ID].includes(current_focus_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_all_rows() {
|
||||||
|
return $(".inbox-header, .inbox-row").not(".hidden_by_filters, .collapsed_container");
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_row_index($elt) {
|
||||||
|
const $all_rows = get_all_rows();
|
||||||
|
const $row = $elt.closest(".inbox-row, .inbox-header");
|
||||||
|
return $all_rows.index($row);
|
||||||
|
}
|
||||||
|
|
||||||
|
function focus_clicked_element($elt) {
|
||||||
|
row_focus = get_row_index($elt);
|
||||||
|
update_triggered_by_user = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function revive_current_focus() {
|
||||||
|
if (is_list_focused()) {
|
||||||
|
set_list_focus();
|
||||||
|
} else {
|
||||||
|
focus_current_id();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_row_a_header($row) {
|
||||||
|
return $row.hasClass("inbox-header");
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_list_focus(input_key) {
|
||||||
|
const $all_rows = get_all_rows();
|
||||||
|
const max_row_focus = $all_rows.length - 1;
|
||||||
|
if (max_row_focus < 0) {
|
||||||
|
set_default_focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row_focus > max_row_focus) {
|
||||||
|
row_focus = max_row_focus;
|
||||||
|
} else if (row_focus < 0) {
|
||||||
|
row_focus = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const $row_to_focus = $($all_rows.get(row_focus));
|
||||||
|
current_focus_id = $row_to_focus.attr("id");
|
||||||
|
const not_a_header_row = !is_row_a_header($row_to_focus);
|
||||||
|
|
||||||
|
if (col_focus > COLUMNS.UNREAD_COUNT) {
|
||||||
|
col_focus = COLUMNS.COLLAPSE_BUTTON;
|
||||||
|
} else if (col_focus < COLUMNS.COLLAPSE_BUTTON) {
|
||||||
|
col_focus = COLUMNS.UNREAD_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since header rows always have a collapse button, other rows have one less element to focus.
|
||||||
|
if (col_focus === COLUMNS.COLLAPSE_BUTTON) {
|
||||||
|
if (not_a_header_row && ["left_arrow", "shift_tab"].includes(input_key)) {
|
||||||
|
// Focus on unread count.
|
||||||
|
col_focus = COLUMNS.UNREAD_COUNT;
|
||||||
|
} else {
|
||||||
|
$row_to_focus.trigger("focus");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (not_a_header_row && col_focus === COLUMNS.RECIPIENT) {
|
||||||
|
if (["right_arrow", "tab"].includes(input_key)) {
|
||||||
|
// Focus on unread count.
|
||||||
|
col_focus = COLUMNS.UNREAD_COUNT;
|
||||||
|
} else if (["left_arrow", "shift_tab"].includes(input_key)) {
|
||||||
|
col_focus = COLUMNS.COLLAPSE_BUTTON;
|
||||||
|
$row_to_focus.trigger("focus");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$row_to_focus.trigger("focus");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const $cols_to_focus = $row_to_focus.find("[tabindex=0]");
|
||||||
|
$($cols_to_focus.get(col_focus - 1)).trigger("focus");
|
||||||
|
}
|
||||||
|
|
||||||
|
function focus_muted_filter() {
|
||||||
|
current_focus_id = MUTED_FILTER_ID;
|
||||||
|
focus_current_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_search_focused() {
|
||||||
|
return current_focus_id === INBOX_SEARCH_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_muted_filter_focused() {
|
||||||
|
return current_focus_id === MUTED_FILTER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function change_focused_element(input_key) {
|
||||||
|
if (is_search_focused()) {
|
||||||
|
const textInput = $(`#${INBOX_SEARCH_ID}`).get(0);
|
||||||
|
const start = textInput.selectionStart;
|
||||||
|
const end = textInput.selectionEnd;
|
||||||
|
const text_length = textInput.value.length;
|
||||||
|
let is_selected = false;
|
||||||
|
if (end - start > 0) {
|
||||||
|
is_selected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (input_key) {
|
||||||
|
case "down_arrow":
|
||||||
|
set_list_focus();
|
||||||
|
return true;
|
||||||
|
case "right_arrow":
|
||||||
|
case "tab":
|
||||||
|
if (end !== text_length || is_selected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
focus_muted_filter();
|
||||||
|
return true;
|
||||||
|
case "escape":
|
||||||
|
set_list_focus();
|
||||||
|
return true;
|
||||||
|
case "shift_tab":
|
||||||
|
// Let user focus outside inbox view.
|
||||||
|
current_focus_id = "";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (is_muted_filter_focused()) {
|
||||||
|
switch (input_key) {
|
||||||
|
case "down_arrow":
|
||||||
|
case "tab":
|
||||||
|
set_list_focus();
|
||||||
|
return true;
|
||||||
|
case "left_arrow":
|
||||||
|
case "shift_tab":
|
||||||
|
set_default_focus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (input_key) {
|
||||||
|
case "down_arrow":
|
||||||
|
row_focus += 1;
|
||||||
|
set_list_focus();
|
||||||
|
return true;
|
||||||
|
case "up_arrow":
|
||||||
|
if (row_focus === 0) {
|
||||||
|
set_default_focus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
row_focus -= 1;
|
||||||
|
set_list_focus();
|
||||||
|
return true;
|
||||||
|
case "right_arrow":
|
||||||
|
case "tab":
|
||||||
|
col_focus += 1;
|
||||||
|
set_list_focus(input_key);
|
||||||
|
return true;
|
||||||
|
case "left_arrow":
|
||||||
|
case "shift_tab":
|
||||||
|
col_focus -= 1;
|
||||||
|
set_list_focus(input_key);
|
||||||
|
return true;
|
||||||
|
case "escape":
|
||||||
|
hashchange.set_hash_to_default_view();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update() {
|
||||||
|
if (!is_visible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unread_dms = unread.get_unread_pm();
|
||||||
|
const unread_dms_count = unread_dms.total_count;
|
||||||
|
const unread_dms_dict = unread_dms.pm_dict;
|
||||||
|
|
||||||
|
const unread_stream_message = unread.get_unread_topics();
|
||||||
|
const unread_streams_dict = unread_stream_message.stream_count;
|
||||||
|
|
||||||
|
let has_dms_post_filter = false;
|
||||||
|
|
||||||
|
for (const [key, value] of unread_dms_dict) {
|
||||||
|
if (value !== 0) {
|
||||||
|
const old_dm_data = dms_dict[key];
|
||||||
|
const new_dm_data = format_dm(key, value);
|
||||||
|
rerender_dm_inbox_row_if_needed(new_dm_data, old_dm_data);
|
||||||
|
dms_dict[key] = new_dm_data;
|
||||||
|
if (!new_dm_data.is_hidden) {
|
||||||
|
has_dms_post_filter = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If it is rendered.
|
||||||
|
if (dms_dict[key] !== undefined) {
|
||||||
|
delete dms_dict[key];
|
||||||
|
get_row_from_conversation_key(key).remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const $inbox_dm_header = $("#inbox-dm-header");
|
||||||
|
if (!has_dms_post_filter) {
|
||||||
|
$inbox_dm_header.addClass("hidden_by_filters");
|
||||||
|
} else {
|
||||||
|
$inbox_dm_header.removeClass("hidden_by_filters");
|
||||||
|
$inbox_dm_header.find(".unread_count").text(unread_dms_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [stream_id, topic_dict] of unread_streams_dict) {
|
||||||
|
const stream_unread = unread.num_unread_for_stream(stream_id);
|
||||||
|
const stream_unread_count = stream_unread.unmuted_count + stream_unread.muted_count;
|
||||||
|
const stream_key = get_stream_key(stream_id);
|
||||||
|
if (stream_unread_count > 0) {
|
||||||
|
// Stream isn't rendered.
|
||||||
|
if (topics_dict[stream_key] === undefined) {
|
||||||
|
insert_stream(stream_id, topic_dict, stream_unread);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const new_stream_data = format_stream(stream_id, stream_unread);
|
||||||
|
for (const [topic, topic_unread_count] of topic_dict) {
|
||||||
|
const topic_key = get_topic_key(stream_id, topic);
|
||||||
|
if (topic_unread_count) {
|
||||||
|
const old_topic_data = topics_dict[stream_key][topic_key];
|
||||||
|
const new_topic_data = format_topic(stream_id, topic, topic_unread_count);
|
||||||
|
topics_dict[stream_key][topic_key] = new_topic_data;
|
||||||
|
rerender_topic_inbox_row_if_needed(new_topic_data, old_topic_data);
|
||||||
|
if (!new_topic_data.is_hidden) {
|
||||||
|
new_stream_data.is_hidden = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
get_row_from_conversation_key(topic_key).remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const old_stream_data = streams_dict[stream_key];
|
||||||
|
streams_dict[stream_key] = new_stream_data;
|
||||||
|
rerender_stream_inbox_header_if_needed(new_stream_data, old_stream_data);
|
||||||
|
} else {
|
||||||
|
delete topics_dict[stream_key];
|
||||||
|
delete streams_dict[stream_key];
|
||||||
|
get_stream_container(stream_key).remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update_triggered_by_user) {
|
||||||
|
setTimeout(revive_current_focus, 0);
|
||||||
|
update_triggered_by_user = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_focus_class_for_header() {
|
||||||
|
let focus_class = ".collapsible-button";
|
||||||
|
|
||||||
|
switch (col_focus) {
|
||||||
|
case COLUMNS.RECIPIENT: {
|
||||||
|
focus_class = ".inbox-header-name a";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case COLUMNS.UNREAD_COUNT: {
|
||||||
|
focus_class = ".unread_count";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return focus_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_focus_class_for_row() {
|
||||||
|
let focus_class = ".inbox-left-part";
|
||||||
|
if (col_focus === COLUMNS.UNREAD_COUNT) {
|
||||||
|
focus_class = ".unread_count";
|
||||||
|
}
|
||||||
|
return focus_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initialize() {
|
||||||
|
$("body").on(
|
||||||
|
"keyup",
|
||||||
|
"#inbox-search",
|
||||||
|
_.debounce(() => {
|
||||||
|
search_and_update();
|
||||||
|
}, 300),
|
||||||
|
);
|
||||||
|
|
||||||
|
$("body").on("keydown", ".inbox-header", (e) => {
|
||||||
|
if (keydown_util.is_enter_event(e)) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const $elt = $(e.currentTarget);
|
||||||
|
$elt.find(get_focus_class_for_header()).trigger("click");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on("click", "#inbox-list .inbox-header .collapsible-button", (e) => {
|
||||||
|
const $elt = $(e.currentTarget);
|
||||||
|
const container_id = $elt.parents(".inbox-header").attr("id");
|
||||||
|
col_focus = COLUMNS.COLLAPSE_BUTTON;
|
||||||
|
focus_clicked_element($elt);
|
||||||
|
collapse_or_expand(container_id);
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on("keydown", ".inbox-row", (e) => {
|
||||||
|
if (keydown_util.is_enter_event(e)) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const $elt = $(e.currentTarget);
|
||||||
|
$elt.find(get_focus_class_for_row()).trigger("click");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on("click", "#inbox-list .inbox-row, #inbox-list .inbox-header", (e) => {
|
||||||
|
const $elt = $(e.currentTarget);
|
||||||
|
col_focus = COLUMNS.RECIPIENT;
|
||||||
|
focus_clicked_element($elt);
|
||||||
|
window.location.href = $elt.find("a").attr("href");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on("click", "#include_muted", () => {
|
||||||
|
current_focus_id = MUTED_FILTER_ID;
|
||||||
|
update_triggered_by_user = true;
|
||||||
|
toggle_muted_filter();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on("click", "#inbox-list .on_hover_dm_read", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
const $elt = $(e.currentTarget);
|
||||||
|
col_focus = COLUMNS.UNREAD_COUNT;
|
||||||
|
focus_clicked_element($elt);
|
||||||
|
const user_ids_string = $elt.attr("data-user-ids-string");
|
||||||
|
if (user_ids_string) {
|
||||||
|
// direct message row
|
||||||
|
unread_ops.mark_pm_as_read(user_ids_string);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on("click", "#inbox-list .on_hover_all_dms_read", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
const unread_dms_msg_ids = unread.get_msg_ids_for_private();
|
||||||
|
const unread_dms_messages = unread_dms_msg_ids.map((msg_id) => message_store.get(msg_id));
|
||||||
|
unread_ops.notify_server_messages_read(unread_dms_messages);
|
||||||
|
set_default_focus();
|
||||||
|
update_triggered_by_user = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on("click", "#inbox-list .on_hover_topic_read", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
const $elt = $(e.currentTarget);
|
||||||
|
col_focus = COLUMNS.UNREAD_COUNT;
|
||||||
|
focus_clicked_element($elt);
|
||||||
|
const user_ids_string = $elt.attr("data-user-ids-string");
|
||||||
|
if (user_ids_string) {
|
||||||
|
// direct message row
|
||||||
|
unread_ops.mark_pm_as_read(user_ids_string);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const stream_id = Number.parseInt($elt.attr("data-stream-id"), 10);
|
||||||
|
const topic = $elt.attr("data-topic-name");
|
||||||
|
if (topic) {
|
||||||
|
unread_ops.mark_topic_as_read(stream_id, topic);
|
||||||
|
} else {
|
||||||
|
unread_ops.mark_stream_as_read(stream_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
import $ from "jquery";
|
||||||
|
|
||||||
|
import * as compose_state from "./compose_state";
|
||||||
|
import * as overlays from "./overlays";
|
||||||
|
import * as popovers from "./popovers";
|
||||||
|
import * as stream_color from "./stream_color";
|
||||||
|
import * as stream_data from "./stream_data";
|
||||||
|
|
||||||
|
let is_inbox_visible = false;
|
||||||
|
|
||||||
|
export function set_visible(value) {
|
||||||
|
is_inbox_visible = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function is_visible() {
|
||||||
|
return is_inbox_visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get_dm_key(msg) {
|
||||||
|
return "dm:" + msg.other_user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function is_in_focus() {
|
||||||
|
// Check if user is focused on
|
||||||
|
// inbox
|
||||||
|
return (
|
||||||
|
is_visible() &&
|
||||||
|
!compose_state.composing() &&
|
||||||
|
!popovers.any_active() &&
|
||||||
|
!overlays.is_overlay_or_modal_open() &&
|
||||||
|
!$(".home-page-input").is(":focus")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update_stream_colors() {
|
||||||
|
if (!is_visible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const $stream_headers = $("#inbox-streams-container .inbox-header");
|
||||||
|
$stream_headers.each((_index, stream_header) => {
|
||||||
|
const $stream_header = $(stream_header);
|
||||||
|
const stream_id = Number.parseInt($stream_header.attr("data-stream-id"), 10);
|
||||||
|
if (!stream_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const color = stream_data.get_color(stream_id);
|
||||||
|
const background_color = stream_color.get_recipient_bar_color(color);
|
||||||
|
$stream_header.css("background", background_color);
|
||||||
|
});
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ export function deselect_top_left_corner_items() {
|
||||||
remove($(".top_left_starred_messages"));
|
remove($(".top_left_starred_messages"));
|
||||||
remove($(".top_left_mentions"));
|
remove($(".top_left_mentions"));
|
||||||
remove($(".top_left_recent_view"));
|
remove($(".top_left_recent_view"));
|
||||||
|
remove($(".top_left_inbox"));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handle_narrow_activated(filter) {
|
export function handle_narrow_activated(filter) {
|
||||||
|
@ -91,6 +92,7 @@ export function highlight_recent_view() {
|
||||||
remove($(".top_left_all_messages"));
|
remove($(".top_left_all_messages"));
|
||||||
remove($(".top_left_starred_messages"));
|
remove($(".top_left_starred_messages"));
|
||||||
remove($(".top_left_mentions"));
|
remove($(".top_left_mentions"));
|
||||||
|
remove($(".top_left_inbox"));
|
||||||
$(".top_left_recent_view").addClass("active-filter");
|
$(".top_left_recent_view").addClass("active-filter");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resize.resize_stream_filters_container();
|
resize.resize_stream_filters_container();
|
||||||
|
@ -120,3 +122,14 @@ function do_new_messages_animation($li) {
|
||||||
export function initialize() {
|
export function initialize() {
|
||||||
update_scheduled_messages_row();
|
update_scheduled_messages_row();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function highlight_inbox_view() {
|
||||||
|
remove($(".top_left_all_messages"));
|
||||||
|
remove($(".top_left_starred_messages"));
|
||||||
|
remove($(".top_left_recent_view"));
|
||||||
|
remove($(".top_left_mentions"));
|
||||||
|
$(".top_left_inbox").addClass("active-filter");
|
||||||
|
setTimeout(() => {
|
||||||
|
resize.resize_stream_filters_container();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import * as compose_state from "./compose_state";
|
||||||
import * as compose_validate from "./compose_validate";
|
import * as compose_validate from "./compose_validate";
|
||||||
import * as drafts from "./drafts";
|
import * as drafts from "./drafts";
|
||||||
import * as huddle_data from "./huddle_data";
|
import * as huddle_data from "./huddle_data";
|
||||||
|
import * as inbox_ui from "./inbox_ui";
|
||||||
import * as message_edit from "./message_edit";
|
import * as message_edit from "./message_edit";
|
||||||
import * as message_edit_history from "./message_edit_history";
|
import * as message_edit_history from "./message_edit_history";
|
||||||
import * as message_helper from "./message_helper";
|
import * as message_helper from "./message_helper";
|
||||||
|
@ -165,6 +166,7 @@ export function insert_new_messages(messages, sent_by_this_client) {
|
||||||
stream_list.update_streams_sidebar();
|
stream_list.update_streams_sidebar();
|
||||||
pm_list.update_private_messages();
|
pm_list.update_private_messages();
|
||||||
recent_view_ui.process_messages(messages);
|
recent_view_ui.process_messages(messages);
|
||||||
|
inbox_ui.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update_messages(events) {
|
export function update_messages(events) {
|
||||||
|
@ -237,6 +239,7 @@ export function update_messages(events) {
|
||||||
anchor_message.topic,
|
anchor_message.topic,
|
||||||
);
|
);
|
||||||
recent_view_ui.inplace_rerender(topic_key);
|
recent_view_ui.inplace_rerender(topic_key);
|
||||||
|
inbox_ui.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,6 +514,7 @@ export function update_messages(events) {
|
||||||
});
|
});
|
||||||
unread.clear_and_populate_unread_mention_topics();
|
unread.clear_and_populate_unread_mention_topics();
|
||||||
recent_view_ui.process_topic_edit(...args);
|
recent_view_ui.process_topic_edit(...args);
|
||||||
|
inbox_ui.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rerender "Message edit history" if it was open to the edited message.
|
// Rerender "Message edit history" if it was open to the edited message.
|
||||||
|
@ -571,6 +575,7 @@ export function remove_messages(message_ids) {
|
||||||
}
|
}
|
||||||
recent_senders.update_topics_of_deleted_message_ids(message_ids);
|
recent_senders.update_topics_of_deleted_message_ids(message_ids);
|
||||||
recent_view_ui.update_topics_of_deleted_message_ids(message_ids);
|
recent_view_ui.update_topics_of_deleted_message_ids(message_ids);
|
||||||
|
inbox_ui.update();
|
||||||
starred_messages.remove(message_ids);
|
starred_messages.remove(message_ids);
|
||||||
starred_messages_ui.rerender_ui();
|
starred_messages_ui.rerender_ui();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {all_messages_data} from "./all_messages_data";
|
||||||
import * as channel from "./channel";
|
import * as channel from "./channel";
|
||||||
import {Filter} from "./filter";
|
import {Filter} from "./filter";
|
||||||
import * as huddle_data from "./huddle_data";
|
import * as huddle_data from "./huddle_data";
|
||||||
|
import * as inbox_ui from "./inbox_ui";
|
||||||
import * as message_feed_loading from "./message_feed_loading";
|
import * as message_feed_loading from "./message_feed_loading";
|
||||||
import * as message_feed_top_notices from "./message_feed_top_notices";
|
import * as message_feed_top_notices from "./message_feed_top_notices";
|
||||||
import * as message_helper from "./message_helper";
|
import * as message_helper from "./message_helper";
|
||||||
|
@ -53,6 +54,7 @@ function process_result(data, opts) {
|
||||||
|
|
||||||
huddle_data.process_loaded_messages(messages);
|
huddle_data.process_loaded_messages(messages);
|
||||||
recent_view_ui.process_messages(messages);
|
recent_view_ui.process_messages(messages);
|
||||||
|
inbox_ui.update();
|
||||||
stream_list.update_streams_sidebar();
|
stream_list.update_streams_sidebar();
|
||||||
stream_list.maybe_scroll_narrow_into_view();
|
stream_list.maybe_scroll_narrow_into_view();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
|
|
||||||
import {Filter} from "./filter";
|
import {Filter} from "./filter";
|
||||||
|
import * as inbox_util from "./inbox_util";
|
||||||
import * as message_list from "./message_list";
|
import * as message_list from "./message_list";
|
||||||
import * as recent_view_util from "./recent_view_util";
|
import * as recent_view_util from "./recent_view_util";
|
||||||
import * as ui_util from "./ui_util";
|
import * as ui_util from "./ui_util";
|
||||||
|
@ -28,6 +29,7 @@ export function update_recipient_bar_background_color() {
|
||||||
for (const msg_list of all_rendered_message_lists()) {
|
for (const msg_list of all_rendered_message_lists()) {
|
||||||
msg_list.view.update_recipient_bar_background_color();
|
msg_list.view.update_recipient_bar_background_color();
|
||||||
}
|
}
|
||||||
|
inbox_util.update_stream_colors();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initialize() {
|
export function initialize() {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import $ from "jquery";
|
||||||
import render_message_view_header from "../templates/message_view_header.hbs";
|
import render_message_view_header from "../templates/message_view_header.hbs";
|
||||||
|
|
||||||
import {$t} from "./i18n";
|
import {$t} from "./i18n";
|
||||||
|
import * as inbox_util from "./inbox_util";
|
||||||
import * as narrow_state from "./narrow_state";
|
import * as narrow_state from "./narrow_state";
|
||||||
import * as peer_data from "./peer_data";
|
import * as peer_data from "./peer_data";
|
||||||
import * as popovers from "./popovers";
|
import * as popovers from "./popovers";
|
||||||
|
@ -26,6 +27,12 @@ function make_message_view_header(filter) {
|
||||||
icon: "clock-o",
|
icon: "clock-o",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (inbox_util.is_visible()) {
|
||||||
|
return {
|
||||||
|
title: $t({defaultMessage: "Inbox"}),
|
||||||
|
zulip_icon: "inbox",
|
||||||
|
};
|
||||||
|
}
|
||||||
if (filter === undefined) {
|
if (filter === undefined) {
|
||||||
return {
|
return {
|
||||||
title: $t({defaultMessage: "All messages"}),
|
title: $t({defaultMessage: "All messages"}),
|
||||||
|
|
|
@ -4,6 +4,7 @@ import * as activity from "./activity";
|
||||||
import * as channel from "./channel";
|
import * as channel from "./channel";
|
||||||
import * as confirm_dialog from "./confirm_dialog";
|
import * as confirm_dialog from "./confirm_dialog";
|
||||||
import {$t_html} from "./i18n";
|
import {$t_html} from "./i18n";
|
||||||
|
import * as inbox_ui from "./inbox_ui";
|
||||||
import * as message_lists from "./message_lists";
|
import * as message_lists from "./message_lists";
|
||||||
import * as muted_users from "./muted_users";
|
import * as muted_users from "./muted_users";
|
||||||
import * as overlays from "./overlays";
|
import * as overlays from "./overlays";
|
||||||
|
@ -58,6 +59,7 @@ export function rerender_for_muted_user() {
|
||||||
// If a user is (un)muted, we want to update their avatars on the Recent Conversations
|
// If a user is (un)muted, we want to update their avatars on the Recent Conversations
|
||||||
// participants column.
|
// participants column.
|
||||||
recent_view_ui.complete_rerender();
|
recent_view_ui.complete_rerender();
|
||||||
|
inbox_ui.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handle_user_updates(muted_user_ids) {
|
export function handle_user_updates(muted_user_ids) {
|
||||||
|
|
|
@ -16,6 +16,8 @@ import {Filter} from "./filter";
|
||||||
import * as hash_util from "./hash_util";
|
import * as hash_util from "./hash_util";
|
||||||
import * as hashchange from "./hashchange";
|
import * as hashchange from "./hashchange";
|
||||||
import {$t} from "./i18n";
|
import {$t} from "./i18n";
|
||||||
|
import * as inbox_ui from "./inbox_ui";
|
||||||
|
import * as inbox_util from "./inbox_util";
|
||||||
import * as left_sidebar_navigation_area from "./left_sidebar_navigation_area";
|
import * as left_sidebar_navigation_area from "./left_sidebar_navigation_area";
|
||||||
import * as message_edit from "./message_edit";
|
import * as message_edit from "./message_edit";
|
||||||
import * as message_feed_loading from "./message_feed_loading";
|
import * as message_feed_loading from "./message_feed_loading";
|
||||||
|
@ -195,6 +197,7 @@ export function activate(raw_operators, opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const coming_from_recent_view = recent_view_util.is_visible();
|
const coming_from_recent_view = recent_view_util.is_visible();
|
||||||
|
const coming_from_inbox = inbox_util.is_visible();
|
||||||
|
|
||||||
// The empty narrow is the home view; so deactivate any narrow if
|
// The empty narrow is the home view; so deactivate any narrow if
|
||||||
// no operators were specified. Take us to all messages when this
|
// no operators were specified. Take us to all messages when this
|
||||||
|
@ -382,11 +385,12 @@ export function activate(raw_operators, opts) {
|
||||||
|
|
||||||
if (coming_from_recent_view) {
|
if (coming_from_recent_view) {
|
||||||
recent_view_ui.hide();
|
recent_view_ui.hide();
|
||||||
|
} else if (coming_from_inbox) {
|
||||||
|
inbox_ui.hide();
|
||||||
} else {
|
} else {
|
||||||
// If Recent Conversations was not visible, then we are switching
|
// We must instead be switching from another message view.
|
||||||
// from another message list view. Save the scroll position in
|
// Save the scroll position in that message list, so that
|
||||||
// that message list, so that we can restore it if/when we
|
// we can restore it if/when we later navigate back to that view.
|
||||||
// later navigate back to that view.
|
|
||||||
save_pre_narrow_offset_for_reload();
|
save_pre_narrow_offset_for_reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as blueslip from "./blueslip";
|
import * as blueslip from "./blueslip";
|
||||||
import {Filter} from "./filter";
|
import {Filter} from "./filter";
|
||||||
|
import * as inbox_util from "./inbox_util";
|
||||||
import {page_params} from "./page_params";
|
import {page_params} from "./page_params";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
import * as recent_view_util from "./recent_view_util";
|
import * as recent_view_util from "./recent_view_util";
|
||||||
|
@ -34,7 +35,7 @@ export function operators() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function is_message_feed_visible() {
|
export function is_message_feed_visible() {
|
||||||
return !recent_view_util.is_visible();
|
return !recent_view_util.is_visible() && !inbox_util.is_visible();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update_email(user_id, new_email) {
|
export function update_email(user_id, new_email) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import mixPlugin from "colord/plugins/mix";
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
|
|
||||||
import {$t} from "./i18n";
|
import {$t} from "./i18n";
|
||||||
|
import * as inbox_util from "./inbox_util";
|
||||||
import * as message_lists from "./message_lists";
|
import * as message_lists from "./message_lists";
|
||||||
import * as message_view_header from "./message_view_header";
|
import * as message_view_header from "./message_view_header";
|
||||||
import * as overlays from "./overlays";
|
import * as overlays from "./overlays";
|
||||||
|
@ -85,6 +86,11 @@ function update_message_recipient_color(stream_name, color) {
|
||||||
recipient_color,
|
recipient_color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inbox_util.is_visible()) {
|
||||||
|
const stream_id = stream_data.get_stream_id(stream_name);
|
||||||
|
$(`#inbox-stream-header-${stream_id}`).css("background", recipient_color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const stream_color_palette = [
|
const stream_color_palette = [
|
||||||
|
|
|
@ -4,6 +4,7 @@ import * as blueslip from "./blueslip";
|
||||||
import * as color_data from "./color_data";
|
import * as color_data from "./color_data";
|
||||||
import * as compose_fade from "./compose_fade";
|
import * as compose_fade from "./compose_fade";
|
||||||
import * as compose_recipient from "./compose_recipient";
|
import * as compose_recipient from "./compose_recipient";
|
||||||
|
import * as inbox_ui from "./inbox_ui";
|
||||||
import * as message_lists from "./message_lists";
|
import * as message_lists from "./message_lists";
|
||||||
import * as message_view_header from "./message_view_header";
|
import * as message_view_header from "./message_view_header";
|
||||||
import * as narrow_state from "./narrow_state";
|
import * as narrow_state from "./narrow_state";
|
||||||
|
@ -55,6 +56,7 @@ export function update_property(stream_id, property, value, other_values) {
|
||||||
stream_muting.update_is_muted(sub, value);
|
stream_muting.update_is_muted(sub, value);
|
||||||
stream_list.refresh_muted_or_unmuted_stream(sub);
|
stream_list.refresh_muted_or_unmuted_stream(sub);
|
||||||
recent_view_ui.complete_rerender();
|
recent_view_ui.complete_rerender();
|
||||||
|
inbox_ui.update();
|
||||||
break;
|
break;
|
||||||
case "desktop_notifications":
|
case "desktop_notifications":
|
||||||
case "audible_notifications":
|
case "audible_notifications":
|
||||||
|
|
|
@ -41,6 +41,7 @@ import * as hashchange from "./hashchange";
|
||||||
import * as hotkey from "./hotkey";
|
import * as hotkey from "./hotkey";
|
||||||
import * as hotspots from "./hotspots";
|
import * as hotspots from "./hotspots";
|
||||||
import * as i18n from "./i18n";
|
import * as i18n from "./i18n";
|
||||||
|
import * as inbox_ui from "./inbox_ui";
|
||||||
import * as invite from "./invite";
|
import * as invite from "./invite";
|
||||||
import * as left_sidebar_navigation_area from "./left_sidebar_navigation_area";
|
import * as left_sidebar_navigation_area from "./left_sidebar_navigation_area";
|
||||||
import * as lightbox from "./lightbox";
|
import * as lightbox from "./lightbox";
|
||||||
|
@ -185,6 +186,7 @@ function initialize_bottom_whitespace() {
|
||||||
function initialize_left_sidebar() {
|
function initialize_left_sidebar() {
|
||||||
const rendered_sidebar = render_left_sidebar({
|
const rendered_sidebar = render_left_sidebar({
|
||||||
is_guest: page_params.is_guest,
|
is_guest: page_params.is_guest,
|
||||||
|
development_environment: page_params.development_environment,
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#left-sidebar-container").html(rendered_sidebar);
|
$("#left-sidebar-container").html(rendered_sidebar);
|
||||||
|
@ -682,6 +684,7 @@ export function initialize_everything() {
|
||||||
realm_logo.initialize();
|
realm_logo.initialize();
|
||||||
message_lists.initialize();
|
message_lists.initialize();
|
||||||
recent_view_ui.initialize();
|
recent_view_ui.initialize();
|
||||||
|
inbox_ui.initialize();
|
||||||
alert_words.initialize(alert_words_params);
|
alert_words.initialize(alert_words_params);
|
||||||
emojisets.initialize();
|
emojisets.initialize();
|
||||||
scroll_bar.initialize();
|
scroll_bar.initialize();
|
||||||
|
|
|
@ -260,11 +260,11 @@ class UnreadTopicCounter {
|
||||||
this.bucketer.delete(msg_id);
|
this.bucketer.delete(msg_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
get_counts() {
|
get_counts(include_per_topic_count = false) {
|
||||||
const res = {};
|
const res = {};
|
||||||
res.stream_unread_messages = 0;
|
res.stream_unread_messages = 0;
|
||||||
res.stream_count = new Map(); // hash by stream_id -> count
|
res.stream_count = new Map(); // hash by stream_id -> count
|
||||||
for (const [stream_id] of this.bucketer) {
|
for (const [stream_id, per_stream_bucketer] of this.bucketer) {
|
||||||
// We track unread counts for streams that may be currently
|
// We track unread counts for streams that may be currently
|
||||||
// unsubscribed. Since users may re-subscribe, we don't
|
// unsubscribed. Since users may re-subscribe, we don't
|
||||||
// completely throw away the data. But we do ignore it here,
|
// completely throw away the data. But we do ignore it here,
|
||||||
|
@ -274,8 +274,24 @@ class UnreadTopicCounter {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.stream_count.set(stream_id, this.get_stream_count(stream_id));
|
if (include_per_topic_count) {
|
||||||
res.stream_unread_messages += res.stream_count.get(stream_id).unmuted_count;
|
const topic_unread = new Map();
|
||||||
|
let stream_count = 0;
|
||||||
|
for (const [topic, msgs] of per_stream_bucketer) {
|
||||||
|
const topic_count = msgs.size;
|
||||||
|
topic_unread.set(topic, topic_count);
|
||||||
|
stream_count += topic_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: These don't agree with the else clause in how
|
||||||
|
// they handle muted streams/topics, and that's
|
||||||
|
// probably a bug.
|
||||||
|
res.stream_count.set(stream_id, topic_unread);
|
||||||
|
res.stream_unread_messages += stream_count;
|
||||||
|
} else {
|
||||||
|
res.stream_count.set(stream_id, this.get_stream_count(stream_id));
|
||||||
|
res.stream_unread_messages += res.stream_count.get(stream_id).unmuted_count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -731,6 +747,17 @@ export function declare_bankruptcy() {
|
||||||
unread_mention_topics.clear();
|
unread_mention_topics.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function get_unread_pm() {
|
||||||
|
const pm_res = unread_direct_message_counter.get_counts();
|
||||||
|
return pm_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get_unread_topics() {
|
||||||
|
const include_per_topic_count = true;
|
||||||
|
const topics_res = unread_topic_counter.get_counts(include_per_topic_count);
|
||||||
|
return topics_res;
|
||||||
|
}
|
||||||
|
|
||||||
export function get_counts() {
|
export function get_counts() {
|
||||||
const res = {};
|
const res = {};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import * as channel from "./channel";
|
||||||
import * as confirm_dialog from "./confirm_dialog";
|
import * as confirm_dialog from "./confirm_dialog";
|
||||||
import * as dialog_widget from "./dialog_widget";
|
import * as dialog_widget from "./dialog_widget";
|
||||||
import {$t_html} from "./i18n";
|
import {$t_html} from "./i18n";
|
||||||
|
import * as inbox_ui from "./inbox_ui";
|
||||||
import * as loading from "./loading";
|
import * as loading from "./loading";
|
||||||
import * as message_flags from "./message_flags";
|
import * as message_flags from "./message_flags";
|
||||||
import * as message_lists from "./message_lists";
|
import * as message_lists from "./message_lists";
|
||||||
|
@ -167,6 +168,7 @@ function process_newly_read_message(message, options) {
|
||||||
}
|
}
|
||||||
notifications.close_notification(message);
|
notifications.close_notification(message);
|
||||||
recent_view_ui.update_topic_unread_count(message);
|
recent_view_ui.update_topic_unread_count(message);
|
||||||
|
inbox_ui.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mark_as_unread_from_here(
|
export function mark_as_unread_from_here(
|
||||||
|
@ -310,6 +312,7 @@ export function process_read_messages_event(message_ids) {
|
||||||
}
|
}
|
||||||
|
|
||||||
unread_ui.update_unread_counts();
|
unread_ui.update_unread_counts();
|
||||||
|
inbox_ui.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function process_unread_messages_event({message_ids, message_details}) {
|
export function process_unread_messages_event({message_ids, message_details}) {
|
||||||
|
@ -386,6 +389,7 @@ export function process_unread_messages_event({message_ids, message_details}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
unread_ui.update_unread_counts();
|
unread_ui.update_unread_counts();
|
||||||
|
inbox_ui.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes a list of messages and marks them as read.
|
// Takes a list of messages and marks them as read.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
|
|
||||||
|
import * as inbox_ui from "./inbox_ui";
|
||||||
import * as message_lists from "./message_lists";
|
import * as message_lists from "./message_lists";
|
||||||
import * as overlays from "./overlays";
|
import * as overlays from "./overlays";
|
||||||
import * as popover_menus from "./popover_menus";
|
import * as popover_menus from "./popover_menus";
|
||||||
|
@ -25,6 +26,7 @@ export function handle_topic_updates(user_topic_event) {
|
||||||
user_topic_event.stream_id,
|
user_topic_event.stream_id,
|
||||||
user_topic_event.topic_name,
|
user_topic_event.topic_name,
|
||||||
);
|
);
|
||||||
|
inbox_ui.update();
|
||||||
|
|
||||||
if (overlays.settings_open() && settings_user_topics.loaded) {
|
if (overlays.settings_open() && settings_user_topics.loaded) {
|
||||||
const stream_id = user_topic_event.stream_id;
|
const stream_id = user_topic_event.stream_id;
|
||||||
|
|
|
@ -0,0 +1,318 @@
|
||||||
|
.inbox-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--color-background-inbox);
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 0;
|
||||||
|
max-height: 100vh;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-right: 1px solid var(--color-border-inbox);
|
||||||
|
border-left: 1px solid var(--color-border-inbox);
|
||||||
|
|
||||||
|
#inbox-pane {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: var(--navbar-fixed-height) 25px 0;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text-message-header);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unread_count {
|
||||||
|
opacity: 0.7;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search_group {
|
||||||
|
display: flex;
|
||||||
|
margin: 15px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-inbox-filter {
|
||||||
|
border: none;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-text-default);
|
||||||
|
padding: 5px 10px;
|
||||||
|
margin-left: 10px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background-color: var(--color-background-btn-inbox-focus);
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-inbox-selected {
|
||||||
|
background-color: var(--color-background-btn-inbox-selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#inbox-filters {
|
||||||
|
.zulip-icon-search-inbox {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(var(--navbar-fixed-height) + 23px);
|
||||||
|
left: 32px;
|
||||||
|
color: var(--color-icon-search-inbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#inbox-search {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
width: var(--width-inbox-search);
|
||||||
|
height: var(--height-inbox-search);
|
||||||
|
background-color: var(--color-background-inbox-search);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding-right: 20px;
|
||||||
|
padding-left: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 17px;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
background: var(--color-background-inbox-search-focus);
|
||||||
|
border: 1px solid var(--color-border-inbox-search-focus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#inbox-list {
|
||||||
|
/* 55px = height of filters
|
||||||
|
42px = height of closed compose box */
|
||||||
|
height: calc(100vh - 55px - 42px - var(--navbar-fixed-height));
|
||||||
|
|
||||||
|
.simplebar-content {
|
||||||
|
overflow-x: hidden;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid hsl(0deg 0% 0% / 20%);
|
||||||
|
margin-bottom: var(--max-unexpanded-compose-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbox-header {
|
||||||
|
display: flex;
|
||||||
|
height: 30px;
|
||||||
|
|
||||||
|
.inbox-left-part {
|
||||||
|
grid-template: auto / auto min-content;
|
||||||
|
grid-template-areas: "header_name unread_count";
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbox-header-name {
|
||||||
|
grid-area: header_name;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 1px 6px;
|
||||||
|
outline: 0;
|
||||||
|
|
||||||
|
& a {
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus a {
|
||||||
|
color: hsl(0deg 0% 100%);
|
||||||
|
background-color: hsl(0deg 0% 11%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: inset 2px 0 0 0 var(--color-unread-marker);
|
||||||
|
|
||||||
|
.toggle-inbox-header-icon {
|
||||||
|
opacity: 1;
|
||||||
|
color: hsl(0deg 0% 11%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-group {
|
||||||
|
margin-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-lock {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-envelope,
|
||||||
|
.stream-privacy.filter-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
margin-right: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-envelope {
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-button {
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zulip-icon-arrow-down {
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 7px 4px;
|
||||||
|
margin-right: 9px;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_collapsed_state {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user_circle {
|
||||||
|
/* size of the user activity circle */
|
||||||
|
min-width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
margin-right: 5px;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zulip-icon-bot {
|
||||||
|
font-size: 11px;
|
||||||
|
margin-left: -2px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbox-row {
|
||||||
|
display: flex;
|
||||||
|
min-height: 30px;
|
||||||
|
background-color: var(--color-background-inbox-row);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-background-inbox-row-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-shadow: inset 2px 0 0 0 var(--color-unread-marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbox-left-part {
|
||||||
|
grid-template: auto / min-content auto min-content;
|
||||||
|
grid-template-areas: "match_topic_and_dm_start recipient_info unread_count";
|
||||||
|
}
|
||||||
|
|
||||||
|
.fake-collapse-button,
|
||||||
|
.inbox-topic-container .user_circle {
|
||||||
|
grid-area: match_topic_and_dm_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recipient_info,
|
||||||
|
.inbox-topic-name {
|
||||||
|
grid-area: recipient_info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.unread_count {
|
||||||
|
grid-area: unread_count;
|
||||||
|
margin-right: 5px;
|
||||||
|
margin-left: 10px;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-privacy {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 4px;
|
||||||
|
margin-left: 17px;
|
||||||
|
|
||||||
|
.zulip-icon {
|
||||||
|
line-height: 14px;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbox-topic-name {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbox-left-part-wrapper {
|
||||||
|
display: flex;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#inbox-direct-messages-container .inbox-left-part {
|
||||||
|
padding: 3px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#inbox-direct-messages-container .inbox-left-part,
|
||||||
|
.inbox-topic-container .inbox-left-part {
|
||||||
|
/* 50px - space occupied by user circle icon */
|
||||||
|
padding-left: 37px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbox-left-part {
|
||||||
|
width: 100%;
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recipients_info {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
column-gap: 10px;
|
||||||
|
grid-area: recipient_info;
|
||||||
|
|
||||||
|
.user_block {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recipients_name {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#inbox_filter_mute_toggle {
|
||||||
|
font-size: 16px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#inbox-view {
|
||||||
|
display: none;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
#inbox-dm-header {
|
||||||
|
background-color: var(--color-background-private-message-header);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden_by_filters,
|
||||||
|
.collapsed_container {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
|
@ -490,12 +490,19 @@ li.active-sub-filter {
|
||||||
& i {
|
& i {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.zulip-icon-inbox {
|
||||||
|
font-size: 14px;
|
||||||
|
top: 2px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li.top_left_all_messages,
|
li.top_left_all_messages,
|
||||||
li.top_left_mentions,
|
li.top_left_mentions,
|
||||||
li.top_left_starred_messages,
|
li.top_left_starred_messages,
|
||||||
li.top_left_drafts,
|
li.top_left_drafts,
|
||||||
|
li.top_left_inbox,
|
||||||
li.top_left_recent_view,
|
li.top_left_recent_view,
|
||||||
li.top_left_scheduled_messages {
|
li.top_left_scheduled_messages {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -198,6 +198,25 @@ body {
|
||||||
--color-background-text-hover-direct-mention: hsl(240deg 70% 70% / 30%);
|
--color-background-text-hover-direct-mention: hsl(240deg 70% 70% / 30%);
|
||||||
--color-background-text-group-mention: hsl(183deg 60% 45% / 18%);
|
--color-background-text-group-mention: hsl(183deg 60% 45% / 18%);
|
||||||
--color-background-text-hover-group-mention: hsl(183deg 60% 45% / 30%);
|
--color-background-text-hover-group-mention: hsl(183deg 60% 45% / 30%);
|
||||||
|
|
||||||
|
/* Inbox view constants - Values from Figma design */
|
||||||
|
--height-inbox-search: 26px;
|
||||||
|
--width-inbox-search: 346px;
|
||||||
|
--color-background-inbox: hsl(0deg 0% 100% / 50%);
|
||||||
|
--color-background-inbox-search: hsl(0deg 0% 0% / 5%);
|
||||||
|
--color-icon-search-inbox: hsl(0deg 0% 0% / 50%);
|
||||||
|
--color-background-inbox-search-focus: hsl(0deg 0% 100%);
|
||||||
|
--color-border-inbox-search-focus: hsl(0deg 0% 100% / 20%);
|
||||||
|
--color-background-inbox-row: hsl(0deg 0% 100%);
|
||||||
|
--color-background-inbox-row-hover: linear-gradient(
|
||||||
|
0deg,
|
||||||
|
hsl(0deg 0% 0% / 5%) 0%,
|
||||||
|
hsl(0deg 0% 0% / 5%) 100%
|
||||||
|
),
|
||||||
|
hsl(0deg 0% 100%);
|
||||||
|
--color-background-btn-inbox-selected: hsl(0deg 0% 0% / 5%);
|
||||||
|
--color-background-btn-inbox-focus: hsl(0deg 0% 80%);
|
||||||
|
--color-border-inbox: hsl(0deg 0% 84.31%);
|
||||||
}
|
}
|
||||||
|
|
||||||
%dark-theme {
|
%dark-theme {
|
||||||
|
@ -258,6 +277,23 @@ body {
|
||||||
--color-background-text-hover-direct-mention: hsl(240deg 52% 60% / 45%);
|
--color-background-text-hover-direct-mention: hsl(240deg 52% 60% / 45%);
|
||||||
--color-background-text-group-mention: hsl(183deg 52% 40% / 20%);
|
--color-background-text-group-mention: hsl(183deg 52% 40% / 20%);
|
||||||
--color-background-text-hover-group-mention: hsl(183deg 52% 40% / 30%);
|
--color-background-text-hover-group-mention: hsl(183deg 52% 40% / 30%);
|
||||||
|
|
||||||
|
/* Inbox view */
|
||||||
|
--color-background-inbox: var(--color-background);
|
||||||
|
--color-background-inbox-search: hsl(0deg 0% 20%);
|
||||||
|
--color-icon-search-inbox: hsl(0deg 0% 100% / 50%);
|
||||||
|
--color-background-inbox-search-focus: hsl(0deg 0% 0%);
|
||||||
|
--color-border-inbox-search-focus: hsl(0deg 0% 0% / 20%);
|
||||||
|
--color-background-inbox-row: hsl(0deg 0% 14%);
|
||||||
|
--color-background-inbox-row-hover: linear-gradient(
|
||||||
|
0deg,
|
||||||
|
hsl(0deg 0% 100% / 5%) 0%,
|
||||||
|
hsl(0deg 0% 100% / 5%) 100%
|
||||||
|
),
|
||||||
|
hsl(0deg 0% 14.12%);
|
||||||
|
--color-background-btn-inbox-selected: hsl(0deg 0% 100% / 5%);
|
||||||
|
--color-background-btn-inbox-focus: hsl(0deg 0% 20%);
|
||||||
|
--color-border-inbox: hsl(0deg 0% 0% / 60%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen {
|
@media screen {
|
||||||
|
@ -2024,6 +2060,11 @@ div.focused-message-list {
|
||||||
margin: 0 2px 0 5px;
|
margin: 0 2px 0 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.zulip-icon-inbox {
|
||||||
|
position: relative;
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-header-stream-settings-button {
|
.message-header-stream-settings-button {
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<div id="inbox-dm-header" tabindex="0" class="inbox-header {{#unless has_dms_post_filter}}hidden_by_filters{{/unless}}">
|
||||||
|
<div class="inbox-left-part-wrapper">
|
||||||
|
<div class="collapsible-button"><i class="zulip-icon zulip-icon-arrow-down toggle-inbox-header-icon"></i></div>
|
||||||
|
<div class="inbox-left-part">
|
||||||
|
<div tabindex="0" class="inbox-header-name">
|
||||||
|
<i class="fa fa-envelope"></i>
|
||||||
|
<a tabindex="-1" role="button" href="/#narrow/is/private">{{t 'Direct message'}}</a>
|
||||||
|
</div>
|
||||||
|
<span class="unread_count tippy-zulip-tooltip on_hover_all_dms_read" data-tippy-content="{{t 'Mark as read' }}" role="button" tabindex="0" aria-label="{{t 'Mark as read' }}">{{unread_dms_count}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="inbox-direct-messages-container">
|
||||||
|
{{#each dms_dict}}
|
||||||
|
{{> inbox_row }}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="inbox-streams-container">
|
||||||
|
{{> inbox_stream_container }}
|
||||||
|
</div>
|
|
@ -0,0 +1,37 @@
|
||||||
|
{{#if is_stream}}
|
||||||
|
{{> inbox_stream_header_row}}
|
||||||
|
{{else}}
|
||||||
|
<div id="inbox-row-conversation-{{conversation_key}}" class="inbox-row {{#if is_hidden}}hidden_by_filters{{/if}}" tabindex="0">
|
||||||
|
<div class="inbox-left-part-wrapper">
|
||||||
|
<div class="inbox-left-part">
|
||||||
|
<div class="hide fake-collapse-button" tabindex="0"></div>
|
||||||
|
{{#if is_direct}}
|
||||||
|
<a class="recipients_info" href="{{dm_url}}">
|
||||||
|
{{#each recipients_info}}
|
||||||
|
<span class="user_block">
|
||||||
|
{{#if is_bot}}
|
||||||
|
<span class="zulip-icon zulip-icon-bot" aria-hidden="true"></span>
|
||||||
|
{{else}}
|
||||||
|
<span class="{{user_circle_class}} user_circle"></span>
|
||||||
|
{{/if}}
|
||||||
|
<span class="recipients_name">{{full_name}}</span>
|
||||||
|
</span>
|
||||||
|
{{/each}}
|
||||||
|
</a>
|
||||||
|
<span class="unread_count tippy-zulip-tooltip on_hover_dm_read" data-user-ids-string="{{user_ids_string}}" data-tippy-content="{{t 'Mark as read' }}" role="button" tabindex="0" aria-label="{{t 'Mark as read' }}">{{unread_count}}</span>
|
||||||
|
{{else if is_topic}}
|
||||||
|
{{!-- Invisible user circle element for alignment of topic text with DM user name --}}
|
||||||
|
<span class="user_circle_green user_circle invisible"></span>
|
||||||
|
<div class="inbox-topic-name">
|
||||||
|
<a tabindex="-1" href="{{topic_url}}">{{topic_name}}</a>
|
||||||
|
</div>
|
||||||
|
<span class="unread_count tippy-zulip-tooltip on_hover_topic_read"
|
||||||
|
data-stream-id="{{stream_id}}" data-topic-name="{{topic_name}}"
|
||||||
|
data-tippy-content="{{t 'Mark as read' }}" role="button" tabindex="0" aria-label="{{t 'Mark as read' }}">
|
||||||
|
{{unread_count}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{{#each topics_dict }}
|
||||||
|
<div id="{{@key}}">
|
||||||
|
{{> inbox_row (lookup ../streams_dict @key)}}
|
||||||
|
<div class="inbox-topic-container">
|
||||||
|
{{#each this}}
|
||||||
|
{{>inbox_row this}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<div id="inbox-stream-header-{{stream_id}}" class="inbox-header {{#if is_hidden}}hidden_by_filters{{/if}}" tabindex="0" data-stream-id="{{stream_id}}" style="background: {{stream_header_color}};">
|
||||||
|
<div class="inbox-left-part-wrapper">
|
||||||
|
<div class="collapsible-button"><i class="zulip-icon zulip-icon-arrow-down toggle-inbox-header-icon"></i></div>
|
||||||
|
<div class="inbox-left-part">
|
||||||
|
<div tabindex="0" class="inbox-header-name">
|
||||||
|
<span class="stream-privacy-original-color-{{stream_id}} stream-privacy filter-icon" style="color: {{stream_color}}">
|
||||||
|
{{> ../stream_privacy }}
|
||||||
|
</span>
|
||||||
|
<a tabindex="-1" href="{{stream_url}}">{{stream_name}}</a>
|
||||||
|
</div>
|
||||||
|
<span class="unread_count tippy-zulip-tooltip on_hover_topic_read" data-stream-id="{{stream_id}}" data-tippy-content="{{t 'Mark as read' }}" role="button" tabindex="0" aria-label="{{t 'Mark as read' }}">{{unread_count}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<div id="inbox-main" class="no-select">
|
||||||
|
<div class="search_group" id="inbox-filters" role="group">
|
||||||
|
<i class="zulip-icon zulip-icon-search-inbox"></i>
|
||||||
|
<input type="text" id="{{INBOX_SEARCH_ID}}" value="{{search_val}}" autocomplete="off" placeholder="{{t 'Filter' }}" />
|
||||||
|
<button data-filter="include_muted" id="{{MUTED_FILTER_ID}}" type="button" class="btn btn-default btn-inbox-filter {{#if is_spectator}}fake_disabled_button{{/if}}" role="checkbox" aria-checked="false" tabindex="0">
|
||||||
|
<i id="inbox_filter_mute_toggle" class="fa fa-square-o"></i>
|
||||||
|
{{t 'Include muted' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="inbox-list" data-simplebar>
|
||||||
|
{{> inbox_list}}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -13,6 +13,17 @@
|
||||||
</a>
|
</a>
|
||||||
<span class="arrow all-messages-sidebar-menu-icon hidden-for-spectators"><i class="zulip-icon zulip-icon-ellipsis-v-solid" aria-hidden="true"></i></span>
|
<span class="arrow all-messages-sidebar-menu-icon hidden-for-spectators"><i class="zulip-icon zulip-icon-ellipsis-v-solid" aria-hidden="true"></i></span>
|
||||||
</li>
|
</li>
|
||||||
|
{{#if development_environment}}
|
||||||
|
<li class="top_left_inbox top_left_row hidden-for-spectators" title="{{t 'Inbox' }} (t)">
|
||||||
|
<a href="#inbox">
|
||||||
|
<span class="filter-icon">
|
||||||
|
<i class="zulip-icon zulip-icon-inbox" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
{{~!-- squash whitespace --~}}
|
||||||
|
<span>{{t 'Inbox' }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
<li class="top_left_recent_view top_left_row">
|
<li class="top_left_recent_view top_left_row">
|
||||||
<a href="#recent" class="tippy-left-sidebar-tooltip global-filter-container" data-tooltip-template-id="recent-conversations-tooltip-template">
|
<a href="#recent" class="tippy-left-sidebar-tooltip global-filter-container" data-tooltip-template-id="recent-conversations-tooltip-template">
|
||||||
<span class="filter-icon">
|
<span class="filter-icon">
|
||||||
|
|
|
@ -273,7 +273,7 @@ run_test("allow normal typing when processing text", ({override, override_rewire
|
||||||
// Unmapped keys should immediately return false, without
|
// Unmapped keys should immediately return false, without
|
||||||
// calling any functions outside of hotkey.js.
|
// calling any functions outside of hotkey.js.
|
||||||
assert_unmapped("bfoyz");
|
assert_unmapped("bfoyz");
|
||||||
assert_unmapped("BEFHILNOQTWXYZ");
|
assert_unmapped("BEFHILNOQWXYZ");
|
||||||
|
|
||||||
// All letters should return false if we are composing text.
|
// All letters should return false if we are composing text.
|
||||||
override_rewire(hotkey, "processing_text", () => true);
|
override_rewire(hotkey, "processing_text", () => true);
|
||||||
|
|
|
@ -41,6 +41,7 @@ run_test("narrowing", () => {
|
||||||
assert.ok(!$(".top_left_mentions").hasClass("active-filter"));
|
assert.ok(!$(".top_left_mentions").hasClass("active-filter"));
|
||||||
assert.ok(!$(".top_left_starred_messages").hasClass("active-filter"));
|
assert.ok(!$(".top_left_starred_messages").hasClass("active-filter"));
|
||||||
assert.ok(!$(".top_left_recent_view").hasClass("active-filter"));
|
assert.ok(!$(".top_left_recent_view").hasClass("active-filter"));
|
||||||
|
assert.ok(!$(".top_left_inbox").hasClass("active-filter"));
|
||||||
|
|
||||||
set_global("setTimeout", (f) => {
|
set_global("setTimeout", (f) => {
|
||||||
f();
|
f();
|
||||||
|
@ -49,7 +50,16 @@ run_test("narrowing", () => {
|
||||||
assert.ok(!$(".top_left_all_messages").hasClass("active-filter"));
|
assert.ok(!$(".top_left_all_messages").hasClass("active-filter"));
|
||||||
assert.ok(!$(".top_left_mentions").hasClass("active-filter"));
|
assert.ok(!$(".top_left_mentions").hasClass("active-filter"));
|
||||||
assert.ok(!$(".top_left_starred_messages").hasClass("active-filter"));
|
assert.ok(!$(".top_left_starred_messages").hasClass("active-filter"));
|
||||||
|
assert.ok(!$(".top_left_inbox").hasClass("active-filter"));
|
||||||
assert.ok($(".top_left_recent_view").hasClass("active-filter"));
|
assert.ok($(".top_left_recent_view").hasClass("active-filter"));
|
||||||
|
|
||||||
|
left_sidebar_navigation_area.handle_narrow_deactivated();
|
||||||
|
left_sidebar_navigation_area.highlight_inbox_view();
|
||||||
|
assert.ok(!$(".top_left_all_messages").hasClass("active-filter"));
|
||||||
|
assert.ok(!$(".top_left_mentions").hasClass("active-filter"));
|
||||||
|
assert.ok(!$(".top_left_starred_messages").hasClass("active-filter"));
|
||||||
|
assert.ok(!$(".top_left_recent_view").hasClass("active-filter"));
|
||||||
|
assert.ok($(".top_left_inbox").hasClass("active-filter"));
|
||||||
});
|
});
|
||||||
|
|
||||||
run_test("update_count_in_dom", () => {
|
run_test("update_count_in_dom", () => {
|
||||||
|
|
Loading…
Reference in New Issue