Bug 1733568 - Add ability for chat accounts to prompt about chat room invites. r=aleca
Differential Revision: https://phabricator.services.mozilla.com/D132197 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
5fdf36be5f
Коммит
24389a8c5e
|
@ -8,9 +8,11 @@ interface imIAccount;
|
|||
interface nsIDOMWindow;
|
||||
interface nsIWebProgress;
|
||||
|
||||
/* This interface is for use in the browser-request notification, to
|
||||
let protocol plugins open a browser window. This is an unfortunate
|
||||
necessity for protocols that require an OAuth authentication. */
|
||||
/**
|
||||
* This interface is for use in the browser-request notification, to
|
||||
* let protocol plugins open a browser window. This is an unfortunate
|
||||
* necessity for protocols that require an OAuth authentication.
|
||||
*/
|
||||
[scriptable, uuid(b89dbb38-0de4-11e0-b3d0-0002e304243c)]
|
||||
interface prplIRequestBrowser: nsISupports {
|
||||
readonly attribute AUTF8String promptText;
|
||||
|
@ -20,12 +22,13 @@ interface prplIRequestBrowser: nsISupports {
|
|||
in nsIWebProgress aWebProgress);
|
||||
};
|
||||
|
||||
/* This interface is used for buddy authorization requests, when the
|
||||
user needs to confirm if a remote contact should be allowed to see
|
||||
his presence information. It is implemented by the aSubject
|
||||
parameter of the buddy-authorization-request and
|
||||
buddy-authorization-request-canceled notifications.
|
||||
*/
|
||||
/**
|
||||
* This interface is used for buddy authorization requests, when the
|
||||
* user needs to confirm if a remote contact should be allowed to see
|
||||
* his presence information. It is implemented by the aSubject
|
||||
* parameter of the buddy-authorization-request and
|
||||
* buddy-authorization-request-canceled notifications.
|
||||
*/
|
||||
[scriptable, uuid(a55c1e24-17cc-4ddc-8c64-3bc315a3c3b1)]
|
||||
interface prplIBuddyRequest: nsISupports {
|
||||
readonly attribute imIAccount account;
|
||||
|
@ -34,6 +37,26 @@ interface prplIBuddyRequest: nsISupports {
|
|||
void deny();
|
||||
};
|
||||
|
||||
/**
|
||||
* This is used with chat room invitation requests, so the user can accept or
|
||||
* reject an invitation. It is implemented by the aSubject parameter of the
|
||||
* conv-authorization-request notification.
|
||||
*/
|
||||
[scriptable, uuid(44ac9606-711b-40f6-9031-94a9c60c938d)]
|
||||
interface prplIChatRequest: nsISupports {
|
||||
readonly attribute imIAccount account;
|
||||
readonly attribute AUTF8String conversationName;
|
||||
/**
|
||||
* Resolves when the request is completed, with a boolean indicating if it
|
||||
* was granted. Rejected if the request is cancelled.
|
||||
*
|
||||
* @type {Promise<boolean>}
|
||||
*/
|
||||
readonly attribute Promise completePromise;
|
||||
void grant();
|
||||
void deny();
|
||||
};
|
||||
|
||||
/**
|
||||
* Verification information for an encryption session (for example prplISession).
|
||||
* Used to present a verification flow to the user.
|
||||
|
|
|
@ -170,6 +170,7 @@ var GenericAccountPrototype = {
|
|||
aConnectionErrorMessage
|
||||
);
|
||||
this.cancelPendingBuddyRequests();
|
||||
this.cancelPendingChatRequests();
|
||||
this.cancelPendingVerificationRequests();
|
||||
},
|
||||
|
||||
|
@ -264,6 +265,86 @@ var GenericAccountPrototype = {
|
|||
delete this._pendingBuddyRequests;
|
||||
},
|
||||
|
||||
_pendingChatRequests: null,
|
||||
addChatRequest(conversationName, grantCallback, denyCallback) {
|
||||
if (!this._pendingChatRequests) {
|
||||
this._pendingChatRequests = new Set();
|
||||
}
|
||||
let resolvePromise;
|
||||
let rejectPromise;
|
||||
let completePromise = new Promise((resolve, reject) => {
|
||||
resolvePromise = resolve;
|
||||
rejectPromise = reject;
|
||||
});
|
||||
/** @implements {prplIChatRequest} */
|
||||
let chatRequest = {
|
||||
get account() {
|
||||
return this._account.imAccount;
|
||||
},
|
||||
get conversationName() {
|
||||
return conversationName;
|
||||
},
|
||||
_account: this,
|
||||
// Grant and deny callbacks both receive the auth request object as an
|
||||
// argument for further use.
|
||||
grant() {
|
||||
resolvePromise(true);
|
||||
grantCallback(this);
|
||||
this._remove();
|
||||
},
|
||||
deny() {
|
||||
resolvePromise(false);
|
||||
denyCallback(this);
|
||||
this._remove();
|
||||
},
|
||||
cancel() {
|
||||
rejectPromise(new Error("Cancelled"));
|
||||
this._remove();
|
||||
},
|
||||
completePromise,
|
||||
_remove() {
|
||||
this._account.removeChatRequest(this);
|
||||
},
|
||||
QueryInterface: ChromeUtils.generateQI(["prplIChatRequest"]),
|
||||
};
|
||||
this._pendingChatRequests.add(chatRequest);
|
||||
Services.obs.notifyObservers(chatRequest, "conv-authorization-request");
|
||||
},
|
||||
removeChatRequest(aRequest) {
|
||||
if (!this._pendingChatRequests) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._pendingChatRequests.delete(aRequest);
|
||||
},
|
||||
/**
|
||||
* Cancel a pending chat request.
|
||||
*
|
||||
* @param {string} conversationName - The conversation the request is for.
|
||||
*/
|
||||
cancelChatRequest(conversationName) {
|
||||
if (!this._pendingChatRequests) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let request of this._pendingChatRequests) {
|
||||
if (request.conversationName == conversationName) {
|
||||
request.cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
cancelPendingChatRequests() {
|
||||
if (!this._pendingChatRequests) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let request of this._pendingChatRequests) {
|
||||
request.cancel();
|
||||
}
|
||||
this._pendingChatRequests = null;
|
||||
},
|
||||
|
||||
requestBuddyInfo(aBuddyName) {},
|
||||
|
||||
get canJoinChat() {
|
||||
|
|
|
@ -1482,6 +1482,7 @@ var chatHandler = {
|
|||
if (notification) {
|
||||
notification.close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (aTopic == "buddy-verification-request") {
|
||||
aSubject.QueryInterface(Ci.imIIncomingSessionVerification);
|
||||
|
@ -1556,6 +1557,53 @@ var chatHandler = {
|
|||
if (notification) {
|
||||
notification.close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (aTopic == "conv-authorization-request") {
|
||||
aSubject.QueryInterface(Ci.prplIChatRequest);
|
||||
let value =
|
||||
"conv-auth-request-" + aSubject.account.id + aSubject.conversationName;
|
||||
let acceptButton = {
|
||||
"l10n-id": "chat-conv-invite-accept",
|
||||
callback() {
|
||||
aSubject.grant();
|
||||
},
|
||||
};
|
||||
let denyButton = {
|
||||
"l10n-id": "chat-conv-invite-deny",
|
||||
callback() {
|
||||
aSubject.deny();
|
||||
},
|
||||
};
|
||||
let box = this.msgNotificationBar;
|
||||
// Remove the notification when the request is cancelled.
|
||||
aSubject.completePromise.catch(() => {
|
||||
let notification = box.getNotificationWithValue(value);
|
||||
if (notification) {
|
||||
notification.close();
|
||||
}
|
||||
});
|
||||
let notification = box.appendNotification(
|
||||
value,
|
||||
{
|
||||
label: "",
|
||||
priority: box.PRIORITY_INFO_HIGH,
|
||||
},
|
||||
[acceptButton, denyButton]
|
||||
);
|
||||
document.l10n.setAttributes(
|
||||
notification.messageText,
|
||||
"chat-conv-invite-label",
|
||||
{
|
||||
conversation: aSubject.conversationName,
|
||||
}
|
||||
);
|
||||
notification.removeAttribute("dismissable");
|
||||
if (!gChatTab) {
|
||||
let tabmail = document.getElementById("tabmail");
|
||||
tabmail.openTab("chat", { background: true });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (aTopic == "conversation-update-type") {
|
||||
// Find conversation in conversation list.
|
||||
|
@ -1731,6 +1779,7 @@ var chatHandler = {
|
|||
this._addObserver("buddy-authorization-request-canceled");
|
||||
this._addObserver("buddy-verification-request");
|
||||
this._addObserver("buddy-verification-request-canceled");
|
||||
this._addObserver("conv-authorization-request");
|
||||
let listbox = document.getElementById("contactlistbox");
|
||||
listbox.addEventListener("keypress", function(aEvent) {
|
||||
let item = listbox.selectedItem;
|
||||
|
|
|
@ -17,4 +17,5 @@ head = head.js
|
|||
[browser_logs.js]
|
||||
[browser_messagesMail.js]
|
||||
[browser_readMessage.js]
|
||||
[browser_requestNotifications.js]
|
||||
[browser_tooltips.js]
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
add_task(async function testGrantingBuddyRequest() {
|
||||
const account = Services.accounts.createAccount("testuser", "prpl-mochitest");
|
||||
const prplAccount = account.prplAccount.wrappedJSObject;
|
||||
account.password = "this is a test";
|
||||
account.connect();
|
||||
|
||||
await openChatTab();
|
||||
ok(BrowserTestUtils.is_visible(document.getElementById("chatPanel")));
|
||||
|
||||
const notificationTopic = TestUtils.topicObserved(
|
||||
"buddy-authorization-request"
|
||||
);
|
||||
const requestPromise = new Promise((resolve, reject) => {
|
||||
prplAccount.addBuddyRequest("test-user", resolve, reject);
|
||||
});
|
||||
const [request] = await notificationTopic;
|
||||
is(request.userName, "test-user");
|
||||
is(request.account.id, account.id);
|
||||
await TestUtils.waitForTick();
|
||||
|
||||
const notificationBox = window.chatHandler.msgNotificationBar;
|
||||
const value = "buddy-auth-request-" + request.account.id + request.userName;
|
||||
const notification = notificationBox.getNotificationWithValue(value);
|
||||
ok(notification, "notification shown");
|
||||
const closePromise = new Promise(resolve => {
|
||||
notification.eventCallback = event => {
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
notification.buttonContainer.firstElementChild,
|
||||
{}
|
||||
);
|
||||
await requestPromise;
|
||||
|
||||
await closePromise;
|
||||
ok(!notificationBox.getNotificationWithValue(value), "notification closed");
|
||||
|
||||
account.disconnect();
|
||||
Services.accounts.deleteAccount(account.id);
|
||||
});
|
||||
|
||||
add_task(async function testCancellingBuddyRequest() {
|
||||
const account = Services.accounts.createAccount("testuser", "prpl-mochitest");
|
||||
const prplAccount = account.prplAccount.wrappedJSObject;
|
||||
account.password = "this is a test";
|
||||
account.connect();
|
||||
|
||||
await openChatTab();
|
||||
ok(BrowserTestUtils.is_visible(document.getElementById("chatPanel")));
|
||||
|
||||
const notificationTopic = TestUtils.topicObserved(
|
||||
"buddy-authorization-request"
|
||||
);
|
||||
prplAccount.addBuddyRequest(
|
||||
"test-user",
|
||||
() => {
|
||||
ok(false, "request was granted");
|
||||
},
|
||||
() => {
|
||||
ok(false, "request was denied");
|
||||
}
|
||||
);
|
||||
const [request] = await notificationTopic;
|
||||
is(request.userName, "test-user");
|
||||
is(request.account.id, account.id);
|
||||
|
||||
const notificationBox = window.chatHandler.msgNotificationBar;
|
||||
const value = "buddy-auth-request-" + request.account.id + request.userName;
|
||||
const notification = notificationBox.getNotificationWithValue(value);
|
||||
ok(notification, "notification shown");
|
||||
const closePromise = new Promise(resolve => {
|
||||
notification.eventCallback = event => {
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
|
||||
const cancelTopic = TestUtils.topicObserved(
|
||||
"buddy-authorization-request-canceled"
|
||||
);
|
||||
prplAccount.cancelBuddyRequest("test-user");
|
||||
const [canceledRequest] = await cancelTopic;
|
||||
is(canceledRequest.userName, request.userName);
|
||||
is(canceledRequest.account.id, request.account.id);
|
||||
|
||||
await closePromise;
|
||||
ok(!notificationBox.getNotificationWithValue(value), "notification closed");
|
||||
|
||||
account.disconnect();
|
||||
Services.accounts.deleteAccount(account.id);
|
||||
});
|
||||
|
||||
add_task(async function testDenyingBuddyRequest() {
|
||||
const account = Services.accounts.createAccount("testuser", "prpl-mochitest");
|
||||
const prplAccount = account.prplAccount.wrappedJSObject;
|
||||
account.password = "this is a test";
|
||||
account.connect();
|
||||
|
||||
await openChatTab();
|
||||
ok(BrowserTestUtils.is_visible(document.getElementById("chatPanel")));
|
||||
|
||||
const notificationTopic = TestUtils.topicObserved(
|
||||
"buddy-authorization-request"
|
||||
);
|
||||
const requestPromise = new Promise((resolve, reject) => {
|
||||
prplAccount.addBuddyRequest("test-user", reject, resolve);
|
||||
});
|
||||
const [request] = await notificationTopic;
|
||||
is(request.userName, "test-user");
|
||||
is(request.account.id, account.id);
|
||||
|
||||
const notificationBox = window.chatHandler.msgNotificationBar;
|
||||
const value = "buddy-auth-request-" + request.account.id + request.userName;
|
||||
const notification = notificationBox.getNotificationWithValue(value);
|
||||
ok(notification, "notification shown");
|
||||
const closePromise = new Promise(resolve => {
|
||||
notification.eventCallback = event => {
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
notification.buttonContainer.lastElementChild,
|
||||
{}
|
||||
);
|
||||
await requestPromise;
|
||||
|
||||
await closePromise;
|
||||
ok(!notificationBox.getNotificationWithValue(value), "notification closed");
|
||||
|
||||
account.disconnect();
|
||||
Services.accounts.deleteAccount(account.id);
|
||||
});
|
||||
|
||||
add_task(async function testGrantingChatRequest() {
|
||||
const account = Services.accounts.createAccount("testuser", "prpl-mochitest");
|
||||
const prplAccount = account.prplAccount.wrappedJSObject;
|
||||
account.password = "this is a test";
|
||||
account.connect();
|
||||
|
||||
await openChatTab();
|
||||
ok(BrowserTestUtils.is_visible(document.getElementById("chatPanel")));
|
||||
|
||||
const requestTopic = TestUtils.topicObserved("conv-authorization-request");
|
||||
const requestPromise = new Promise((resolve, reject) => {
|
||||
prplAccount.addChatRequest("test-chat", resolve, reject);
|
||||
});
|
||||
const [request] = await requestTopic;
|
||||
is(request.conversationName, "test-chat");
|
||||
is(request.account.id, account.id);
|
||||
|
||||
const notificationBox = window.chatHandler.msgNotificationBar;
|
||||
const value =
|
||||
"conv-auth-request-" + request.account.id + request.conversationName;
|
||||
const notification = notificationBox.getNotificationWithValue(value);
|
||||
ok(notification, "notification shown");
|
||||
ok(
|
||||
BrowserTestUtils.is_hidden(notification.closeButton),
|
||||
"Can't dismiss without interacting"
|
||||
);
|
||||
const closePromise = new Promise(resolve => {
|
||||
notification.eventCallback = event => {
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
notification.buttonContainer.firstElementChild,
|
||||
{}
|
||||
);
|
||||
await requestPromise;
|
||||
const result = await request.completePromise;
|
||||
ok(result);
|
||||
|
||||
await closePromise;
|
||||
ok(!notificationBox.getNotificationWithValue(value), "notification closed");
|
||||
|
||||
account.disconnect();
|
||||
Services.accounts.deleteAccount(account.id);
|
||||
});
|
||||
|
||||
add_task(async function testCancellingChatRequest() {
|
||||
const account = Services.accounts.createAccount("testuser", "prpl-mochitest");
|
||||
const prplAccount = account.prplAccount.wrappedJSObject;
|
||||
account.password = "this is a test";
|
||||
account.connect();
|
||||
|
||||
await openChatTab();
|
||||
ok(
|
||||
BrowserTestUtils.is_visible(document.getElementById("chatPanel")),
|
||||
"chat tab visible"
|
||||
);
|
||||
|
||||
const requestTopic = TestUtils.topicObserved("conv-authorization-request");
|
||||
prplAccount.addChatRequest(
|
||||
"test-chat",
|
||||
() => {
|
||||
ok(false, "chat request was granted");
|
||||
},
|
||||
() => {
|
||||
ok(false, "chat request was denied");
|
||||
}
|
||||
);
|
||||
const [request] = await requestTopic;
|
||||
is(request.conversationName, "test-chat", "conversation name matches");
|
||||
is(request.account.id, account.id, "account id matches");
|
||||
|
||||
const notificationBox = window.chatHandler.msgNotificationBar;
|
||||
const value =
|
||||
"conv-auth-request-" + request.account.id + request.conversationName;
|
||||
const notification = notificationBox.getNotificationWithValue(value);
|
||||
ok(notification, "notification shown");
|
||||
const closePromise = new Promise(resolve => {
|
||||
notification.eventCallback = event => {
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
|
||||
prplAccount.cancelChatRequest("test-chat");
|
||||
await Assert.rejects(
|
||||
request.completePromise,
|
||||
/Cancelled/,
|
||||
"completePromise is rejected to indicate cancellation"
|
||||
);
|
||||
|
||||
await closePromise;
|
||||
ok(!notificationBox.getNotificationWithValue(value), "notification closed");
|
||||
|
||||
account.disconnect();
|
||||
Services.accounts.deleteAccount(account.id);
|
||||
});
|
||||
|
||||
add_task(async function testDenyingChatRequest() {
|
||||
const account = Services.accounts.createAccount("testuser", "prpl-mochitest");
|
||||
const prplAccount = account.prplAccount.wrappedJSObject;
|
||||
account.password = "this is a test";
|
||||
account.connect();
|
||||
|
||||
await openChatTab();
|
||||
ok(BrowserTestUtils.is_visible(document.getElementById("chatPanel")));
|
||||
|
||||
const requestTopic = TestUtils.topicObserved("conv-authorization-request");
|
||||
const requestPromise = new Promise((resolve, reject) => {
|
||||
prplAccount.addChatRequest("test-chat", reject, resolve);
|
||||
});
|
||||
const [request] = await requestTopic;
|
||||
is(request.conversationName, "test-chat");
|
||||
is(request.account.id, account.id);
|
||||
|
||||
const notificationBox = window.chatHandler.msgNotificationBar;
|
||||
const value =
|
||||
"conv-auth-request-" + request.account.id + request.conversationName;
|
||||
const notification = notificationBox.getNotificationWithValue(value);
|
||||
ok(notification, "notification shown");
|
||||
const closePromise = new Promise(resolve => {
|
||||
notification.eventCallback = event => {
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
notification.buttonContainer.lastElementChild,
|
||||
{}
|
||||
);
|
||||
await requestPromise;
|
||||
const result = await request.completePromise;
|
||||
ok(!result);
|
||||
|
||||
await closePromise;
|
||||
ok(!notificationBox.getNotificationWithValue(value), "notification closed");
|
||||
|
||||
account.disconnect();
|
||||
Services.accounts.deleteAccount(account.id);
|
||||
});
|
|
@ -30,3 +30,18 @@ chat-identity-verified =
|
|||
chat-buddy-identity-status = Encryption Trust
|
||||
chat-buddy-identity-status-verified = Verified
|
||||
chat-buddy-identity-status-unverified = Unverified
|
||||
|
||||
## Conversation invite notification box
|
||||
|
||||
# This string appears in a notification bar at the top of the Contacts window
|
||||
# when someone invited the user to a multi user chat conversation, to request
|
||||
# the user to confirm they want to join the chat.
|
||||
# Variables:
|
||||
# $conversation (String) - Name of the conversation the user is invited to.
|
||||
chat-conv-invite-label = You have been invited to chat in { $conversation }
|
||||
chat-conv-invite-accept =
|
||||
.label = Accept
|
||||
.accesskey = A
|
||||
chat-conv-invite-deny =
|
||||
.label = Reject
|
||||
.accesskey = R
|
||||
|
|
Загрузка…
Ссылка в новой задаче