display_settings: Allow user to set default_view.

TextField is used to allow users to set long stream + topic narrow
names in the urls.

We currently restrict users to only set "all_messages" and
"recent_topics" as narrows.

This commit achieves 3 things:
* Removes recent topics as the default view which loads when
  hash is empty.
* Loads default_view when hash is empty.
* Loads default_view on pressing escape key when it is unhandled by
  other present UI elements.

NOTE: After this commit loading zulip with an empty hash will
automatically set hash to default_view.  Ideally, we'd just display
the default view without a hash, but that involves extra complexity.

One exception is when user is trying to load an overlay directly,
i.e. zulip is loaded with an overlay hash. In this case,
we render recent topics is background irrespective of default_view.

We consider this last detail to be a bug not important enough to block
adding this setting.
This commit is contained in:
Aman Agrawal 2021-03-10 12:56:10 +00:00 committed by Tim Abbott
parent 709fbe5c0a
commit e587c029f6
25 changed files with 160 additions and 13 deletions

View File

@ -676,6 +676,20 @@ run_test("update_display_settings", (override) => {
assert(page_params.color_scheme, 3); assert(page_params.color_scheme, 3);
} }
{
event = event_fixtures.update_display_settings__default_view_recent_topics;
page_params.default_view = "all_messages";
dispatch(event);
assert(page_params.default_view, "recent_topics");
}
{
event = event_fixtures.update_display_settings__default_view_all_messages;
page_params.default_view = "recent_topics";
dispatch(event);
assert(page_params.default_view, "all_messages");
}
{ {
const stub = make_stub(); const stub = make_stub();
event = event_fixtures.update_display_settings__color_scheme_automatic; event = event_fixtures.update_display_settings__color_scheme_automatic;

View File

@ -610,6 +610,20 @@ exports.fixtures = {
user: test_user.email, user: test_user.email,
}, },
update_display_settings__default_view_all_messages: {
type: "update_display_settings",
setting_name: "default_view",
setting: 1,
user: test_user.email,
},
update_display_settings__default_view_recent_topics: {
type: "update_display_settings",
setting_name: "default_view",
setting: "recent_topics",
user: test_user.email,
},
update_display_settings__demote_inactive_streams: { update_display_settings__demote_inactive_streams: {
type: "update_display_settings", type: "update_display_settings",
setting_name: "demote_inactive_streams", setting_name: "demote_inactive_streams",

View File

@ -59,7 +59,7 @@ function maybe_hide_recent_topics() {
} }
export function in_recent_topics_hash() { export function in_recent_topics_hash() {
return ["recent_topics", "#", ""].includes(window.location.hash); return ["#recent_topics"].includes(window.location.hash);
} }
export function changehash(newhash) { export function changehash(newhash) {
@ -114,6 +114,10 @@ function is_overlay_hash(hash) {
return overlay_list.includes(main_hash); return overlay_list.includes(main_hash);
} }
export function show_default_view() {
window.location.hash = page_params.default_view;
}
// Returns true if this function performed a narrow // Returns true if this function performed a narrow
function do_hashchange_normal(from_reload) { function do_hashchange_normal(from_reload) {
message_viewport.stop_auto_scrolling(); message_viewport.stop_auto_scrolling();
@ -130,8 +134,7 @@ function do_hashchange_normal(from_reload) {
if (operators === undefined) { if (operators === undefined) {
// If the narrow URL didn't parse, clear // If the narrow URL didn't parse, clear
// window.location.hash and send them to the home tab // window.location.hash and send them to the home tab
set_hash("#all_messages"); show_default_view();
activate_home_tab();
return false; return false;
} }
const narrow_opts = { const narrow_opts = {
@ -152,6 +155,8 @@ function do_hashchange_normal(from_reload) {
} }
case "": case "":
case "#": case "#":
show_default_view();
break;
case "#recent_topics": case "#recent_topics":
recent_topics.show(); recent_topics.show();
break; break;
@ -176,6 +181,9 @@ function do_hashchange_overlay(old_hash) {
if (old_hash === undefined) { if (old_hash === undefined) {
// User directly requested to open an overlay. // User directly requested to open an overlay.
// We need to show recent topics in the background. // We need to show recent topics in the background.
// Even though recent topics may not be the default view
// here, we show it because we need to show a view in
// background and recent topics seems preferrable for that.
recent_topics.show(); recent_topics.show();
} }
const base = hash_util.get_hash_category(window.location.hash); const base = hash_util.get_hash_category(window.location.hash);

View File

@ -305,7 +305,7 @@ export function process_escape_key(e) {
return true; return true;
} }
hashchange.go_to_location(""); hashchange.show_default_view();
return true; return true;
} }
@ -770,7 +770,7 @@ export function process_hotkey(e, hotkey) {
narrow.narrow_to_next_pm_string(); narrow.narrow_to_next_pm_string();
return true; return true;
case "open_recent_topics": case "open_recent_topics":
hashchange.go_to_location("#"); hashchange.go_to_location("#recent_topics");
return true; return true;
case "all_messages": case "all_messages":
hashchange.go_to_location("#all_messages"); hashchange.go_to_location("#all_messages");

View File

@ -564,10 +564,13 @@ export function change_focused_element(e, input_key) {
} }
switch (input_key) { switch (input_key) {
// Allow broswer to handle all
// character keypresses.
case "vim_left": case "vim_left":
case "vim_right": case "vim_right":
case "vim_down": case "vim_down":
case "vim_up": case "vim_up":
case "open_recent_topics":
return false; return false;
case "shift_tab": case "shift_tab":
current_focus_elem = filter_buttons().last(); current_focus_elem = filter_buttons().last();

View File

@ -445,6 +445,7 @@ export function dispatch_normal_event(event) {
const user_display_settings = [ const user_display_settings = [
"color_scheme", "color_scheme",
"default_language", "default_language",
"default_view",
"demote_inactive_streams", "demote_inactive_streams",
"dense_mode", "dense_mode",
"emojiset", "emojiset",

View File

@ -83,6 +83,7 @@ export function build_page() {
settings_label, settings_label,
demote_inactive_streams_values: settings_config.demote_inactive_streams_values, demote_inactive_streams_values: settings_config.demote_inactive_streams_values,
color_scheme_values: settings_config.color_scheme_values, color_scheme_values: settings_config.color_scheme_values,
default_view_values: settings_config.default_view_values,
twenty_four_hour_time_values: settings_config.twenty_four_hour_time_values, twenty_four_hour_time_values: settings_config.twenty_four_hour_time_values,
general_settings: settings_config.all_notifications().general_settings, general_settings: settings_config.all_notifications().general_settings,
notification_settings: settings_config.all_notifications().settings, notification_settings: settings_config.all_notifications().settings,

View File

@ -28,6 +28,17 @@ export const demote_inactive_streams_values = {
}, },
}; };
export const default_view_values = {
recent_topics: {
code: "recent_topics",
description: i18n.t("Recent topics"),
},
all_messages: {
code: "all_messages",
description: i18n.t("All messages"),
},
};
export const color_scheme_values = { export const color_scheme_values = {
automatic: { automatic: {
code: 1, code: 1,

View File

@ -42,6 +42,8 @@ export function set_up() {
$("#color_scheme").val(page_params.color_scheme); $("#color_scheme").val(page_params.color_scheme);
$("#default_view").val(page_params.default_view);
$("#twenty_four_hour_time").val(JSON.stringify(page_params.twenty_four_hour_time)); $("#twenty_four_hour_time").val(JSON.stringify(page_params.twenty_four_hour_time));
$(`.emojiset_choice[value="${CSS.escape(page_params.emojiset)}"]`).prop("checked", true); $(`.emojiset_choice[value="${CSS.escape(page_params.emojiset)}"]`).prop("checked", true);
@ -109,6 +111,11 @@ export function set_up() {
change_display_setting(data, "#display-settings-status"); change_display_setting(data, "#display-settings-status");
}); });
$("#default_view").on("change", function () {
const data = {default_view: JSON.stringify(this.value)};
change_display_setting(data, "#display-settings-status");
});
$("body").on("click", ".reload_link", () => { $("body").on("click", ".reload_link", () => {
window.location.reload(); window.location.reload();
}); });
@ -179,6 +186,7 @@ export function update_page() {
$("#translate_emoticons").prop("checked", page_params.translate_emoticons); $("#translate_emoticons").prop("checked", page_params.translate_emoticons);
$("#twenty_four_hour_time").val(JSON.stringify(page_params.twenty_four_hour_time)); $("#twenty_four_hour_time").val(JSON.stringify(page_params.twenty_four_hour_time));
$("#color_scheme").val(JSON.stringify(page_params.color_scheme)); $("#color_scheme").val(JSON.stringify(page_params.color_scheme));
$("#default_view").val(page_params.default_view);
// TODO: Set emojiset selector here. // TODO: Set emojiset selector here.
// Longer term, we'll want to automate this function // Longer term, we'll want to automate this function

View File

@ -21,6 +21,15 @@
<h3 class="inline-block">{{t "Display settings" }}</h3> <h3 class="inline-block">{{t "Display settings" }}</h3>
<div class="alert-notification" id="display-settings-status"></div> <div class="alert-notification" id="display-settings-status"></div>
<div class="input-group">
<label for="default_view" class="dropdown-title">{{t "Default view" }}
{{> ../help_link_widget link="/help/change-default-view" }}
</label>
<select name="default_view" id="default_view">
{{> dropdown_options_widget option_values=default_view_values}}
</select>
</div>
<div class="input-group"> <div class="input-group">
<label for="color_scheme" class="dropdown-title">{{t "Color scheme" }} <label for="color_scheme" class="dropdown-title">{{t "Color scheme" }}
</label> </label>

View File

@ -10,14 +10,19 @@ below features are supported.
## Changes in Zulip 4.0 ## Changes in Zulip 4.0
**Feature level 42**
* `PATCH /settings/display`: Added a new `default_view` setting allowing
the user to [set the default view](/help/change-default-view).
**Feature level 41** **Feature level 41**
* [`GET /events`](/api/get-events): Remove name field from update * [`GET /events`](/api/get-events): Removed `name` field from update
subscription events. subscription events.
**Feature level 40** **Feature level 40**
* [`GET /events`](/api/get-events): Remove email field from update * [`GET /events`](/api/get-events): Removed `email` field from update
subscription events. subscription events.
**Feature level 39** **Feature level 39**

View File

@ -52,6 +52,10 @@
<td class="definition">{% trans %}Show keyboard shortcuts{% endtrans %}</td> <td class="definition">{% trans %}Show keyboard shortcuts{% endtrans %}</td>
<td><span class="hotkey"><kbd>?</kbd></span></td> <td><span class="hotkey"><kbd>?</kbd></span></td>
</tr> </tr>
<tr>
<td class="definition">{% trans %}Go to default view{% endtrans %}</td>
<td><span class="hotkey"><kbd>Esc</kbd> or <kbd>Ctrl</kbd> + <kbd>[</kbd></span></td>
</tr>
</table> </table>
</div> </div>
<div> <div>
@ -241,7 +245,7 @@
</thead> </thead>
<tr> <tr>
<td class="definition">{% trans %}View recent topics{% endtrans %}</td> <td class="definition">{% trans %}View recent topics{% endtrans %}</td>
<td><span class="hotkey"><kbd>T</kbd> or <kbd>Esc</kbd> or <kbd>Ctrl</kbd> + <kbd>[</kbd></span></td> <td><span class="hotkey"><kbd>T</kbd></span></td>
</tr> </tr>
<tr> <tr>
<td class="definition">{% trans %}Search recent topics{% endtrans %}</td> <td class="definition">{% trans %}Search recent topics{% endtrans %}</td>

View File

@ -57,7 +57,7 @@
<span class="arrow starred-messages-sidebar-menu-icon"><i class="zulip-icon ellipsis-v-solid" aria-hidden="true"></i></span> <span class="arrow starred-messages-sidebar-menu-icon"><i class="zulip-icon ellipsis-v-solid" aria-hidden="true"></i></span>
</li> </li>
<li class="top_left_recent_topics top_left_row" title="{{ _('Recent topics') }} (t)"> <li class="top_left_recent_topics top_left_row" title="{{ _('Recent topics') }} (t)">
<a href="#"> <a href="#recent_topics">
<span class="filter-icon"> <span class="filter-icon">
<i class="fa fa-clock-o" aria-hidden="true"></i> <i class="fa fa-clock-o" aria-hidden="true"></i>
</span> </span>

View File

@ -0,0 +1,26 @@
# Change default view
The default view in Zulip (i.e. what view you reach after logging in
to the Zulip webapp or hitting the `Esc` keyboard shortcut repeatedly)
can be configured. By default, **Recent topics** is the default view;
the previous default, **All messages**, is also supported.
[Contact us](/help/contact-support) if you'd like to be able to
configure a different view as the default.
### Change default view
{start_tabs}
{settings_tab|display-settings}
2. Under **Display Settings**, click on the **Default view** dropdown.
3. Select a view.
4. Open a new Zulip tab or press `Esc` twice (first to exit
"Settings", and again to return to the default view) to see your
changes in action.
{end_tabs}

View File

@ -14,6 +14,7 @@
* [Review your settings](/help/review-your-settings) * [Review your settings](/help/review-your-settings)
* [Change your profile picture](/help/change-your-profile-picture) * [Change your profile picture](/help/change-your-profile-picture)
* [Change your language](/help/change-your-language) * [Change your language](/help/change-your-language)
* [Change default view](/help/change-default-view)
* [Use 24-hour time](/help/change-the-time-format) * [Use 24-hour time](/help/change-the-time-format)
* [Joining an organization](/help/join-a-zulip-organization) * [Joining an organization](/help/join-a-zulip-organization)
* [Switching between organizations](/help/switching-between-organizations) * [Switching between organizations](/help/switching-between-organizations)

View File

@ -41,6 +41,9 @@ below, and add more to your repertoire as needed.
* **Toggle keyboard shortcuts view**: `?` * **Toggle keyboard shortcuts view**: `?`
* **Go to default view**: Press `Esc` or `Ctrl + [` until you are in
the [default view](/help/change-default-view).
## Navigation ## Navigation
* **Search messages**: `/` or `Ctrl+k` * **Search messages**: `/` or `Ctrl+k`
@ -139,9 +142,9 @@ title="thumbs up"/>**: `+`
## Recent topics ## Recent topics
* **View recent topics**: `t` or `Esc` or `Ctrl` + `[` * **View recent topics**: `t`
* **Search recent topics**: `t` * **Search recent topics**: `t`
* **Escape from recent topics search**: `esc` or arrow keys * **Escape from recent topics search**: `Esc` or arrow keys
* **Navigate recent topics**: Use arrow keys or vim keys (`j`, `k`, `l`, `h`). * **Navigate recent topics**: Use arrow keys or vim keys (`j`, `k`, `l`, `h`).
Use `Enter` to engage with elements. Use `Enter` to engage with elements.

View File

@ -30,7 +30,7 @@ DESKTOP_WARNING_VERSION = "5.2.0"
# #
# Changes should be accompanied by documentation explaining what the # Changes should be accompanied by documentation explaining what the
# new level means in templates/zerver/api/changelog.md. # new level means in templates/zerver/api/changelog.md.
API_FEATURE_LEVEL = 41 API_FEATURE_LEVEL = 42
# Bump the minor PROVISION_VERSION to indicate that folks should provision # Bump the minor PROVISION_VERSION to indicate that folks should provision
# only when going from an old version of the code to a newer version. Bump # only when going from an old version of the code to a newer version. Bump
@ -45,4 +45,4 @@ API_FEATURE_LEVEL = 41
# historical commits sharing the same major version, in which case a # historical commits sharing the same major version, in which case a
# minor version bump suffices. # minor version bump suffices.
PROVISION_VERSION = "132.0" PROVISION_VERSION = "133.0"

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1.7 on 2021-03-10 04:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("zerver", "0310_jsonfield"),
]
operations = [
migrations.AddField(
model_name="userprofile",
name="default_view",
field=models.TextField(default="recent_topics"),
),
]

View File

@ -1153,6 +1153,9 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
# display settings # display settings
default_language: str = models.CharField(default="en", max_length=MAX_LANGUAGE_ID_LENGTH) default_language: str = models.CharField(default="en", max_length=MAX_LANGUAGE_ID_LENGTH)
# This setting controls which view is rendered first when Zulip loads.
# Values for it are URL suffix after `#`.
default_view: str = models.TextField(default="recent_topics")
dense_mode: bool = models.BooleanField(default=True) dense_mode: bool = models.BooleanField(default=True)
fluid_layout_width: bool = models.BooleanField(default=False) fluid_layout_width: bool = models.BooleanField(default=False)
high_contrast_mode: bool = models.BooleanField(default=False) high_contrast_mode: bool = models.BooleanField(default=False)
@ -1244,6 +1247,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
property_types = dict( property_types = dict(
color_scheme=int, color_scheme=int,
default_language=str, default_language=str,
default_view=str,
demote_inactive_streams=int, demote_inactive_streams=int,
dense_mode=bool, dense_mode=bool,
emojiset=str, emojiset=str,

View File

@ -6725,6 +6725,15 @@ paths:
Present if `update_display_settings` is present in `fetch_event_types`. Present if `update_display_settings` is present in `fetch_event_types`.
Whether the user has chosen for the layout width to be fluid. Whether the user has chosen for the layout width to be fluid.
default_view:
type: string
description: |
Present if `update_display_settings` is present in `fetch_event_types`.
The [default view](/help/change-default-view) in Zulip, represented
as the URL suffix after `#` to be rendered when Zulip loads.
Currently supported values are `all_messages` and `recent_topics`.
high_contrast_mode: high_contrast_mode:
type: boolean type: boolean
description: | description: |

View File

@ -1863,6 +1863,7 @@ class UserDisplayActionTest(BaseAction):
test_changes: Dict[str, Any] = dict( test_changes: Dict[str, Any] = dict(
emojiset=["twitter"], emojiset=["twitter"],
default_language=["es", "de", "en"], default_language=["es", "de", "en"],
default_view=["all_messages", "recent_topics"],
timezone=["America/Denver", "Pacific/Pago_Pago", "Pacific/Galapagos", ""], timezone=["America/Denver", "Pacific/Pago_Pago", "Pacific/Galapagos", ""],
demote_inactive_streams=[2, 3, 1], demote_inactive_streams=[2, 3, 1],
color_scheme=[2, 3, 1], color_scheme=[2, 3, 1],

View File

@ -53,6 +53,7 @@ class HomeTest(ZulipTestCase):
"debug_mode", "debug_mode",
"default_language", "default_language",
"default_language_name", "default_language_name",
"default_view",
"delivery_email", "delivery_email",
"demote_inactive_streams", "demote_inactive_streams",
"dense_mode", "dense_mode",

View File

@ -336,6 +336,7 @@ class ChangeSettingsTest(ZulipTestCase):
test_changes: Dict[str, Any] = dict( test_changes: Dict[str, Any] = dict(
default_language="de", default_language="de",
default_view="all_messages",
emojiset="google", emojiset="google",
timezone="US/Mountain", timezone="US/Mountain",
demote_inactive_streams=2, demote_inactive_streams=2,

View File

@ -1183,6 +1183,7 @@ class UserProfileTest(ZulipTestCase):
hamlet.color_scheme = UserProfile.COLOR_SCHEME_LIGHT hamlet.color_scheme = UserProfile.COLOR_SCHEME_LIGHT
cordelia.default_language = "de" cordelia.default_language = "de"
cordelia.default_view = "all_messages"
cordelia.emojiset = "twitter" cordelia.emojiset = "twitter"
cordelia.timezone = "America/Phoenix" cordelia.timezone = "America/Phoenix"
cordelia.color_scheme = UserProfile.COLOR_SCHEME_NIGHT cordelia.color_scheme = UserProfile.COLOR_SCHEME_NIGHT

View File

@ -180,6 +180,7 @@ def json_change_settings(
emojiset_choices = {emojiset["key"] for emojiset in UserProfile.emojiset_choices()} emojiset_choices = {emojiset["key"] for emojiset in UserProfile.emojiset_choices()}
default_view_options = ["recent_topics", "all_messages"]
@human_users_only @human_users_only
@ -197,6 +198,9 @@ def update_display_settings_backend(
), ),
translate_emoticons: Optional[bool] = REQ(validator=check_bool, default=None), translate_emoticons: Optional[bool] = REQ(validator=check_bool, default=None),
default_language: Optional[str] = REQ(validator=check_string, default=None), default_language: Optional[str] = REQ(validator=check_string, default=None),
default_view: Optional[str] = REQ(
validator=check_string_in(default_view_options), default=None
),
left_side_userlist: Optional[bool] = REQ(validator=check_bool, default=None), left_side_userlist: Optional[bool] = REQ(validator=check_bool, default=None),
emojiset: Optional[str] = REQ(validator=check_string_in(emojiset_choices), default=None), emojiset: Optional[str] = REQ(validator=check_string_in(emojiset_choices), default=None),
demote_inactive_streams: Optional[int] = REQ( demote_inactive_streams: Optional[int] = REQ(