Display message delivery error, color text using theme colors
fixes #672
This commit is contained in:
parent
cf8501ba30
commit
447464f4d1
|
@ -16,9 +16,12 @@ public class Message : Object {
|
||||||
UNSENT,
|
UNSENT,
|
||||||
WONTSEND,
|
WONTSEND,
|
||||||
SENDING,
|
SENDING,
|
||||||
SENT
|
SENT,
|
||||||
|
ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Marked[] MARKED_RECEIVED = new Marked[] { Marked.READ, Marked.RECEIVED, Marked.ACKNOWLEDGED };
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
ERROR,
|
ERROR,
|
||||||
CHAT,
|
CHAT,
|
||||||
|
|
|
@ -144,6 +144,19 @@ public class MessageProcessor : StreamInteractionModule, Object {
|
||||||
hitted_range[query_id] = -2;
|
hitted_range[query_id] = -2;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
stream_interactor.module_manager.get_module(account, Xmpp.MessageModule.IDENTITY).received_error.connect((stream, message_stanza, error_stanza) => {
|
||||||
|
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(message_stanza.from.bare_jid, account);
|
||||||
|
if (conversation == null) return;
|
||||||
|
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(message_stanza.id, conversation);
|
||||||
|
if (message == null) return;
|
||||||
|
// We don't care about delivery errors if our counterpart already ACKed the message.
|
||||||
|
if (message.marked in Message.MARKED_RECEIVED) return;
|
||||||
|
|
||||||
|
warning("Message delivery error from %s. Type: %s, Condition: %s, Text: %s", message_stanza.from.to_string(), error_stanza.type_ ?? "-", error_stanza.condition, error_stanza.text ?? "-");
|
||||||
|
if (error_stanza.condition == Xmpp.ErrorStanza.CONDITION_RECIPIENT_UNAVAILABLE && error_stanza.type_ == Xmpp.ErrorStanza.TYPE_CANCEL) return;
|
||||||
|
|
||||||
|
message.marked = Message.Marked.ERROR;
|
||||||
|
});
|
||||||
|
|
||||||
convert_sending_to_unsent_msgs(account);
|
convert_sending_to_unsent_msgs(account);
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,14 @@ window.dino-main .dino-conversation .message-box.edit-mode:hover {
|
||||||
background: alpha(@theme_selected_bg_color, 0.12);
|
background: alpha(@theme_selected_bg_color, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.dino-main .dino-conversation .message-box.error {
|
||||||
|
background: alpha(@error_color, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.dino-main .dino-conversation .message-box.error:hover {
|
||||||
|
background: alpha(@error_color, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
window.dino-main .file-box-outer,
|
window.dino-main .file-box-outer,
|
||||||
window.dino-main .call-box-outer {
|
window.dino-main .call-box-outer {
|
||||||
background: @theme_base_color;
|
background: @theme_base_color;
|
||||||
|
|
|
@ -16,6 +16,7 @@ public class ConversationItemSkeleton : EventBox {
|
||||||
public Conversation conversation { get; set; }
|
public Conversation conversation { get; set; }
|
||||||
public Plugins.MetaConversationItem item;
|
public Plugins.MetaConversationItem item;
|
||||||
public bool item_in_edit_mode { get; set; }
|
public bool item_in_edit_mode { get; set; }
|
||||||
|
public Entities.Message.Marked item_mark { get; set; }
|
||||||
public ContentMetaItem? content_meta_item = null;
|
public ContentMetaItem? content_meta_item = null;
|
||||||
public Widget? widget = null;
|
public Widget? widget = null;
|
||||||
|
|
||||||
|
@ -34,6 +35,10 @@ public class ConversationItemSkeleton : EventBox {
|
||||||
item.bind_property("in-edit-mode", this, "item-in-edit-mode");
|
item.bind_property("in-edit-mode", this, "item-in-edit-mode");
|
||||||
this.notify["item-in-edit-mode"].connect(update_edit_mode);
|
this.notify["item-in-edit-mode"].connect(update_edit_mode);
|
||||||
|
|
||||||
|
item.bind_property("mark", this, "item-mark", BindingFlags.SYNC_CREATE);
|
||||||
|
this.notify["item-mark"].connect(update_error_mode);
|
||||||
|
update_error_mode();
|
||||||
|
|
||||||
widget = item.get_widget(Plugins.WidgetType.GTK) as Widget;
|
widget = item.get_widget(Plugins.WidgetType.GTK) as Widget;
|
||||||
if (widget != null) {
|
if (widget != null) {
|
||||||
widget.valign = Align.END;
|
widget.valign = Align.END;
|
||||||
|
@ -96,6 +101,14 @@ public class ConversationItemSkeleton : EventBox {
|
||||||
this.get_style_context().remove_class("edit-mode");
|
this.get_style_context().remove_class("edit-mode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void update_error_mode() {
|
||||||
|
if (item_mark == Message.Marked.ERROR) {
|
||||||
|
this.get_style_context().add_class("error");
|
||||||
|
} else {
|
||||||
|
this.get_style_context().remove_class("error");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[GtkTemplate (ui = "/im/dino/Dino/conversation_content_view/item_metadata_header.ui")]
|
[GtkTemplate (ui = "/im/dino/Dino/conversation_content_view/item_metadata_header.ui")]
|
||||||
|
|
|
@ -77,6 +77,12 @@ public class MessageItemWidget : SizeRequestBin {
|
||||||
public signal void edit_cancelled();
|
public signal void edit_cancelled();
|
||||||
public signal void edit_sent(string text);
|
public signal void edit_sent(string text);
|
||||||
|
|
||||||
|
enum AdditionalInfo {
|
||||||
|
NONE,
|
||||||
|
PENDING,
|
||||||
|
DELIVERY_FAILED
|
||||||
|
}
|
||||||
|
|
||||||
StreamInteractor stream_interactor;
|
StreamInteractor stream_interactor;
|
||||||
public ContentItem content_item;
|
public ContentItem content_item;
|
||||||
public Message.Marked marked { get; set; }
|
public Message.Marked marked { get; set; }
|
||||||
|
@ -84,6 +90,7 @@ public class MessageItemWidget : SizeRequestBin {
|
||||||
Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true, visible=true };
|
Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true, visible=true };
|
||||||
MessageItemEditMode? edit_mode = null;
|
MessageItemEditMode? edit_mode = null;
|
||||||
ChatTextViewController? controller = null;
|
ChatTextViewController? controller = null;
|
||||||
|
AdditionalInfo additional_info = AdditionalInfo.NONE;
|
||||||
|
|
||||||
ulong realize_id = -1;
|
ulong realize_id = -1;
|
||||||
ulong style_updated_id = -1;
|
ulong style_updated_id = -1;
|
||||||
|
@ -99,6 +106,34 @@ public class MessageItemWidget : SizeRequestBin {
|
||||||
this.stream_interactor = stream_interactor;
|
this.stream_interactor = stream_interactor;
|
||||||
this.content_item = content_item;
|
this.content_item = content_item;
|
||||||
|
|
||||||
|
Message message = ((MessageItem) content_item).message;
|
||||||
|
if (message.direction == Message.DIRECTION_SENT && !(message.marked in Message.MARKED_RECEIVED)) {
|
||||||
|
var binding = message.bind_property("marked", this, "marked");
|
||||||
|
marked_notify_handler_id = this.notify["marked"].connect(() => {
|
||||||
|
// Currently "pending", but not anymore
|
||||||
|
if (additional_info == AdditionalInfo.PENDING &&
|
||||||
|
message.marked != Message.Marked.SENDING && message.marked != Message.Marked.UNSENT) {
|
||||||
|
update_label();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently "error", but not anymore
|
||||||
|
if (additional_info == AdditionalInfo.DELIVERY_FAILED && message.marked != Message.Marked.ERROR) {
|
||||||
|
update_label();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently not error, but should be
|
||||||
|
if (additional_info != AdditionalInfo.DELIVERY_FAILED && message.marked == Message.Marked.ERROR) {
|
||||||
|
update_label();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing bad can happen anymore
|
||||||
|
if (message.marked in Message.MARKED_RECEIVED) {
|
||||||
|
binding.unbind();
|
||||||
|
this.disconnect(marked_notify_handler_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
update_label();
|
update_label();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,25 +216,21 @@ public class MessageItemWidget : SizeRequestBin {
|
||||||
markup_text = @"<span size=\'$size_str\'>" + markup_text + "</span>";
|
markup_text = @"<span size=\'$size_str\'>" + markup_text + "</span>";
|
||||||
}
|
}
|
||||||
|
|
||||||
string gray_color = Util.is_dark_theme(label) ? "#808080" : "#909090";
|
string gray_color = Util.rgba_to_hex(Util.get_label_pango_class_color(label, "dim-label"));
|
||||||
|
|
||||||
if (message.edit_to != null) {
|
if (message.edit_to != null) {
|
||||||
markup_text += " <span size='small' color='%s'>(%s)</span>".printf(gray_color, _("edited"));
|
markup_text += " <span size='small' color='%s'>(%s)</span>".printf(gray_color, _("edited"));
|
||||||
theme_dependent = true;
|
theme_dependent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append "pending..." iff message has not been sent yet
|
// Append message status info
|
||||||
|
additional_info = AdditionalInfo.NONE;
|
||||||
if (message.direction == Message.DIRECTION_SENT && (message.marked == Message.Marked.SENDING || message.marked == Message.Marked.UNSENT)) {
|
if (message.direction == Message.DIRECTION_SENT && (message.marked == Message.Marked.SENDING || message.marked == Message.Marked.UNSENT)) {
|
||||||
|
// Append "pending..." iff message has not been sent yet
|
||||||
if (message.time.compare(new DateTime.now_utc().add_seconds(-10)) < 0) {
|
if (message.time.compare(new DateTime.now_utc().add_seconds(-10)) < 0) {
|
||||||
markup_text += " <span size='small' color='%s'>%s</span>".printf(gray_color, "pending…");
|
markup_text += " <span size='small' color='%s'>%s</span>".printf(gray_color, _("pending…"));
|
||||||
|
theme_dependent = true;
|
||||||
// Update the label as soon as the sent state changes
|
additional_info = AdditionalInfo.PENDING;
|
||||||
var binding = message.bind_property("marked", this, "marked");
|
|
||||||
marked_notify_handler_id = this.notify["marked"].connect(() => {
|
|
||||||
binding.unbind();
|
|
||||||
this.disconnect(marked_notify_handler_id);
|
|
||||||
update_label();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
int time_diff = (- (int) message.time.difference(new DateTime.now_utc()) / 1000);
|
int time_diff = (- (int) message.time.difference(new DateTime.now_utc()) / 1000);
|
||||||
Timeout.add(10000 - time_diff, () => {
|
Timeout.add(10000 - time_diff, () => {
|
||||||
|
@ -207,6 +238,12 @@ public class MessageItemWidget : SizeRequestBin {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (message.direction == Message.DIRECTION_SENT && message.marked == Message.Marked.ERROR) {
|
||||||
|
// Append "delivery failed" if there was a server error
|
||||||
|
string error_color = Util.rgba_to_hex(Util.get_label_pango_color(label, "@error_color"));
|
||||||
|
markup_text += " <span size='small' color='%s'>%s</span>".printf(error_color, _("delivery failed"));
|
||||||
|
theme_dependent = true;
|
||||||
|
additional_info = AdditionalInfo.DELIVERY_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme_dependent && realize_id == -1) {
|
if (theme_dependent && realize_id == -1) {
|
||||||
|
|
|
@ -154,10 +154,33 @@ public static void image_set_from_scaled_pixbuf(Image image, Gdk.Pixbuf pixbuf,
|
||||||
image.set_from_surface(surface);
|
image.set_from_surface(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Gdk.RGBA get_label_pango_color(Label label, string css_color) {
|
||||||
|
Gtk.CssProvider provider = force_color(label, css_color);
|
||||||
|
Gdk.RGBA color_rgba = label.get_style_context().get_color(StateFlags.NORMAL);
|
||||||
|
label.get_style_context().remove_provider(provider);
|
||||||
|
return color_rgba;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Gdk.RGBA get_label_pango_class_color(Label label, string css_class) {
|
||||||
|
label.get_style_context().add_class(css_class);
|
||||||
|
Gdk.RGBA color_rgba = label.get_style_context().get_color(StateFlags.NORMAL);
|
||||||
|
label.get_style_context().remove_class(css_class);
|
||||||
|
return color_rgba;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string rgba_to_hex(Gdk.RGBA rgba) {
|
||||||
|
return "#%02x%02x%02x%02x".printf(
|
||||||
|
(uint)(Math.round(rgba.red*255)),
|
||||||
|
(uint)(Math.round(rgba.green*255)),
|
||||||
|
(uint)(Math.round(rgba.blue*255)),
|
||||||
|
(uint)(Math.round(rgba.alpha*255)))
|
||||||
|
.up();
|
||||||
|
}
|
||||||
|
|
||||||
private const string force_background_css = "%s { background-color: %s; }";
|
private const string force_background_css = "%s { background-color: %s; }";
|
||||||
private const string force_color_css = "%s { color: %s; }";
|
private const string force_color_css = "%s { color: %s; }";
|
||||||
|
|
||||||
public static void force_css(Gtk.Widget widget, string css) {
|
public static Gtk.CssProvider force_css(Gtk.Widget widget, string css) {
|
||||||
var p = new Gtk.CssProvider();
|
var p = new Gtk.CssProvider();
|
||||||
try {
|
try {
|
||||||
p.load_from_data(css);
|
p.load_from_data(css);
|
||||||
|
@ -165,14 +188,15 @@ public static void force_css(Gtk.Widget widget, string css) {
|
||||||
} catch (GLib.Error err) {
|
} catch (GLib.Error err) {
|
||||||
// handle err
|
// handle err
|
||||||
}
|
}
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void force_background(Gtk.Widget widget, string color, string selector = "*") {
|
public static void force_background(Gtk.Widget widget, string color, string selector = "*") {
|
||||||
force_css(widget, force_background_css.printf(selector, color));
|
force_css(widget, force_background_css.printf(selector, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void force_color(Gtk.Widget widget, string color, string selector = "*") {
|
public static Gtk.CssProvider force_color(Gtk.Widget widget, string color, string selector = "*") {
|
||||||
force_css(widget, force_color_css.printf(selector, color));
|
return force_css(widget, force_color_css.printf(selector, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void force_error_color(Gtk.Widget widget, string selector = "*") {
|
public static void force_error_color(Gtk.Widget widget, string selector = "*") {
|
||||||
|
|
|
@ -12,6 +12,7 @@ namespace Xmpp {
|
||||||
public StanzaListenerHolder<MessageStanza> send_pipeline = new StanzaListenerHolder<MessageStanza>();
|
public StanzaListenerHolder<MessageStanza> send_pipeline = new StanzaListenerHolder<MessageStanza>();
|
||||||
|
|
||||||
public signal void received_message(XmppStream stream, MessageStanza message);
|
public signal void received_message(XmppStream stream, MessageStanza message);
|
||||||
|
public signal void received_error(XmppStream stream, MessageStanza message, ErrorStanza error);
|
||||||
public signal void received_message_unprocessed(XmppStream stream, MessageStanza message);
|
public signal void received_message_unprocessed(XmppStream stream, MessageStanza message);
|
||||||
|
|
||||||
public async void send_message(XmppStream stream, MessageStanza message) throws IOStreamError {
|
public async void send_message(XmppStream stream, MessageStanza message) throws IOStreamError {
|
||||||
|
@ -24,7 +25,11 @@ namespace Xmpp {
|
||||||
|
|
||||||
received_message_unprocessed(stream, message);
|
received_message_unprocessed(stream, message);
|
||||||
|
|
||||||
if (!message.is_error()) {
|
if (message.is_error()) {
|
||||||
|
ErrorStanza? error_stanza = message.get_error();
|
||||||
|
if (error_stanza == null) return;
|
||||||
|
received_error(stream, message, error_stanza);
|
||||||
|
} else {
|
||||||
bool abort = yield received_pipeline.run(stream, message);
|
bool abort = yield received_pipeline.run(stream, message);
|
||||||
if (abort) return;
|
if (abort) return;
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class Stanza : Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorStanza? get_error() {
|
public ErrorStanza? get_error() {
|
||||||
return new ErrorStanza.from_stanza(this.stanza);
|
return ErrorStanza.from_stanza(this.stanza);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,8 +60,11 @@ namespace Xmpp {
|
||||||
|
|
||||||
public StanzaNode error_node;
|
public StanzaNode error_node;
|
||||||
|
|
||||||
public ErrorStanza.from_stanza(StanzaNode stanza) {
|
public static ErrorStanza? from_stanza(StanzaNode stanza) {
|
||||||
error_node = stanza.get_subnode("error");
|
var error_stanza = new ErrorStanza();
|
||||||
|
error_stanza.error_node = stanza.get_subnode("error");
|
||||||
|
if (error_stanza.error_node == null) return null;
|
||||||
|
return error_stanza;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorStanza.build(string type, string condition, string? human_readable, StanzaNode? application_condition) {
|
public ErrorStanza.build(string type, string condition, string? human_readable, StanzaNode? application_condition) {
|
||||||
|
|
Loading…
Reference in a new issue