aboutsummaryrefslogtreecommitdiff
path: root/main/src/ui/conversation_content_view
diff options
context:
space:
mode:
authorfiaxh <git@lightrise.org>2021-05-11 12:57:02 +0200
committerfiaxh <git@lightrise.org>2021-05-11 12:57:02 +0200
commitd71604913dd5b3372a823320db83c37c845fac5c (patch)
tree2ffbff97a02c81d48d8aef4a4b7ee870507236e9 /main/src/ui/conversation_content_view
parente92ed27317ae398c867c946cf7206b1f0b32f3b4 (diff)
parent90f9ecf62b2ebfef14de2874e7942552409632bf (diff)
downloaddino-d71604913dd5b3372a823320db83c37c845fac5c.tar.gz
dino-d71604913dd5b3372a823320db83c37c845fac5c.zip
Merge remote-tracking branch 'origin/feature/calls'
Diffstat (limited to 'main/src/ui/conversation_content_view')
-rw-r--r--main/src/ui/conversation_content_view/call_widget.vala215
-rw-r--r--main/src/ui/conversation_content_view/content_populator.vala4
-rw-r--r--main/src/ui/conversation_content_view/conversation_item_skeleton.vala74
-rw-r--r--main/src/ui/conversation_content_view/file_widget.vala3
4 files changed, 264 insertions, 32 deletions
diff --git a/main/src/ui/conversation_content_view/call_widget.vala b/main/src/ui/conversation_content_view/call_widget.vala
new file mode 100644
index 00000000..74525d11
--- /dev/null
+++ b/main/src/ui/conversation_content_view/call_widget.vala
@@ -0,0 +1,215 @@
+using Gee;
+using Gdk;
+using Gtk;
+using Pango;
+
+using Dino.Entities;
+
+namespace Dino.Ui {
+
+ public class CallMetaItem : ConversationSummary.ContentMetaItem {
+
+ private StreamInteractor stream_interactor;
+
+ public CallMetaItem(ContentItem content_item, StreamInteractor stream_interactor) {
+ base(content_item);
+ this.stream_interactor = stream_interactor;
+ }
+
+ public override Object? get_widget(Plugins.WidgetType type) {
+ CallItem call_item = content_item as CallItem;
+ return new CallWidget(stream_interactor, call_item.call, call_item.conversation) { visible=true };
+ }
+
+ public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) { return null; }
+ }
+
+ [GtkTemplate (ui = "/im/dino/Dino/call_widget.ui")]
+ public class CallWidget : SizeRequestBox {
+
+ [GtkChild] public Image image;
+ [GtkChild] public Label title_label;
+ [GtkChild] public Label subtitle_label;
+ [GtkChild] public Revealer incoming_call_revealer;
+ [GtkChild] public Button accept_call_button;
+ [GtkChild] public Button reject_call_button;
+
+ private StreamInteractor stream_interactor;
+ private Call call;
+ private Conversation conversation;
+ public Call.State call_state { get; set; } // needs to be public for binding
+ private uint time_update_handler_id = 0;
+
+ construct {
+ margin_top = 4;
+ size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH;
+ }
+
+ public CallWidget(StreamInteractor stream_interactor, Call call, Conversation conversation) {
+ this.stream_interactor = stream_interactor;
+ this.call = call;
+ this.conversation = conversation;
+
+ size_allocate.connect((allocation) => {
+ if (allocation.height > parent.get_allocated_height()) {
+ Idle.add(() => { parent.queue_resize(); return false; });
+ }
+ });
+
+ call.bind_property("state", this, "call-state");
+ this.notify["call-state"].connect(update_widget);
+
+ accept_call_button.clicked.connect(() => {
+ stream_interactor.get_module(Calls.IDENTITY).accept_call(call);
+
+ var call_window = new CallWindow();
+ call_window.controller = new CallWindowController(call_window, call, stream_interactor);
+ call_window.present();
+ });
+
+ reject_call_button.clicked.connect(() => {
+ stream_interactor.get_module(Calls.IDENTITY).reject_call(call);
+ });
+
+ update_widget();
+ }
+
+ private void update_widget() {
+ incoming_call_revealer.reveal_child = false;
+ incoming_call_revealer.get_style_context().remove_class("incoming");
+
+ switch (call.state) {
+ case Call.State.RINGING:
+ image.set_from_icon_name("dino-phone-ring-symbolic", IconSize.LARGE_TOOLBAR);
+ if (call.direction == Call.DIRECTION_INCOMING) {
+ bool video = stream_interactor.get_module(Calls.IDENTITY).should_we_send_video(call);
+ title_label.label = video ? _("Video call incoming") : _("Call incoming");
+ subtitle_label.label = "Ring ring…!";
+ incoming_call_revealer.reveal_child = true;
+ incoming_call_revealer.get_style_context().add_class("incoming");
+ } else {
+ title_label.label = _("Establishing call");
+ subtitle_label.label = "Ring ring…?";
+ }
+ break;
+ case Call.State.ESTABLISHING:
+ image.set_from_icon_name("dino-phone-ring-symbolic", IconSize.LARGE_TOOLBAR);
+ if (call.direction == Call.DIRECTION_INCOMING) {
+ bool video = stream_interactor.get_module(Calls.IDENTITY).should_we_send_video(call);
+ title_label.label = video ? _("Video call establishing") : _("Call establishing");
+ subtitle_label.label = "Connecting…";
+ }
+ break;
+ case Call.State.IN_PROGRESS:
+ image.set_from_icon_name("dino-phone-in-talk-symbolic", IconSize.LARGE_TOOLBAR);
+ title_label.label = _("Call in progress…");
+ string duration = get_duration_string((new DateTime.now_utc()).difference(call.local_time));
+ subtitle_label.label = _("Started %s ago").printf(duration);
+
+ time_update_handler_id = Timeout.add_seconds(get_next_time_change() + 1, () => {
+ Source.remove(time_update_handler_id);
+ time_update_handler_id = 0;
+ update_widget();
+ return true;
+ });
+
+ break;
+ case Call.State.OTHER_DEVICE_ACCEPTED:
+ image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR);
+ title_label.label = call.direction == Call.DIRECTION_INCOMING ? _("Incoming call") : _("Outgoing call");
+ subtitle_label.label = _("You handled this call on another device");
+
+ break;
+ case Call.State.ENDED:
+ image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR);
+ title_label.label = _("Call ended");
+ string formated_end = Util.format_time(call.end_time, _("%H∶%M"), _("%l∶%M %p"));
+ string duration = get_duration_string(call.end_time.difference(call.local_time));
+ subtitle_label.label = _("Ended at %s").printf(formated_end) +
+ " · " +
+ _("Lasted for %s").printf(duration);
+ break;
+ case Call.State.MISSED:
+ image.set_from_icon_name("dino-phone-missed-symbolic", IconSize.LARGE_TOOLBAR);
+ title_label.label = _("Call missed");
+ string who = null;
+ if (call.direction == Call.DIRECTION_INCOMING) {
+ who = "You";
+ } else {
+ who = Util.get_participant_display_name(stream_interactor, conversation, call.to);
+ }
+ subtitle_label.label = "%s missed this call".printf(who);
+ break;
+ case Call.State.DECLINED:
+ image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR);
+ title_label.label = _("Call declined");
+ string who = null;
+ if (call.direction == Call.DIRECTION_INCOMING) {
+ who = "You";
+ } else {
+ who = Util.get_participant_display_name(stream_interactor, conversation, call.to);
+ }
+ subtitle_label.label = "%s declined this call".printf(who);
+ break;
+ case Call.State.FAILED:
+ image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR);
+ title_label.label = _("Call failed");
+ subtitle_label.label = "Call failed to establish";
+ break;
+ }
+ }
+
+ private string get_duration_string(TimeSpan duration) {
+ DateTime a = new DateTime.now_utc();
+ DateTime b = new DateTime.now_utc();
+ a.difference(b);
+
+ TimeSpan remainder_duration = duration;
+
+ int hours = (int) Math.floor(remainder_duration / TimeSpan.HOUR);
+ remainder_duration -= hours * TimeSpan.HOUR;
+
+ int minutes = (int) Math.floor(remainder_duration / TimeSpan.MINUTE);
+ remainder_duration -= minutes * TimeSpan.MINUTE;
+
+ string ret = "";
+
+ if (hours > 0) {
+ ret += n("%i hour", "%i hours", hours).printf(hours);
+ }
+
+ if (minutes > 0) {
+ if (ret.length > 0) {
+ ret += " ";
+ }
+ ret += n("%i minute", "%i minutes", minutes).printf(minutes);
+ }
+
+ if (ret.length > 0) {
+ return ret;
+ }
+
+ return _("seconds");
+ }
+
+ private int get_next_time_change() {
+ DateTime now = new DateTime.now_local();
+ DateTime item_time = call.local_time;
+
+ if (now.get_second() < item_time.get_second()) {
+ return item_time.get_second() - now.get_second();
+ } else {
+ return 60 - (now.get_second() - item_time.get_second());
+ }
+ }
+
+ public override void dispose() {
+ base.dispose();
+
+ if (time_update_handler_id != 0) {
+ Source.remove(time_update_handler_id);
+ time_update_handler_id = 0;
+ }
+ }
+ }
+}
diff --git a/main/src/ui/conversation_content_view/content_populator.vala b/main/src/ui/conversation_content_view/content_populator.vala
index 97f15bf9..ef859bde 100644
--- a/main/src/ui/conversation_content_view/content_populator.vala
+++ b/main/src/ui/conversation_content_view/content_populator.vala
@@ -68,7 +68,10 @@ public class ContentProvider : ContentItemCollection, Object {
return new MessageMetaItem(content_item, stream_interactor);
} else if (content_item.type_ == FileItem.TYPE) {
return new FileMetaItem(content_item, stream_interactor);
+ } else if (content_item.type_ == CallItem.TYPE) {
+ return new CallMetaItem(content_item, stream_interactor);
}
+ critical("Got unknown content item type %s", content_item.type_);
return null;
}
}
@@ -85,6 +88,7 @@ public abstract class ContentMetaItem : Plugins.MetaConversationItem {
this.mark = content_item.mark;
content_item.bind_property("mark", this, "mark");
+ content_item.bind_property("encryption", this, "encryption");
this.can_merge = true;
this.requires_avatar = true;
diff --git a/main/src/ui/conversation_content_view/conversation_item_skeleton.vala b/main/src/ui/conversation_content_view/conversation_item_skeleton.vala
index c0099bf4..bcb6864e 100644
--- a/main/src/ui/conversation_content_view/conversation_item_skeleton.vala
+++ b/main/src/ui/conversation_content_view/conversation_item_skeleton.vala
@@ -104,7 +104,7 @@ public class ItemMetaDataHeader : Box {
[GtkChild] public Label dot_label;
[GtkChild] public Label time_label;
public Image received_image = new Image() { opacity=0.4 };
- public Image? unencrypted_image = null;
+ public Widget? encryption_image = null;
public static IconSize ICON_SIZE_HEADER = Gtk.icon_size_register("im.dino.Dino.HEADER_ICON", 17, 12);
@@ -124,50 +124,66 @@ public class ItemMetaDataHeader : Box {
update_name_label();
name_label.style_updated.connect(update_name_label);
+ conversation.notify["encryption"].connect(update_unencrypted_icon);
+ item.notify["encryption"].connect(update_encryption_icon);
+ update_encryption_icon();
+
+ this.add(received_image);
+
+ if (item.time != null) {
+ update_time();
+ }
+
+ item.bind_property("mark", this, "item-mark");
+ this.notify["item-mark"].connect_after(update_received_mark);
+ update_received_mark();
+ }
+
+ private void update_encryption_icon() {
Application app = GLib.Application.get_default() as Application;
ContentMetaItem ci = item as ContentMetaItem;
- if (ci != null) {
+ if (item.encryption != Encryption.NONE && ci != null) {
+ Widget? widget = null;
foreach(var e in app.plugin_registry.encryption_list_entries) {
if (e.encryption == item.encryption) {
- Object? w = e.get_encryption_icon(conversation, ci.content_item);
- if (w != null) {
- this.add(w as Widget);
- } else {
- Image image = new Image.from_icon_name("dino-changes-prevent-symbolic", ICON_SIZE_HEADER) { opacity=0.4, visible = true };
- this.add(image);
- }
+ widget = e.get_encryption_icon(conversation, ci.content_item) as Widget;
break;
}
}
+ if (widget == null) {
+ widget = new Image.from_icon_name("dino-changes-prevent-symbolic", ICON_SIZE_HEADER) { opacity=0.4, visible = true };
+ }
+ update_encryption_image(widget);
}
if (item.encryption == Encryption.NONE) {
- conversation.notify["encryption"].connect(update_unencrypted_icon);
update_unencrypted_icon();
}
+ }
- this.add(received_image);
-
- if (item.time != null) {
- update_time();
+ private void update_unencrypted_icon() {
+ if (item.encryption != Encryption.NONE) return;
+
+ if (conversation.encryption != Encryption.NONE && encryption_image == null) {
+ Image image = new Image() { opacity=0.4, visible = true };
+ image.set_from_icon_name("dino-changes-allowed-symbolic", ICON_SIZE_HEADER);
+ image.tooltip_text = _("Unencrypted");
+ update_encryption_image(image);
+ Util.force_error_color(image);
+ } else if (conversation.encryption == Encryption.NONE && encryption_image != null) {
+ update_encryption_image(null);
}
-
- item.bind_property("mark", this, "item-mark");
- this.notify["item-mark"].connect_after(update_received_mark);
- update_received_mark();
}
- private void update_unencrypted_icon() {
- if (conversation.encryption != Encryption.NONE && unencrypted_image == null) {
- unencrypted_image = new Image() { opacity=0.4, visible = true };
- unencrypted_image.set_from_icon_name("dino-changes-allowed-symbolic", ICON_SIZE_HEADER);
- unencrypted_image.tooltip_text = _("Unencrypted");
- this.add(unencrypted_image);
- this.reorder_child(unencrypted_image, 3);
- Util.force_error_color(unencrypted_image);
- } else if (conversation.encryption == Encryption.NONE && unencrypted_image != null) {
- this.remove(unencrypted_image);
- unencrypted_image = null;
+ private void update_encryption_image(Widget? widget) {
+ if (encryption_image != null) {
+ this.remove(encryption_image);
+ encryption_image = null;
+ }
+ if (widget != null) {
+ this.add(widget);
+ this.reorder_child(widget, 3);
+ encryption_image = widget;
}
}
diff --git a/main/src/ui/conversation_content_view/file_widget.vala b/main/src/ui/conversation_content_view/file_widget.vala
index 9b748876..7d77ba11 100644
--- a/main/src/ui/conversation_content_view/file_widget.vala
+++ b/main/src/ui/conversation_content_view/file_widget.vala
@@ -32,9 +32,6 @@ public class FileWidget : SizeRequestBox {
DEFAULT
}
- private const int MAX_HEIGHT = 300;
- private const int MAX_WIDTH = 600;
-
private StreamInteractor stream_interactor;
private FileTransfer file_transfer;
public FileTransfer.State file_transfer_state { get; set; }