Approve room invitations

We use to rely on Empathy for this but as we plan to allow users to be
connected on IM without having Empathy running the Shell should do it now.

https://bugzilla.gnome.org/show_bug.cgi?id=653740
This commit is contained in:
Guillaume Desmottes 2011-06-30 14:05:41 +02:00
parent c34b357051
commit 998c5f17fc
4 changed files with 163 additions and 11 deletions

View File

@ -190,9 +190,10 @@ NotificationDaemon.prototype = {
actions, hints, timeout) { actions, hints, timeout) {
let id; let id;
// Filter out chat and presence notifications from Empathy, since we // Filter out chat, presence and invitation notifications from Empathy, since we
// handle that information from telepathyClient.js // handle that information from telepathyClient.js
if (appName == 'Empathy' && (hints['category'] == 'im.received' || if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
hints['category'] == 'x-empathy.im.room-invitation' ||
hints['category'] == 'presence.online' || hints['category'] == 'presence.online' ||
hints['category'] == 'presence.offline')) { hints['category'] == 'presence.offline')) {
// Ignore replacesId since we already sent back a // Ignore replacesId since we already sent back a

View File

@ -178,19 +178,63 @@ Client.prototype = {
} }
}, },
_displayRoomInvitation: function(conn, channel, dispatchOp, context) {
// We can only approve the rooms if we have been invited to it
let selfHandle = channel.group_get_self_handle();
if (selfHandle == 0) {
Shell.decline_dispatch_op(context, 'Not invited to the room');
return;
}
let [invited, inviter, reason, msg] = channel.group_get_local_pending_info(selfHandle);
if (!invited) {
Shell.decline_dispatch_op(context, 'Not invited to the room');
return;
}
// Request a TpContact for the inviter
Shell.get_tp_contacts(conn, [inviter],
contactFeatures,
Lang.bind(this, this._createRoomInviteSource, channel, context, dispatchOp));
context.delay();
},
_createRoomInviteSource: function(connection, contacts, failed, channel, context, dispatchOp) {
if (contacts.length < 1) {
Shell.decline_dispatch_op(context, 'Failed to get inviter');
return;
}
// We got the TpContact
let source = new RoomInviteSource(dispatchOp);
Main.messageTray.add(source);
let notif = new RoomInviteNotification(source, dispatchOp, channel, contacts[0]);
source.notify(notif);
context.accept();
},
_approveChannels: function(approver, account, conn, channels, _approveChannels: function(approver, account, conn, channels,
dispatchOp, context) { dispatchOp, context) {
// Approve the channels right away as we are going to handle it let channel = channels[0];
dispatchOp.claim_with_async(this._tpClient, let [targetHandle, targetHandleType] = channel.get_handle();
Lang.bind (this, function(dispatchOp, result) {
try {
dispatchOp.claim_with_finish(result);
this._handlingChannels(account, conn, channels);
} catch (err) {
global.logError('Failed to Claim channel: ' + err);
}}));
context.accept(); if (targetHandleType == Tp.HandleType.CONTACT) {
// Approve private text channels right away as we are going to handle it
dispatchOp.claim_with_async(this._tpClient,
Lang.bind(this, function(dispatchOp, result) {
try {
dispatchOp.claim_with_finish(result);
this._handlingChannels(account, conn, channels);
} catch (err) {
throw new Error('Failed to Claim channel: ' + err);
}}));
context.accept();
} else {
this._displayRoomInvitation(conn, channel, dispatchOp, context);
}
}, },
_handleChannels: function(handler, account, conn, channels, _handleChannels: function(handler, account, conn, channels,
@ -684,3 +728,86 @@ ChatNotification.prototype = {
this.source.respond(text); this.source.respond(text);
} }
}; };
function RoomInviteSource(dispatchOp) {
this._init(dispatchOp);
}
RoomInviteSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(dispatchOp) {
MessageTray.Source.prototype._init.call(this, _("Invitation"));
this._setSummaryIcon(this.createNotificationIcon());
this._dispatchOp = dispatchOp;
// Destroy the source if the channel dispatch operation is invalidated
// as we can't approve any more.
this._invalidId = dispatchOp.connect('invalidated',
Lang.bind(this, function(domain, code, msg) {
this.destroy();
}));
},
destroy: function() {
if (this._invalidId != 0) {
this._dispatchOp.disconnect(this._invalidId);
this._invalidId = 0;
}
MessageTray.Source.prototype.destroy.call(this);
},
createNotificationIcon: function() {
// FIXME: We don't have a 'chat room' icon (bgo #653737) use
// system-users for now as Empathy does.
return new St.Icon({ icon_name: 'system-users',
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
}
}
function RoomInviteNotification(source, dispatchOp, channel, inviter) {
this._init(source, dispatchOp, channel, inviter);
}
RoomInviteNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, dispatchOp, channel, inviter) {
MessageTray.Notification.prototype._init.call(this,
source,
/* translators: argument is a room name like
* room@jabber.org for example. */
_("Invitation to %s").format(channel.get_identifier()),
null,
{ customContent: true });
this.setResident(true);
/* translators: first argument is the name of a contact and the second
* one the name of a room. "Alice is inviting you to join room@jabber.org
* for example. */
this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier()));
this.addButton('decline', _("Decline"));
this.addButton('accept', _("Accept"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'decline':
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
'', function(src, result) {
src.leave_channels_finish(result)});
break;
case 'accept':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy();
}));
}
};

View File

@ -101,6 +101,15 @@ shell_tp_client_init (ShellTpClient *self)
/* Approver */ /* Approver */
tp_base_client_add_approver_filter (TP_BASE_CLIENT (self), filter); tp_base_client_add_approver_filter (TP_BASE_CLIENT (self), filter);
/* Approve room invitations. We don't handle or observe room channels so
* just register this filter for the approver. */
tp_base_client_take_approver_filter (TP_BASE_CLIENT (self), tp_asv_new (
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
TP_IFACE_CHANNEL_TYPE_TEXT,
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
TP_HANDLE_TYPE_ROOM,
NULL));
/* Handler */ /* Handler */
tp_base_client_add_handler_filter (TP_BASE_CLIENT (self), filter); tp_base_client_add_handler_filter (TP_BASE_CLIENT (self), filter);
@ -371,3 +380,15 @@ shell_get_contact_events (TplLogManager *log_manager,
NULL, NULL, NULL, NULL,
callback, NULL); callback, NULL);
} }
/* gjs doesn't allow us to craft a GError so we need a C wrapper */
void
shell_decline_dispatch_op (TpAddDispatchOperationContext *context,
const gchar *message)
{
GError *error = g_error_new_literal (TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
message);
tp_add_dispatch_operation_context_fail (context, error);
g_error_free (error);
}

View File

@ -111,5 +111,8 @@ void shell_get_contact_events (TplLogManager *log_manager,
guint num_events, guint num_events,
GAsyncReadyCallback callback); GAsyncReadyCallback callback);
void shell_decline_dispatch_op (TpAddDispatchOperationContext *context,
const gchar *message);
G_END_DECLS G_END_DECLS
#endif /* __SHELL_TP_CLIENT_H__ */ #endif /* __SHELL_TP_CLIENT_H__ */