From 9a7634d5276bc047f769d5325d7dd718b8c6301d Mon Sep 17 00:00:00 2001 From: Prakhar Pratyush Date: Tue, 5 Mar 2024 16:33:05 +0530 Subject: [PATCH] onboarding: Show one-time modal to introduce inbox view. To improve onboarding experience, this commit adds a one-time modal which introduces the inbox view. Users see this one-time modal on visiting the inbox view. Fixes part of #29073. --- tools/lib/capitalization.py | 2 ++ web/src/inbox_ui.js | 23 ++++++++++++ web/styles/modal.css | 15 ++++++++ web/templates/introduce_zulip_view_modal.hbs | 38 ++++++++++++++++++++ zerver/lib/hotspots.py | 3 ++ zerver/tests/test_hotspots.py | 5 +-- 6 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 web/templates/introduce_zulip_view_modal.hbs diff --git a/tools/lib/capitalization.py b/tools/lib/capitalization.py index 3744880548..fe2ea054e6 100644 --- a/tools/lib/capitalization.py +++ b/tools/lib/capitalization.py @@ -16,6 +16,7 @@ IGNORED_PHRASES = [ r"Cookie Bot", r"DevAuthBackend", r"DSN", + r"Esc", r"GCM", r"GitHub", r"Gravatar", @@ -23,6 +24,7 @@ IGNORED_PHRASES = [ r"HTTP", r"ID", r"IDs", + r"Inbox", r"IP", r"JSON", r"Kerberos", diff --git a/web/src/inbox_ui.js b/web/src/inbox_ui.js index a2f48acc86..15f3b26f81 100644 --- a/web/src/inbox_ui.js +++ b/web/src/inbox_ui.js @@ -4,19 +4,23 @@ 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 render_introduce_zulip_view_modal from "../templates/introduce_zulip_view_modal.hbs"; import render_user_with_status_icon from "../templates/user_with_status_icon.hbs"; import * as buddy_data from "./buddy_data"; import * as compose_closed_ui from "./compose_closed_ui"; import * as compose_state from "./compose_state"; +import * as dialog_widget from "./dialog_widget"; import * as dropdown_widget from "./dropdown_widget"; import * as hash_util from "./hash_util"; +import {$t_html} from "./i18n"; 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 modals from "./modals"; +import * as onboarding_steps from "./onboarding_steps"; import * as overlays from "./overlays"; import * as people from "./people"; import * as popovers from "./popovers"; @@ -26,6 +30,7 @@ import * as stream_data from "./stream_data"; import * as sub_store from "./sub_store"; import * as unread from "./unread"; import * as unread_ops from "./unread_ops"; +import {user_settings} from "./user_settings"; import * as user_status from "./user_status"; import * as user_topics from "./user_topics"; import * as user_topics_ui from "./user_topics_ui"; @@ -92,6 +97,24 @@ export function show() { set_visible, complete_rerender, }); + + if (onboarding_steps.ONE_TIME_NOTICES_TO_DISPLAY.has("intro_inbox_view_modal")) { + const html_body = render_introduce_zulip_view_modal({ + zulip_view: "inbox", + current_home_view_and_escape_navigation_enabled: + user_settings.web_home_view === "inbox" && + user_settings.web_escape_navigates_to_home_view, + }); + dialog_widget.launch({ + html_heading: $t_html({defaultMessage: "Welcome to your inbox!"}), + html_body, + html_submit_button: $t_html({defaultMessage: "Continue"}), + on_click() {}, + single_footer_button: true, + focus_submit_on_open: true, + }); + onboarding_steps.post_onboarding_step_as_read("intro_inbox_view_modal"); + } } export function hide() { diff --git a/web/styles/modal.css b/web/styles/modal.css index a35e2f59fc..689ff2a00f 100644 --- a/web/styles/modal.css +++ b/web/styles/modal.css @@ -467,3 +467,18 @@ } } } + +#introduce-zulip-view-modal { + i { + vertical-align: middle; + } + + .keyboard-button { + color: var(--color-hotkey-hint); + font-size: 12px; + font-weight: 500; + padding: 2px 4px; + border-radius: 3px; + border: 1px solid var(--color-hotkey-hint); + } +} diff --git a/web/templates/introduce_zulip_view_modal.hbs b/web/templates/introduce_zulip_view_modal.hbs new file mode 100644 index 0000000000..d6ae60a13a --- /dev/null +++ b/web/templates/introduce_zulip_view_modal.hbs @@ -0,0 +1,38 @@ +
+

+ {{#if (eq zulip_view "inbox")}} + {{#tr}}The inbox view provides an overview of your conversations with unread messages.{{/tr}} + {{/if}} + {{#tr}} + In Zulip, a conversation is either a direct message + thread, or a topic in a stream. + {{#*inline "z-link-direct-message"}}{{> @partial-block }}{{/inline}} + {{#*inline "z-link-stream-topic"}}{{> @partial-block }}{{/inline}} + {{/tr}} + {{#if (eq zulip_view "inbox")}} + {{t 'The colored bars indicate which stream the conversation is in.' }} + {{/if}} +

+

+ {{t 'Click on a conversation to view it. To return here, you can:'}} +

+

+
diff --git a/zerver/lib/hotspots.py b/zerver/lib/hotspots.py index df173d9ce1..3f0c9769fa 100644 --- a/zerver/lib/hotspots.py +++ b/zerver/lib/hotspots.py @@ -83,6 +83,9 @@ ONE_TIME_NOTICES: List[OneTimeNotice] = [ OneTimeNotice( name="visibility_policy_banner", ), + OneTimeNotice( + name="intro_inbox_view_modal", + ), ] # We would most likely implement new hotspots in the future that aren't diff --git a/zerver/tests/test_hotspots.py b/zerver/tests/test_hotspots.py index 766b49b5d2..2a0d5d9b0a 100644 --- a/zerver/tests/test_hotspots.py +++ b/zerver/tests/test_hotspots.py @@ -39,9 +39,10 @@ class TestGetNextOnboardingSteps(ZulipTestCase): do_mark_onboarding_step_as_read(self.user, "intro_streams") do_mark_onboarding_step_as_read(self.user, "intro_compose") onboarding_steps = get_next_onboarding_steps(self.user) - self.assert_length(onboarding_steps, 2) + self.assert_length(onboarding_steps, 3) self.assertEqual(onboarding_steps[0]["name"], "visibility_policy_banner") - self.assertEqual(onboarding_steps[1]["name"], "intro_topics") + self.assertEqual(onboarding_steps[1]["name"], "intro_inbox_view_modal") + self.assertEqual(onboarding_steps[2]["name"], "intro_topics") def test_all_onboarding_steps_done(self) -> None: with self.settings(TUTORIAL_ENABLED=True):