Zulip Onboarding Steps 子系统解析:一次性引导提示的配置、展示与已读机制
Zulip Onboarding Steps 子系统解析一次性引导提示的配置、展示与已读机制【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulip导读Zulip 的 Onboarding Steps 是一套轻量级的用户引导机制用于在用户首次接触某些不易自明not self-evident的 UI 元素时通过一次性横幅banner或弹窗modal提供上下文说明。本文以 docs/subsystems/onboarding-steps.md 为核心骨架结合服务端、前端与测试源码完整讲解如何为 Zulip 配置一个新的引导步骤、服务端如何决定下发哪些步骤、前端如何判断展示并回写已读状态以及这条链路在源码中的完整实现。读完本文你将掌握从注册引导步骤名到标记已读的全流程开发方法并理解其中涉及的后端模型、事件同步、API 端点和重试机制。什么是 Onboarding StepsOnboarding Steps 是 Zulip 中一类一次性one-time提示每个用户只会看到一次用于引导用户注意到某个重要的 UI 元素或功能入口。当前实现下引导步骤以banner横幅或modal弹窗两种形态呈现文档也指出历史上还曾有过 hotspots热点高亮这种引导形式但现已不再提供。这类引导特别适合 Zulip 这类功能密集的团队协作产品——例如收件箱视图最近会话视图话题已解决等概念对新手并不直观通过一次性提示可以显著降低学习成本而不会像常驻教程那样打扰老用户。该子系统在前端和后端各有一个核心入口文件服务端注册中心zerver/lib/onboarding_steps.py前端展示与已读逻辑web/src/onboarding_steps.ts核心数据模型与步骤注册中心持久化模型OnboardingStep每个用户的已读步骤持久化在 Django 模型 zerver/models/onboarding_steps.py 中class OnboardingStep(models.Model): user models.ForeignKey(UserProfile, on_deleteCASCADE) onboarding_step models.CharField(max_length40) timestamp models.DateTimeField(defaulttimezone_now) class Meta: unique_together (user, onboarding_step)关键约束有两点onboarding_step字段最长40 个字符自定义步骤名时需控制长度(user, onboarding_step)联合唯一同一用户对同一步骤只保留一条记录天然保证了一次性语义。步骤的两种类型在 zerver/lib/onboarding_steps.py 中定义了三个 dataclassdataclass class APIOnboardingStep: type: str name: str dataclass class OneTimeNotice: name: str def to_dict(self) - APIOnboardingStep: return APIOnboardingStep(typeone_time_notice, nameself.name) dataclass class OneTimeAction: name: str def to_dict(self) - APIOnboardingStep: return APIOnboardingStep(typeone_time_action, nameself.name)OneTimeNotice一次性提示对应 banner/modal 类提示前端仅负责展示一次。OneTimeAction一次性动作对应需要在展示前主动执行的动作流程例如自动跳转到欢迎机器人的私信会话。两者对外API / 前端都序列化为APIOnboardingStep仅type字段不同one_time_notice或one_time_action。前端对应的 schema 校验定义在 web/src/state_data.tsconst one_time_notice_schema z.object({ name: z.string(), type: z.literal(one_time_notice), }); const one_time_action_schema z.object({ name: z.string(), type: z.literal(one_time_action), }); export const onboarding_step_schema z.union([one_time_notice_schema, one_time_action_schema]);当前已注册的步骤清单服务端维护了全量步骤注册表zerver/lib/onboarding_steps.pyALL_ONBOARDING_STEPS ONE_TIME_NOTICES ONE_TIME_ACTIONS类型name步骤名前端消费位置one_time_noticevisibility_policy_bannerweb/src/compose.tsone_time_noticeintro_inbox_view_modalweb/src/inbox_ui.tsone_time_noticeintro_recent_view_modalweb/src/recent_view_ui.tsone_time_noticefirst_stream_created_bannerweb/src/stream_create.tsone_time_noticejump_to_conversation_bannerweb/src/compose_notifications.tsone_time_noticenon_interleaved_view_messages_fadingweb/src/compose_notifications.tsone_time_noticeinterleaved_view_messages_fadingweb/src/compose_notifications.tsone_time_noticeintro_resolve_topicweb/src/message_edit.tsone_time_noticenavigation_tour_videoweb/src/onboarding_steps.tsone_time_noticeintro_go_to_conversation_tooltipweb/src/compose_recipient.tsone_time_actionnarrow_to_dm_with_welcome_bot_new_userweb/src/onboarding_steps.ts可见当前引导覆盖了收件箱视图、最近会话、创建频道、话题已解决、消息淡出提示、导航导览视频等主要新手触点。三步配置一个新 Onboarding Step原文档给出了非常简洁的三步流程这里结合真实源码逐一展开。Step 1注册引导步骤名打开 zerver/lib/onboarding_steps.py向ONE_TIME_NOTICES列表追加一项ONE_TIME_NOTICES: list[OneTimeNotice] [ ... OneTimeNotice( nameProvide a concise name, ), ]几个实践要点命名要简短名称会写入OnboardingStep.onboarding_step字段数据库层面限制为 40 字符保持可读性从现有清单看命名惯例是意图 形态如intro_inbox_view_modal表示收件箱视图介绍弹窗first_stream_created_banner表示首次创建频道横幅必须在注册表中存在后端视图 zerver/views/onboarding_steps.py 会校验提交的步骤名是否存在于ALL_ONBOARDING_STEPS否则返回错误Unknown onboarding_step: {onboarding_step}如果这个步骤是一次性动作如自动跳转到某视图则追加到ONE_TIME_ACTIONS列表。Step 2展示判断与渲染当承载引导的那个 UI 元素即将出现时在对应前端模块中读取 web/src/onboarding_steps.ts 导出的集合export const ONE_TIME_NOTICES_TO_DISPLAY new Setstring();判断逻辑形如以收件箱视图弹窗为例见 web/src/inbox_ui.tsif (onboarding_steps.ONE_TIME_NOTICES_TO_DISPLAY.has(intro_inbox_view_modal)) { // 展示 intro_inbox_view_modal并在展示完成后标记已读 onboarding_steps.post_onboarding_step_as_read(intro_inbox_view_modal); }ONE_TIME_NOTICES_TO_DISPLAY是服务端下发、前端维护的待展示集合只有出现在集合中的步骤名才需要展示。该集合由update_onboarding_steps_to_display在每次拿到服务端数据时重建web/src/onboarding_steps.ts——它只把type one_time_notice的步骤放入集合one_time_action类型不走此集合而是由initialize直接触发对应动作。Step 3标记为已读提示展示完成后调用 post_onboarding_step_as_readpost_onboarding_step_as_read(intro_inbox_view_modal);该函数会向服务端POST /json/users/me/onboarding_steps提交步骤名成功后服务端持久化记录前端随后通过事件更新ONE_TIME_NOTICES_TO_DISPLAY移除该步骤用户便不会再看到该提示。服务端如何决定下发哪些步骤服务端的核心决策函数是get_next_onboarding_stepszerver/lib/onboarding_steps.pydef get_next_onboarding_steps(user: UserProfile) - list[APIOnboardingStep]: # 若服务端关闭了教程功能则不发送任何引导步骤 if not settings.TUTORIAL_ENABLED: return [] seen_onboarding_steps: list[str] list( OnboardingStep.objects.filter(useruser).values_list(onboarding_step, flatTrue) ) if settings.NAVIGATION_TOUR_VIDEO_URL is None: # 管理员禁用了导航导览视频视为已读 seen_onboarding_steps.append(navigation_tour_video) seen_onboarding_steps_set frozenset(seen_onboarding_steps) onboarding_steps: list[APIOnboardingStep] [] for onboarding_step in ALL_ONBOARDING_STEPS: if onboarding_step.name in seen_onboarding_steps_set: continue onboarding_steps.append(onboarding_step.to_dict()) return onboarding_steps逻辑要点总开关TUTORIAL_ENABLED若为False直接返回空列表任何用户都收不到引导默认值为True见 zproject/default_settings.py已读过滤查询该用户所有已读步骤与全量注册表做差集只下发未读步骤视频步骤特判若管理员把NAVIGATION_TOUR_VIDEO_URL配置为None默认值是官方视频地址见 zproject/default_settings.py则navigation_tour_video直接视为已读不再下发。下发时机有两个均定义在 zerver/lib/events.py 与 zerver/actions/onboarding_steps.py初始注册do_events_register用户加载客户端时state[onboarding_steps]携带全部待展示步骤state[navigation_tour_video_url]携带视频地址实时事件用户标记某步骤已读后服务端向该用户推送typeonboarding_steps事件payload 为重新计算后的剩余步骤列表前端据此更新本地集合web/src/onboarding_steps.ts 及事件处理逻辑。标记已读的完整链路前端带重试的 POSTpost_onboarding_step_as_read内部使用channel.post调用/json/users/me/onboarding_steps并实现了最多 5 次MAX_RETRIES 5的指数退避重试web/src/onboarding_steps.ts服务端返回400步骤名非法几乎不可能发生因为不是用户输入时不重试其他错误使用get_retry_backoff_seconds计算退避时长后setTimeout递归重试。该函数还支持可选的第二参数schedule_navigation_tour_video_reminder_delay仅对navigation_tour_video步骤有效内部有assert校验用于稍后观看场景——延迟若干秒后由欢迎机器人发送一条提醒私信。服务端API 端点与动作函数API 端点为POST /json/users/me/onboarding_steps由 zerver/views/onboarding_steps.py 处理校验onboarding_step存在于ALL_ONBOARDING_STEPS否则抛出JsonableError(Unknown onboarding_step: ...)若携带schedule_navigation_tour_video_reminder_delay则校验步骤必须是navigation_tour_video并通过check_schedule_message调度一条由WELCOME_BOT发送的私信提醒deliver_at now delay调用动作函数do_mark_onboarding_step_as_read落库。动作函数位于 zerver/actions/onboarding_steps.pytransaction.atomic(durableTrue) def do_mark_onboarding_step_as_read(user: UserProfile, onboarding_step: str) - None: OnboardingStep.objects.get_or_create(useruser, onboarding_steponboarding_step) event dict( typeonboarding_steps, onboarding_steps[asdict(step) for step in get_next_onboarding_steps(user)], ) send_event_on_commit(user.realm, event, [user.id])get_or_create保证了幂等性重复标记不会报错也不会产生重复记录事务提交后通过 Tornado 事件系统向该用户推送更新前端update_onboarding_steps_to_display重建集合提示即时消失。一次性动作OneTimeAction与导览视频弹窗自动跳转到欢迎机器人私信narrow_to_dm_with_welcome_bot_new_user是一个典型的OneTimeActionweb/src/onboarding_steps.ts在新用户注册后的首次加载中若该步骤尚未完成前端会自动调用post_onboarding_step_as_read将其标记为已读并判断当前是否处于首页视图——若用户是通过带next参数的链接进入特定视图则尊重用户意图不跳转否则自动窄化narrow到欢迎机器人的私信会话引导新用户发出第一条消息。导航导览视频弹窗navigation_tour_video是形态最复杂的一个提示web/src/onboarding_steps.ts它通过dialog_widget.launch渲染由 web/templates/navigation_tour_video_modal.hbs 生成的弹窗支持跳过视频 / 稍后观看 / 看完三种结束路径Watch later稍后观看点击后以2 * 60 * 60秒2 小时为延迟调用post_onboarding_step_as_read(navigation_tour_video, reminder_delay_seconds)服务端据此调度欢迎机器人的提醒私信跳过或看完弹窗关闭时on_hide若未点击过稍后观看则直接调用post_onboarding_step_as_read(navigation_tour_video)标记已读弹窗关闭后还会把焦点显式移回#compose-textarea避免与消息输入框的焦点竞争导致的不稳定行为。测试与验证该子系统的行为由 zerver/tests/test_onboarding_steps.py 覆盖主要用例包括部分已读、部分未读test_some_done_some_not验证get_next_onboarding_steps只返回未读步骤、顺序与注册表一致且新用户默认已读visibility_policy_banner同时验证TUTORIAL_ENABLEDFalse时返回空列表、NAVIGATION_TOUR_VIDEO_URLNone时排除视频步骤全部已读test_all_onboarding_steps_done遍历ALL_ONBOARDING_STEPS全部标记后get_next_onboarding_steps返回空列表API 端点test_onboarding_steps_url_endpoint直接对/json/users/me/onboarding_steps发 POST 验证落库并验证非法步骤名返回Unknown onboarding_step: invalid提醒调度test_schedule_navigation_tour_video_reminder配合time_machine冻结时间验证schedule_navigation_tour_video_reminder_delay30时生成一条发送者为欢迎机器人、内容含 Welcome to Zulip video 的定时私信且scheduled_timestamp精确等于now 30s。后端主要调用链可概括为前端 post_onboarding_step_as_read(name) └─ POST /json/users/me/onboarding_steps └─ zerver/views/onboarding_steps.py: mark_onboarding_step_as_read ├─ 校验 name ∈ ALL_ONBOARDING_STEPS ├─ (可选) check_schedule_message 调度欢迎机器人提醒私信 └─ zerver/actions/onboarding_steps.py: do_mark_onboarding_step_as_read ├─ OnboardingStep.get_or_create(user, name) └─ send_event_on_commit → 事件 onboarding_steps → 前端更新 ONE_TIME_NOTICES_TO_DISPLAY运维与配置注意事项TUTORIAL_ENABLED默认True见 zproject/default_settings.py服务端总开关设为False后所有用户均不再收到任何引导步骤NAVIGATION_TOUR_VIDEO_URL默认官方视频地址见 zproject/default_settings.py设为None可禁用导览视频弹窗同时该步骤对用户自动视为已读新用户默认行为创建用户时服务端会自动把visibility_policy_banner标记为已读zerver/actions/create_user.py因为该横幅只面向存量用户若用户注册时选择从已有账户导入设置则通过copy_onboarding_stepszerver/lib/onboarding_steps.py 与 zerver/lib/create_user.py把源账户的全部已读步骤复制过来避免老用户在新账户上重复看到提示。总结Zulip 的 Onboarding Steps 子系统用注册表 一次性记录 事件同步三件套实现了轻量、幂等、可扩展的用户引导能力开发一个新提示只需三步——在ONE_TIME_NOTICES注册名称、在 UI 出现处查ONE_TIME_NOTICES_TO_DISPLAY决定展示、展示后调用post_onboarding_step_as_read落库。配合TUTORIAL_ENABLED、NAVIGATION_TOUR_VIDEO_URL两个配置项运维者可以灵活控制引导内容的开启与裁剪。理解这条从注册到已读的完整链路也为你阅读 Zulip 其他依赖事件同步的子系统如通知、未读计数打下了良好基础。【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulip创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →