mirror of
https://github.com/zulip/zulip.git
synced 2025-11-11 01:16:19 +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/notifications_bar.js',
|
||||||
'js/compose.js',
|
'js/compose.js',
|
||||||
'js/subs.js',
|
'js/subs.js',
|
||||||
|
'js/message_edit.js',
|
||||||
'js/ui.js',
|
'js/ui.js',
|
||||||
'js/typeahead_helper.js',
|
'js/typeahead_helper.js',
|
||||||
'js/search.js',
|
'js/search.js',
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ urlpatterns += patterns('zephyr.views',
|
|||||||
url(r'^json/get_bots$', 'json_get_bots'),
|
url(r'^json/get_bots$', 'json_get_bots'),
|
||||||
url(r'^json/update_onboarding_steps$', 'json_update_onboarding_steps'),
|
url(r'^json/update_onboarding_steps$', 'json_update_onboarding_steps'),
|
||||||
url(r'^json/update_message$', 'json_update_message'),
|
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.
|
# These are json format views used by the API. They require an API key.
|
||||||
url(r'^api/v1/get_profile$', 'api_get_profile'),
|
url(r'^api/v1/get_profile$', 'api_get_profile'),
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ var globals =
|
|||||||
+ ' compose rows hotkeys narrow reload notifications_bar search subs'
|
+ ' compose rows hotkeys narrow reload notifications_bar search subs'
|
||||||
+ ' composebox_typeahead typeahead_helper notifications hashchange'
|
+ ' composebox_typeahead typeahead_helper notifications hashchange'
|
||||||
+ ' invite ui util activity timerender MessageList blueslip stream_list'
|
+ ' invite ui util activity timerender MessageList blueslip stream_list'
|
||||||
+ ' onboarding tab_bar'
|
+ ' onboarding message_edit tab_bar'
|
||||||
|
|
||||||
// colorspace.js
|
// colorspace.js
|
||||||
+ ' colorspace'
|
+ ' 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);
|
$.each(rendered_elems, ui.process_condensing);
|
||||||
|
|
||||||
// Re-add the fading of messages that is lost when we re-render.
|
// Re-add the fading of messages that is lost when we re-render.
|
||||||
@@ -524,6 +532,17 @@ MessageList.prototype = {
|
|||||||
this._render_win_end), 'bottom');
|
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() {
|
rerender: function MessageList_rerender() {
|
||||||
// We need to clear the rendering state, rather than just
|
// We need to clear the rendering state, rather than just
|
||||||
// doing _clear_table, since we want to potentially recollapse
|
// 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),
|
timerender.set_full_datetime(current_msg_list.get(id),
|
||||||
elt.closest(".message_row").find(".message_time"));
|
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 = {
|
var args = {
|
||||||
message: current_msg_list.get(id),
|
message: message,
|
||||||
|
can_edit_message: can_edit,
|
||||||
narrowed: narrow.active()
|
narrowed: narrow.active()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1055,7 +1058,7 @@ $(function () {
|
|||||||
$("#main_div").on("click", ".messagebox", function (e) {
|
$("#main_div").on("click", ".messagebox", function (e) {
|
||||||
var target = $(e.target);
|
var target = $(e.target);
|
||||||
if (target.is("a") || target.is("img.message_inline_image") || target.is("img.twitter-avatar") ||
|
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
|
// If this click came from a hyperlink, don't trigger the
|
||||||
// reply action. The simple way of doing this is simply
|
// reply action. The simple way of doing this is simply
|
||||||
// to call e.stopPropagation() from within the link's
|
// to call e.stopPropagation() from within the link's
|
||||||
@@ -1396,6 +1399,23 @@ $(function () {
|
|||||||
|
|
||||||
e.stopPropagation();
|
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) {
|
$('body').on('click', '.toggle_home', function (e) {
|
||||||
|
|||||||
@@ -1435,3 +1435,7 @@ li.expanded_subject {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message_edit {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|||||||
@@ -61,6 +61,14 @@
|
|||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/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>
|
<li>
|
||||||
<a class="popover_toggle_collapse" data-msgid="{{message.id}}">
|
<a class="popover_toggle_collapse" data-msgid="{{message.id}}">
|
||||||
<i class="{{#if message.collapsed}}icon-plus{{else}}icon-minus{{/if}}"></i>
|
<i class="{{#if message.collapsed}}icon-plus{{else}}icon-minus{{/if}}"></i>
|
||||||
|
|||||||
@@ -78,6 +78,10 @@
|
|||||||
{{/include_sender}}
|
{{/include_sender}}
|
||||||
</div>
|
</div>
|
||||||
<div class="message_content">{{#if ../../use_match_properties}}{{{match_content}}}{{else}}{{{content}}}{{/if}}</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_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>
|
<div class="message_condenser message_length_controller" title="Make this message take up less space on the screen">[Condense this message]</div>
|
||||||
</td>
|
</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):
|
def json_update_message(request, user_profile):
|
||||||
return update_message_backend(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
|
@has_request_variables
|
||||||
def update_message_backend(request, user_profile,
|
def update_message_backend(request, user_profile,
|
||||||
message_id=REQ(converter=to_non_negative_int),
|
message_id=REQ(converter=to_non_negative_int),
|
||||||
|
|||||||
Reference in New Issue
Block a user