aboutsummaryrefslogtreecommitdiff
path: root/main/src/ui/conversation_content_view/call_widget.vala
blob: 74525d113d533e774810cc890e127990d08645d4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
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;
            }
        }
    }
}