mirror of
https://github.com/zulip/zulip.git
synced 2025-11-10 17:07:07 +00:00
Add user interface frontend for editing messages.
The only known outstanding bug with this is that it doesn't properly handle the updating of a message's highlighting/presence in a narrowed view (e.g. in theory, a message should disappear if it is edited such that its subject doesn't match your narrow or it no longer matches your search). I think I'll just open a trac ticket about that once this is merged, since it's a little hairy to deal with and kinda a marginal use case. Also it's not pretty, but that should be easy to tweak once we get the framework merged. Conflicts: tools/jslint/check-all.js (imported from commit 2d0e3a440bcd885546bd8e28aff97bf379649950)
This commit is contained in:
@@ -288,6 +288,7 @@ PIPELINE_JS = {
|
||||
'js/notifications_bar.js',
|
||||
'js/compose.js',
|
||||
'js/subs.js',
|
||||
'js/message_edit.js',
|
||||
'js/ui.js',
|
||||
'js/typeahead_helper.js',
|
||||
'js/search.js',
|
||||
|
||||
@@ -113,6 +113,7 @@ urlpatterns += patterns('zephyr.views',
|
||||
url(r'^json/get_bots$', 'json_get_bots'),
|
||||
url(r'^json/update_onboarding_steps$', 'json_update_onboarding_steps'),
|
||||
url(r'^json/update_message$', 'json_update_message'),
|
||||
url(r'^json/fetch_raw_message$', 'json_fetch_raw_message'),
|
||||
|
||||
# These are json format views used by the API. They require an API key.
|
||||
url(r'^api/v1/get_profile$', 'api_get_profile'),
|
||||
|
||||
@@ -18,7 +18,7 @@ var globals =
|
||||
+ ' compose rows hotkeys narrow reload notifications_bar search subs'
|
||||
+ ' composebox_typeahead typeahead_helper notifications hashchange'
|
||||
+ ' invite ui util activity timerender MessageList blueslip stream_list'
|
||||
+ ' onboarding tab_bar'
|
||||
+ ' onboarding message_edit tab_bar'
|
||||
|
||||
// colorspace.js
|
||||
+ ' colorspace'
|
||||
|
||||
89
zephyr/static/js/message_edit.js
Normal file
89
zephyr/static/js/message_edit.js
Normal file
@@ -0,0 +1,89 @@
|
||||
var message_edit = (function () {
|
||||
var exports = {};
|
||||
var currently_editing_messages = {};
|
||||
|
||||
exports.save = function (row) {
|
||||
var msg_list = current_msg_list;
|
||||
var message = current_msg_list.get(rows.id(row));
|
||||
var new_subject = row.find(".message_edit_subject").val();
|
||||
var new_content = row.find(".message_edit_content").val();
|
||||
var request = {message_id: message.id};
|
||||
if (new_subject !== message.subject) {
|
||||
request.subject = new_subject;
|
||||
}
|
||||
if (new_content !== message.raw_content) {
|
||||
request.content = new_content;
|
||||
}
|
||||
if (request.subject === undefined &&
|
||||
request.content === undefined) {
|
||||
// If they didn't change anything, just cancel it.
|
||||
return message_edit.cancel(row);
|
||||
}
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/json/update_message',
|
||||
data: request,
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
if (msg_list === current_msg_list) {
|
||||
message_edit.cancel(row);
|
||||
}
|
||||
}
|
||||
});
|
||||
// The message will automatically get replaced when it arrives.
|
||||
};
|
||||
|
||||
function edit_message (row, raw_content) {
|
||||
var message = current_msg_list.get(rows.id(row));
|
||||
var edit_row = row.find(".message_edit");
|
||||
var form = $(templates.render('message_edit_form',
|
||||
{is_stream: message.is_stream,
|
||||
subject: message.subject,
|
||||
content: raw_content}));
|
||||
|
||||
var edit_obj = {form: form, raw_content: raw_content};
|
||||
current_msg_list.show_edit_message(row, edit_obj);
|
||||
|
||||
currently_editing_messages[message.id] = edit_obj;
|
||||
}
|
||||
|
||||
exports.start = function (row) {
|
||||
var message = current_msg_list.get(rows.id(row));
|
||||
var msg_list = current_msg_list;
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/json/fetch_raw_message',
|
||||
data: {message_id: message.id},
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
if (current_msg_list === msg_list) {
|
||||
message.raw_content = data.raw_content;
|
||||
edit_message(row, data.raw_content);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.cancel = function (row) {
|
||||
var message = current_msg_list.get(rows.id(row));
|
||||
delete currently_editing_messages[message.id];
|
||||
current_msg_list.hide_edit_message(row);
|
||||
};
|
||||
|
||||
exports.maybe_show_edit = function(row, id) {
|
||||
if (currently_editing_messages[id] !== undefined){
|
||||
current_msg_list.show_edit_message(row, currently_editing_messages[id]);
|
||||
}
|
||||
};
|
||||
|
||||
$(document).on('narrow_deactivated.zephyr', function (event) {
|
||||
$.each(currently_editing_messages, function(idx, elem) {
|
||||
if (current_msg_list.get(idx) !== undefined) {
|
||||
var row = rows.get(idx, current_msg_list.table_name);
|
||||
current_msg_list.show_edit_message(row, elem);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return exports;
|
||||
}());
|
||||
@@ -402,6 +402,14 @@ MessageList.prototype = {
|
||||
}});
|
||||
}
|
||||
|
||||
$.each(rendered_elems, function(idx, elem) {
|
||||
var row = $(elem);
|
||||
if (! row.hasClass('message_row')) {
|
||||
return;
|
||||
}
|
||||
var id = rows.id(row);
|
||||
message_edit.maybe_show_edit(row, id);
|
||||
});
|
||||
$.each(rendered_elems, ui.process_condensing);
|
||||
|
||||
// Re-add the fading of messages that is lost when we re-render.
|
||||
@@ -524,6 +532,17 @@ MessageList.prototype = {
|
||||
this._render_win_end), 'bottom');
|
||||
},
|
||||
|
||||
show_edit_message: function MessageList_show_edit_message(row, edit_obj) {
|
||||
row.find(".message_edit_form").empty().append(edit_obj.form);
|
||||
row.find(".message_content").hide();
|
||||
row.find(".message_edit").show();
|
||||
},
|
||||
|
||||
hide_edit_message: function MessageList_hide_edit_message(row) {
|
||||
row.find(".message_content").show();
|
||||
row.find(".message_edit").hide();
|
||||
},
|
||||
|
||||
rerender: function MessageList_rerender() {
|
||||
// We need to clear the rendering state, rather than just
|
||||
// doing _clear_table, since we want to potentially recollapse
|
||||
|
||||
@@ -535,8 +535,11 @@ function show_actions_popover(element, id) {
|
||||
timerender.set_full_datetime(current_msg_list.get(id),
|
||||
elt.closest(".message_row").find(".message_time"));
|
||||
|
||||
var message = current_msg_list.get(id);
|
||||
var can_edit = message.sender_email.toLowerCase() === page_params.email.toLowerCase();
|
||||
var args = {
|
||||
message: current_msg_list.get(id),
|
||||
message: message,
|
||||
can_edit_message: can_edit,
|
||||
narrowed: narrow.active()
|
||||
};
|
||||
|
||||
@@ -1055,7 +1058,7 @@ $(function () {
|
||||
$("#main_div").on("click", ".messagebox", function (e) {
|
||||
var target = $(e.target);
|
||||
if (target.is("a") || target.is("img.message_inline_image") || target.is("img.twitter-avatar") ||
|
||||
target.is("div.message_length_controller")) {
|
||||
target.is("div.message_length_controller") || target.is("textarea") || target.is("input")) {
|
||||
// If this click came from a hyperlink, don't trigger the
|
||||
// reply action. The simple way of doing this is simply
|
||||
// to call e.stopPropagation() from within the link's
|
||||
@@ -1396,6 +1399,23 @@ $(function () {
|
||||
|
||||
e.stopPropagation();
|
||||
});
|
||||
$('body').on('click', '.popover_edit_message', function (e) {
|
||||
var msgid = $(e.currentTarget).data('msgid');
|
||||
var row = rows.get(msgid, current_msg_list.table_name);
|
||||
ui.hide_actions_popover();
|
||||
message_edit.start(row);
|
||||
e.stopPropagation();
|
||||
});
|
||||
$("body").on("click", ".message_edit_save", function (e) {
|
||||
var row = $(this).closest(".message_row");
|
||||
message_edit.save(row);
|
||||
e.stopPropagation();
|
||||
});
|
||||
$("body").on("click", ".message_edit_cancel", function (e) {
|
||||
var row = $(this).closest(".message_row");
|
||||
message_edit.cancel(row);
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
|
||||
$('body').on('click', '.toggle_home', function (e) {
|
||||
|
||||
@@ -1435,3 +1435,7 @@ li.expanded_subject {
|
||||
text-align: center;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.message_edit {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,14 @@
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
{{#if can_edit_message}}
|
||||
<li>
|
||||
<a class="popover_edit_message" data-msgid="{{message.id}}">
|
||||
<i class="icon-pencil"></i> Edit this message
|
||||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
<li>
|
||||
<a class="popover_toggle_collapse" data-msgid="{{message.id}}">
|
||||
<i class="{{#if message.collapsed}}icon-plus{{else}}icon-minus{{/if}}"></i>
|
||||
|
||||
@@ -78,6 +78,10 @@
|
||||
{{/include_sender}}
|
||||
</div>
|
||||
<div class="message_content">{{#if ../../use_match_properties}}{{{match_content}}}{{else}}{{{content}}}{{/if}}</div>
|
||||
<div class="message_edit">
|
||||
<div class="message_edit_form" id="{{id}}"></div>
|
||||
<a class="message_edit_save">Save</a> <a class="message_edit_cancel">Cancel</a>
|
||||
</div>
|
||||
<div class="message_expander message_length_controller" title="See the rest of this message">[More...]</div>
|
||||
<div class="message_condenser message_length_controller" title="Make this message take up less space on the screen">[Condense this message]</div>
|
||||
</td>
|
||||
|
||||
8
zephyr/static/templates/message_edit_form.handlebars
Normal file
8
zephyr/static/templates/message_edit_form.handlebars
Normal file
@@ -0,0 +1,8 @@
|
||||
{{! Client-side Mustache template for rendering the trailing bookend.}}
|
||||
|
||||
<form>
|
||||
{{#if is_stream}}
|
||||
<span>Subject: <input type="text" value={{subject}} class="message_edit_subject"></input></span><br />
|
||||
{{/if}}
|
||||
<span>Content: <textarea class="message_edit_content">{{content}}</textarea></span>
|
||||
</form>
|
||||
@@ -1038,6 +1038,20 @@ def json_tutorial_status(request, user_profile, status=REQ('status')):
|
||||
def json_update_message(request, user_profile):
|
||||
return update_message_backend(request, user_profile)
|
||||
|
||||
@authenticated_json_post_view
|
||||
@has_request_variables
|
||||
def json_fetch_raw_message(request, user_profile,
|
||||
message_id=REQ(converter=to_non_negative_int)):
|
||||
try:
|
||||
message = Message.objects.get(id=message_id)
|
||||
except Message.DoesNotExist:
|
||||
return json_error("No such message")
|
||||
|
||||
if message.sender != user_profile:
|
||||
return json_error("Message was not sent by you")
|
||||
|
||||
return json_success({"raw_content": message.content})
|
||||
|
||||
@has_request_variables
|
||||
def update_message_backend(request, user_profile,
|
||||
message_id=REQ(converter=to_non_negative_int),
|
||||
|
||||
Reference in New Issue
Block a user