docs: Split widgets and slash command developer pages.

This commit is contained in:
Abhijeet Prasad Bodas
2021-07-01 15:19:45 +05:30
committed by Steve Howell
parent 706ec9714c
commit 6bbfddb6c3
3 changed files with 96 additions and 108 deletions

View File

@@ -36,3 +36,4 @@ Subsystems documentation
unread_messages
billing
widgets
slash-commands

View File

@@ -0,0 +1,56 @@
# Slash commands
Slash commands are commands (mainly for power users) to quickly do
some stuff from the compose box. The codebase refers to these as "zcommand"s,
and both these terms are often user interchangeably.
Currently supported slash commands are:
- `/day` and `/night` to change the UI theme
- `/ping` to ping to server and get back the time for the round
trip. Mainly for testing.
- `/fluid-width` and `/fixed-width` to toggle that setting
- `/settings` to open the settings page
It is important to distinguish slash commands from the
[widget system](/subsystems/widgets.md). Slash commands essentially
**do not send** messages (and could have very well had their
own "command prompt" (but don't), since they have nothing to do with
message sending), while widgets are special kinds of messages.
### Data flow
These commands have client-side support in `zcommands.js`.
They send commands to the server using the `/json/command`
endpoint.
In the case of "/ping", the server code in `zcommand.py`
basically just acks the client. The client then computes
the round trip time and shows a little message above
the compose box that the user can see and then dismiss.
For commands like "/day" and "/night", the server does
a little bit of logic to toggle the user's night mode
setting, and this is largely done inside `zcommand.py`.
The server sends a very basic response, and then
the client actually changes the display colors. The
client also shows the user a little message above
the compose box instructing them how to reverse the
change.
(It's possible that we don't really need a general
`/json/zcommand` endpoint for these, and we
may decide later to just use custom
API endpoints for each command. There's some logic
in having a central API for these, though, since they
are typically things that only UI-based clients will
invoke, and they may share validation code.)
It is the client's responsibility to correctly detect and
process when a user uses a slash command, and not instead
send a message with the raw content.
## Typeahead
Typeahead for both slash commands (and widgets) is implemented
via the `slash_commands` object in `static/js/composebox_typeahead.js`.

View File

@@ -1,100 +1,31 @@
# Widgets (experimental)
# Widgets
[Note: this document is currently intended to be a roadmap/design
document. It may be converted over time to permanent documentation.]
## What is a widget?
## Overview
Widgets are special kinds of messages. These include:
- polls
- TODO lists
- `/me` messages
- Trivia bot
During 2018 we built out a "widget system" in Zulip. It includes
these features:
Some widgets are used with a leading `/` (like `/poll Tea or coffee?`), similar
to [slash commands](/subsystems/slash-commands.md), but these two concepts
are very different. Slash commands have nothing to do with message sending.
- **/ping**
- **/day** (and /night, /light, /dark)
- **/poll** (and /todo)
- **zform-enabled messages** for the trivia_quiz bot
There's a strong overlap between **widgets** and **slash
commands**, and many widgets are launched by slash commands.
A few exceptions are worth noting. If you type "/me shrugs"
in the compose box, it's just a message that gets
slightly customized rendering. And
if you type "/settings", it's just a shortcut to open
the settings popup. Neither of these are really "widgets,"
per se.
Another exception, in the opposite direction, is our
trivia_quiz bot. It does not involve slash commands.
Instead it sends "extra_data" in messages to invoke
**zforms** (which enable button-based UIs in the
The trivia_quiz_bot does not use `/`'s. Instead, it sends "extra_data"
in messages to invoke **zforms** (which enable button-based UIs in the
messages).
Here are some code entities used in the above
features:
- `SubMessage` database table
- `/json/zcommand` API endpoint
- `/json/submessage` API endpoint
- `static/js/zcommand.js`
- `static/js/submessage.js`
- `static/js/poll_widget.js`
- `static/js/widgetize.js`
- `static/js/zform.js`
- `static/templates/widgets/`
- `zerver/lib/widget.py`
- `zerver/lib/zcommand.py`
- `zerver/views/submessage.py`
## `/me` messages
## Simple slash commands
These are the least complex. We use our markdown processors to
detect if a message is a `/me` message, plumb the flag through
the message object (as `is_me_message`) and have the clients
format it correctly. Related code (for the web app) lies in
`message_list_view.js` in `_maybe_format_me_message`.
We support a few very simple slash commands that
are intended for single users to do simple tasks:
- Ping the server
- Toggle day/night mode
### Data flow
These commands have client-side support in `zcommands.js`.
They send commands to the server using the `/json/command`
endpoint.
In the case of "/ping", the server code in `zcommand.py`
basically just acks the client. The client then computes
the round trip time and shows a little message above
the compose box that the user can see and then dismiss.
For commands like "/day" and "/night", the server does
a little bit of logic to toggle the user's night mode
setting, and this is largely done inside `zcommand.py`.
The server sends a very basic response, and then
the client actually changes the display colors. The
client also shows the user a little message above
the compose box instructing them how to reverse the
change.
It's a bit of a stretch to label "/ping" and "/day"
as **widgets**. In some ways they're just compose-box
shortcuts for doing UI tasks. The commands share
the new "zcommand" namespace in the code, and both
have some common UI for talking to users.
(It's possible that we don't really need a general
`/json/zcommand` endpoint for these, and we
may decide later to just use custom
API endpoints for each command. There's some logic
in having a central API for these, though, since they
are typically things that only UI-based clients will
invoke, and they may share validation code.)
### Availability
The above commands are available for all Zulip servers
that use 1.9 or above. You must use the web app client to
get the features; other clients will send the messages
without any translation (e.g. "/day" will just be a message
that says "/day" if you use the mobile app).
## Poll, todo lists, and games
## Polls, todo lists, and games
The most interactive widgets that we built during
2018 are for polls, todo lists, and games. You
@@ -116,14 +47,26 @@ interactive experience.
### Data flow
The **poll** widget uses the "submessage" architecture.
We'll use the poll widget as a concrete example.
Some important code entities for the widget implementation are:
- `SubMessage` database table
- `/json/submessage` API endpoint
- `static/js/submessage.js`
- `static/js/poll_widget.js`
- `static/js/widgetize.js`
- `static/js/zform.js`
- `static/templates/widgets/`
- `zerver/lib/widget.py`
- `zerver/views/submessage.py`
Both **poll** and **todo** widgets use the "submessage" architecture.
We'll use the poll widget as an example.
The `SubMessage` table, as the name indicates, allows
you to associate multiple submessages to any given
`Message` row. When a message gets sent, there's a
hook inside of `widget.py` that will detect slash
commands like "/poll". If a message needs to be
hook inside of `widget.py` that will detect widgets
like "/poll". If a message needs to be
widgetized, an initial `SubMessage` row will be
created with an appropriate `msg_type` (and persisted
to the database). This data will also be included
@@ -136,7 +79,7 @@ appropriate widgets.
The web app client will next collect poll options and votes
from users. The web app client has
code in `submessage.js` that dispatches events
to `widgetize.js`, which in turns sends events to
to `widgetize.js`, which in turn sends events to
individual widgets. The widgets know how to render
themselves and set up click/input handlers to collect
data. They can then post back to `/json/submessage`
@@ -148,9 +91,6 @@ schema of the messages is driven by the individual widgets.
Most of the logic is in the client; things are fairly opaque
to the server at this point.
The "submessage" architecture is generic.
Our todo list widget uses the same architecture as "poll".
If a client joins Zulip after a message has accumulated
several submessage events, it will see all of those
events the first time it sees the parent message. Clients
@@ -171,7 +111,7 @@ A good way to learn the system is to read the code
in `static/js/poll_widget.js`. It is worth noting that
writing a new widget requires only minor backend
changes in the current architecture. This could change
in the future, but for now a frontend developer mostly
in the future, but for now, a frontend developer mostly
needs to know JS, CSS, and HTML.
It may be useful to think of widgets in terms of a
@@ -199,7 +139,7 @@ most widgets are somewhat ephemeral in nature, so it's not
the end of the world if upgrades cause some older messages
to be obsolete, as long as the code degrades gracefully.
Mission critical widgets should have a deprecation strategy.
Mission-critical widgets should have a deprecation strategy.
For example, you could add optional features for one version
bump and then only make them mandatory for the next version,
as long as you don't radically change the data model. And
@@ -229,7 +169,7 @@ architecture comes to the rescue.
This section will describe our "zform" architecture.
For context, imagine a naive triva bot. The trivia bot
For context, imagine a naive trivia bot. The trivia bot
sends a question with the answers labeled as A, B, C,
and D. Folks who want to answer the bot send back an
answer have to send an actual Zulip message with something
@@ -391,12 +331,3 @@ shown here) and then sets up a click handler like below:
~~~
And then we are basically done!
## Slash commands
This document is more about "widget" behavior than "slash command"
interfaces, but there is indeed a lot of overlap between the two
concepts.
Typeahead for slash commands is implemented via the `slash_commands`
object in `static/js/composebox_typeahead.js`.