compose: Move to `hbs` file and append it to DOM on init.

We move compose.html to compose.hbs file while keeping
`#compose` still in `home.html` as a hanger
where append rest of the elements.

This will provide us with two benefits:
* We could share common elements between message_edit_form and
compose.
* We can insert compose directly in any element. We may decide to
do it for recent topics.
This commit is contained in:
Aman Agrawal 2021-04-14 11:10:34 +00:00 committed by Tim Abbott
parent 4472986fae
commit 70d444a8eb
9 changed files with 157 additions and 132 deletions

View File

@ -1062,6 +1062,14 @@ test_ui("initialize", (override) => {
};
});
stub_templates((template_name, context) => {
assert.equal(template_name, "compose");
assert.equal(context.embedded, false);
assert.equal(context.allow_file_upload, true);
assert.equal(context.giphy_api_available, true);
return "fake-compose-template";
});
compose.initialize();
assert(resize_watch_manual_resize_checked);

View File

@ -2,6 +2,7 @@
const {strict: assert} = require("assert");
const {stub_templates} = require("../zjsunit/handlebars");
const {mock_cjs, mock_esm, set_global, zrequire} = require("../zjsunit/namespace");
const {run_test} = require("../zjsunit/test");
const $ = require("../zjsunit/zjquery");
@ -71,6 +72,11 @@ test("videos", (override) => {
override(upload, "feature_check", () => {});
stub_out_video_calls();
stub_templates((template_name) => {
assert.equal(template_name, "compose");
return "fake-compose-template";
});
compose.initialize();
(function test_no_provider_video_link_compose_clicked() {

View File

@ -1,6 +1,7 @@
import $ from "jquery";
import _ from "lodash";
import render_compose from "../templates/compose.hbs";
import render_compose_all_everyone from "../templates/compose_all_everyone.hbs";
import render_compose_announce from "../templates/compose_announce.hbs";
import render_compose_invite_users from "../templates/compose_invite_users.hbs";
@ -1098,6 +1099,15 @@ export function warn_if_mentioning_unsubscribed_user(mentioned) {
}
export function initialize() {
const $compose = $("#compose");
$compose.append(
render_compose({
embedded: $compose.attr("data-embedded") === "",
file_upload_enabled: page_params.max_file_upload_size_mib > 0,
giphy_api_available: page_params.giphy_api_key !== "",
}),
);
$("#below-compose-content .video_link").toggle(compute_show_video_chat_button());
$(
"#stream_message_recipient_stream,#stream_message_recipient_topic,#private_message_recipient",

View File

@ -459,6 +459,11 @@ export function initialize_everything() {
const user_status_params = pop_fields("user_status");
tippyjs.initialize();
// We need to initialze compose early, because other modules'
// initialization expects `#compose` to be already present in the
// DOM, dating from when the compose area was part of the backend
// template.
compose.initialize();
message_lists.initialize();
alert_popup.initialize();
alert_words.initialize(alert_words_params);
@ -499,7 +504,6 @@ export function initialize_everything() {
});
markdown.initialize(page_params.realm_linkifiers, markdown_config.get_helpers());
realm_playground.initialize(page_params.realm_playgrounds);
compose.initialize();
composebox_typeahead.initialize(); // Must happen after compose.initialize()
search.initialize();
tutorial.initialize();

View File

@ -0,0 +1,124 @@
<div id="compose-container">
<div id="compose_controls" class="compose-content new-style">
<div id="nonexistent_stream_reply_error" class="alert-error">
<span class="compose-send-status-close">&times;</span>
<span id="compose-reply-error-msg"></span>
</div>
<div id="compose_buttons">
<span class="new_message_button">
<a class="drafts-link no-underline button small rounded compose_drafts_button" href="#drafts" title="{{t 'Drafts' }} (d)">
{{t 'Drafts' }}
</a>
<span class="alert-draft pull-left">{{t 'Saved as draft' }}</span>
</span>
<span class="new_message_button">
<button type="button" class="button small rounded compose_mobile_button"
id="left_bar_compose_mobile_button_big"
title="{{t 'New message' }} (c)">
<span>+</span>
</button>
</span>
<span class="new_message_button">
<button type="button" class="button small rounded compose_stream_button"
id="left_bar_compose_stream_button_big"
title="{{t 'New topic' }} (c)">
<span class="compose_stream_button_label">{{t 'New topic' }}</span>
</button>
</span>
{{#unless embedded }}
<span class="new_message_button">
<button type="button" class="button small rounded compose_private_button"
id="left_bar_compose_private_button_big"
title="{{t 'New private message' }} (x)">
<span class="compose_private_button_label">{{t 'New private message' }}</span>
</button>
</span>
{{/unless}}
<span class="new_message_button">
<button type="button" class="button small rounded compose_reply_button"
id="left_bar_compose_reply_button_big"
title="{{t 'Reply' }} (r)">
<span class="compose_reply_button_label">{{t 'Reply' }}</span>
</button>
</span>
</div>
</div>
<div class="message_comp compose-content">
<div class="alert" id="compose-send-status">
<span class="compose-send-status-close">&times;</span>
<span id="compose-error-msg"></span>
</div>
<div id="compose_invite_users" class="alert home-error-bar"></div>
<div id="compose-all-everyone" class="alert home-error-bar"></div>
<div id="compose-announce" class="alert home-error-bar"></div>
<div id="compose_not_subscribed" class="alert home-error-bar"></div>
<div id="compose_private_stream_alert" class="alert home-error-bar"></div>
<div id="out-of-view-notification" class="notification-alert"></div>
<div class="composition-area">
<button type="button" class="close" id='compose_close' title="{{t 'Cancel compose' }} (Esc)">&times;</button>
<form id="send_message_form" action="/json/messages" method="post">
{{ csrf_input }}
<div class="compose_table">
<div id="stream-message">
<div class="stream-selection-header-colorblock message_header_stream left_part"></div>
<div class="right_part">
<span id="compose-lock-icon">
<i class="fa fa-lock" title="{{t 'This is a private stream' }}" aria-hidden="true"></i>
</span>
<input type="text" class="recipient_box" name="stream_message_recipient_stream" id="stream_message_recipient_stream" maxlength="30" value="" placeholder="{{t 'Stream' }}" autocomplete="off" tabindex="0" aria-label="{{t 'Stream' }}" />
<i class="fa fa-angle-right" aria-hidden="true"></i>
<input type="text" class="recipient_box" name="stream_message_recipient_topic" id="stream_message_recipient_topic" maxlength="60" value="" placeholder="{{t 'Topic' }}" autocomplete="off" tabindex="0" aria-label="{{t 'Topic' }}" />
</div>
</div>
<div id="private-message">
<div class="to_text">
<span>{{t 'To' }}:</span>
</div>
<div class="right_part">
<div class="pm_recipient">
<div class="pill-container" data-before="{{t 'You and' }}">
<div class="input" contenteditable="true" id="private_message_recipient" data-no-recipients-text="{{t 'Add one or more users' }}" data-some-recipients-text="{{t 'Add another user...' }}"></div>
</div>
</div>
</div>
</div>
<div>
<div class="messagebox">
<textarea class="new_message_textarea" name="content" id='compose-textarea' placeholder="{{t 'Compose your message here' }}" tabindex="0" maxlength="10000" aria-label="{{t 'Compose your message here...' }}"></textarea>
<div class="scrolling_list preview_message_area" data-simplebar id="preview_message_area" style="display:none;">
<div id="markdown_preview_spinner"></div>
<div id="preview_content" class="preview_content rendered_markdown"></div>
</div>
<div class="drag"></div>
<div id="below-compose-content">
<button type="submit" id="compose-send-button" class="button small send_message" title="{{t 'Send' }} (Ctrl + Enter)">{{t 'Send' }}</button>
<input type="file" id="file_input" class="notvisible pull-left" multiple />
{{#if file_upload_enabled }}
<a role="button" class="message-control-button fa fa-paperclip notdisplayed" aria-label="{{t 'Attach files' }}" id="attach_files" tabindex=0 title="{{t 'Attach files' }}"></a>
{{/if}}
<a role="button" id="markdown_preview" class="message-control-button fa fa-eye" aria-label="{{t 'Preview' }}" tabindex=0 title="{{t 'Preview' }}"></a>
<a role="button" id="undo_markdown_preview" class="message-control-button fa fa-edit" aria-label="{{t 'Write' }}" tabindex=0 style="display:none;" title="{{t 'Write' }}"></a>
<a role="button" class="message-control-button fa fa-video-camera video_link" aria-label="{{t 'Add video call' }}" tabindex=0 title="{{t 'Add video call' }}"></a>
<a role="button" class="message-control-button fa fa-smile-o" aria-label="{{t 'Add emoji' }}" id="emoji_map" tabindex=0 title="{{t 'Add emoji' }}"></a>
{{#if giphy_api_available }}
<a role="button" class="message-control-button" aria-label="{{t 'Add GIF' }}" id="compose_box_giphy_grid" title="{{t 'Add GIF' }}">
<img class="compose_giphy_logo" tabindex=0 src="/static/images/GIPHY_logo.png">
</a>
{{/if}}
<a class="message-control-link drafts-link" href="#drafts" title="{{t 'Drafts' }} (d)">{{t 'Drafts' }}</a>
<a role="button" class="message-control-link" tabindex=0 data-overlay-trigger="message-formatting">{{t 'Help' }}</a>
<span id="sending-indicator"></span>
<div id="send_controls" class="new-style">
<label id="enter-sends-label" class="compose_checkbox_label checkbox">
<input type="checkbox" id="enter_sends" />
<span></span>{{t 'Press Enter to send' }}
</label>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>

View File

@ -1,126 +0,0 @@
<div id="compose">
<div id="compose-container">
<div id="compose_controls" class="compose-content new-style">
<div id="nonexistent_stream_reply_error" class="alert-error">
<span class="compose-send-status-close">&times;</span>
<span id="compose-reply-error-msg"></span>
</div>
<div id="compose_buttons">
<span class="new_message_button">
<a class="drafts-link no-underline button small rounded compose_drafts_button" href="#drafts" title="{{ _('Drafts') }} (d)">
{{ _('Drafts') }}
</a>
<span class="alert-draft pull-left">{{ _('Saved as draft') }}</span>
</span>
<span class="new_message_button">
<button type="button" class="button small rounded compose_mobile_button"
id="left_bar_compose_mobile_button_big"
title="{{ _('New message') }} (c)">
<span>+</span>
</button>
</span>
<span class="new_message_button">
<button type="button" class="button small rounded compose_stream_button"
id="left_bar_compose_stream_button_big"
title="{{ _('New topic') }} (c)">
<span class="compose_stream_button_label">{{ _('New topic') }}</span>
</button>
</span>
{% if not embedded %}
<span class="new_message_button">
<button type="button" class="button small rounded compose_private_button"
id="left_bar_compose_private_button_big"
title="{{ _('New private message') }} (x)">
<span class="compose_private_button_label">{{ _('New private message') }}</span>
</button>
</span>
{% endif %}
<span class="new_message_button">
<button type="button" class="button small rounded compose_reply_button"
id="left_bar_compose_reply_button_big"
title="{{ _('Reply') }} (r)">
<span class="compose_reply_button_label">{{ _('Reply') }}</span>
</button>
</span>
</div>
</div>
<div class="message_comp compose-content">
<div class="alert" id="compose-send-status">
<span class="compose-send-status-close">&times;</span>
<span id="compose-error-msg"></span>
</div>
<div id="compose_invite_users" class="alert home-error-bar"></div>
<div id="compose-all-everyone" class="alert home-error-bar"></div>
<div id="compose-announce" class="alert home-error-bar"></div>
<div id="compose_not_subscribed" class="alert home-error-bar"></div>
<div id="compose_private_stream_alert" class="alert home-error-bar"></div>
<div id="out-of-view-notification" class="notification-alert"></div>
<div class="composition-area">
<button type="button" class="close" id='compose_close' title="{{ _('Cancel compose') }} (Esc)">&times;</button>
<form id="send_message_form" action="/json/messages" method="post">
{{ csrf_input }}
<div class="compose_table">
<div id="stream-message">
<div class="stream-selection-header-colorblock message_header_stream left_part"></div>
<div class="right_part">
<span id="compose-lock-icon">
<i class="fa fa-lock" title="{{ _('This is a private stream') }}" aria-hidden="true"></i>
</span>
<input type="text" class="recipient_box" name="stream_message_recipient_stream" id="stream_message_recipient_stream" maxlength="30" value="" placeholder="{{ _('Stream') }}" autocomplete="off" tabindex="0" aria-label="{{ _('Stream') }}" />
<i class="fa fa-angle-right" aria-hidden="true"></i>
<input type="text" class="recipient_box" name="stream_message_recipient_topic" id="stream_message_recipient_topic" maxlength="60" value="" placeholder="{{ _('Topic') }}" autocomplete="off" tabindex="0" aria-label="{{ _('Topic') }}" />
</div>
</div>
<div id="private-message">
<div class="to_text">
<span>{{ _('To') }}:</span>
</div>
<div class="right_part">
<div class="pm_recipient">
<div class="pill-container" data-before="{{ _('You and') }}">
<div class="input" contenteditable="true" id="private_message_recipient" data-no-recipients-text="{{ _('Add one or more users') }}" data-some-recipients-text="{{ _('Add another user...') }}"></div>
</div>
</div>
</div>
</div>
<div>
<div class="messagebox">
<textarea class="new_message_textarea" name="content" id='compose-textarea' placeholder="{{ _('Compose your message here') }}" tabindex="0" maxlength="10000" aria-label="{{ _('Compose your message here...') }}"></textarea>
<div class="scrolling_list preview_message_area" data-simplebar id="preview_message_area" style="display:none;">
<div id="markdown_preview_spinner"></div>
<div id="preview_content" class="preview_content rendered_markdown"></div>
</div>
<div class="drag"></div>
<div id="below-compose-content">
<button type="submit" id="compose-send-button" class="button small send_message" title="{{ _('Send') }} (Ctrl + Enter)">{{ _('Send') }}</button>
<input type="file" id="file_input" class="notvisible pull-left" multiple />
{% if max_file_upload_size_mib > 0 %}
<a role="button" class="message-control-button fa fa-paperclip notdisplayed" aria-label="{{ _('Attach files') }}" id="attach_files" tabindex=0 title="{{ _('Attach files') }}"></a>
{% endif %}
<a role="button" id="markdown_preview" class="message-control-button fa fa-eye" aria-label="{{ _('Preview') }}" tabindex=0 title="{{ _('Preview') }}"></a>
<a role="button" id="undo_markdown_preview" class="message-control-button fa fa-edit" aria-label="{{ _('Write') }}" tabindex=0 style="display:none;" title="{{ _('Write') }}"></a>
<a role="button" class="message-control-button fa fa-video-camera video_link" aria-label="{{ _('Add video call') }}" tabindex=0 title="{{ _('Add video call') }}"></a>
<a role="button" class="message-control-button fa fa-smile-o" aria-label="{{_('Add emoji')}}" id="emoji_map" tabindex=0 title="{{ _('Add emoji') }}"></a>
{% if giphy_api_available %}
<a role="button" class="message-control-button" aria-label="{{_('Add GIF')}}" id="compose_box_giphy_grid" title="{{ _('Add GIF') }}">
<img class="compose_giphy_logo" tabindex=0 src="/static/images/GIPHY_logo.png">
</a>
{% endif %}
<a class="message-control-link drafts-link" href="#drafts" title="{{ _('Drafts') }} (d)">{{ _('Drafts') }}</a>
<a role="button" class="message-control-link" tabindex=0 data-overlay-trigger="message-formatting">{{ _('Help') }}</a>
<span id="sending-indicator"></span>
<div id="send_controls" class="new-style">
<label id="enter-sends-label" class="compose_checkbox_label checkbox">
<input type="checkbox" id="enter_sends" />
<span></span>{{ _('Press Enter to send') }}
</label>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>

View File

@ -156,7 +156,8 @@
{% include "zerver/app/message_history.html" %}
{% include "zerver/app/delete_message.html" %}
{% include "zerver/app/compose.html" %}
<div id="compose" {% if embedded %}data-embedded{% endif %}>
</div>
<audio id="notification-sound-audio">
<source id="notification-sound-source-ogg" type="audio/ogg">
<source id="notification-sound-source-mp3" type="audio/mpeg">

View File

@ -238,7 +238,7 @@ class HomeTest(ZulipTestCase):
def test_home(self) -> None:
# Keep this list sorted!!!
html_bits = [
"Compose your message here...",
"start the conversation",
"Exclude messages with topic",
"Keyboard shortcuts",
"Loading...",
@ -400,7 +400,7 @@ class HomeTest(ZulipTestCase):
which still want the home page to load properly.
"""
html = result.content.decode("utf-8")
if "Compose your message" not in html:
if "start a conversation" not in html:
raise AssertionError("Home page probably did not load.")
def test_terms_of_service(self) -> None:

View File

@ -243,8 +243,6 @@ def home_real(request: HttpRequest) -> HttpResponse:
"embedded": narrow_stream is not None,
"invite_as": PreregistrationUser.INVITE_AS,
"max_file_upload_size_mib": settings.MAX_FILE_UPLOAD_SIZE,
"giphy_api_available": bool(page_params["giphy_api_key"])
and (page_params["realm_giphy_rating"] != 0),
},
)
patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True)