Commit 221798e6 authored by JC Brand's avatar JC Brand

Merge branch 'open-room-via-url'

parents d3fb9d81 3514d6d5
......@@ -3,6 +3,7 @@
## 3.3.0 (Unreleased)
### Bugfixes
- #800 Could not register successfully in ejabberd 17.01
- Don't require `auto_login` to be `true` when using the API to log in.
- Moment locale wasn't being set to the value passed via the `i18n` option.
- Refetch the roster from the server after reconnection.
......@@ -12,7 +13,9 @@
Otherwise connected contacts might not get your presence updates.
### New Features
- #828 Add routing for the `#converse-login` and `#converse-register` URL
- #314 Add support for opening chat rooms with a URL fragment such as `#converse/room?jid=room@domain`
and private chats with a URL fragment such as `#converse/chat?jid=user@domain`
- #828 Add routing for the `#converse/login` and `#converse/register` URL
fragments, which will render the registration and login forms respectively.
### UX/UI changes
......
......@@ -300,6 +300,22 @@ have to be registered anew.
``_converse.on('reconnected', function () { ... });``
roomsAutoJoined
---------------
Emitted once any rooms that have been configured to be automatically joined,
specified via the _`auto_join_rooms` setting, have been entered.
``_converse.on('roomsAutoJoined', function () { ... });``
Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_:
.. code-block:: javascript
_converse.api.waitUntil('roomsAutoJoined').then(function () {
// Your code here...
});
roomInviteSent
~~~~~~~~~~~~~~
......
......@@ -6,6 +6,16 @@
Features
========
Open chats via URL
==================
From version 3.3.0, converse.js now has the ability to open chats (private or
groupchat) based on the URL fragment.
A room (aka groupchat) can be opened with a URL fragment such as `#converse/room?jid=room@domain`
and a private chat with a URL fragment such as
`#converse/chat?jid=user@domain`.
Off-the-record encryption
=========================
......
......@@ -10,7 +10,7 @@
define(["converse-core"], factory);
}(this, function (converse) {
"use strict";
const { Backbone, Strophe, b64_sha1, utils, _ } = converse.env;
const { Backbone, Promise, Strophe, b64_sha1, utils, _ } = converse.env;
converse.plugins.add('converse-chatboxes', {
......@@ -55,6 +55,23 @@
'chatBoxesInitialized'
]);
function openChat (jid) {
if (!utils.isValidJID(jid)) {
return converse.log(
`Invalid JID "${jid}" provided in URL fragment`,
Strophe.LogLevel.WARN
);
}
Promise.all([
_converse.api.waitUntil('rosterContactsFetched'),
_converse.api.waitUntil('chatBoxesFetched')
]).then(() => {
_converse.api.chats.open(jid);
});
}
_converse.router.route('converse/chat?jid=:jid', openChat);
_converse.ChatBoxes = Backbone.Collection.extend({
comparator: 'time_opened',
......@@ -343,9 +360,12 @@
_converse.log("chats.open: You need to provide at least one JID", Strophe.LogLevel.ERROR);
return null;
} else if (_.isString(jids)) {
return _converse.getViewForChatBox(
_converse.chatboxes.getChatBox(jids, true, attrs).trigger('show')
);
const chatbox = _converse.chatboxes.getChatBox(jids, true, attrs);
if (_.isNil(chatbox)) {
_converse.log("Could not open chatbox for JID: "+jids);
return;
}
return _converse.getViewForChatBox(chatbox.trigger('show'));
}
return _.map(jids, (jid) =>
_converse.getViewForChatBox(
......
......@@ -836,6 +836,9 @@
close (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); }
if (Backbone.history.getFragment() === "converse/chat?jid="+this.model.get('jid')) {
_converse.router.navigate('');
}
if (_converse.connection.connected) {
// Immediately sending the chat state, because the
// model is going to be destroyed afterwards.
......
......@@ -511,7 +511,7 @@
if (jid_element.value &&
!_converse.locked_domain &&
!_converse.default_domain &&
_.filter(jid_element.value.split('@')).length < 2) {
!utils.isValidJID(jid_element.value)) {
jid_element.setCustomValidity(__('Please enter a valid XMPP address'));
return false;
}
......@@ -550,6 +550,10 @@
jid = Strophe.getBareJidFromJid(jid).toLowerCase()+'/'+resource;
}
}
if (_.includes(["converse/login", "converse/register"],
Backbone.history.getFragment())) {
_converse.router.navigate('', {'replace': true});
}
_converse.connection.reset();
_converse.connection.connect(jid, password, _converse.onConnectStatusChanged);
}
......
......@@ -230,6 +230,9 @@
}
};
_converse.router = new Backbone.Router();
_converse.initialize = function (settings, callback) {
"use strict";
settings = !_.isUndefined(settings) ? settings : {};
......
......@@ -353,9 +353,28 @@
'toggle_occupants': true
},
});
_converse.api.promises.add('roomsPanelRendered');
_converse.api.promises.add(['roomsPanelRendered', 'roomsAutoJoined']);
_converse.openChatRoom = function (settings, bring_to_foreground) {
function openRoom (jid) {
if (!utils.isValidJID(jid)) {
return converse.log(
`Invalid JID "${jid}" provided in URL fragment`,
Strophe.LogLevel.WARN
);
}
const promises = [_converse.api.waitUntil('roomsAutoJoined')]
if (!_converse.allow_bookmarks) {
promises.push( _converse.api.waitUntil('bookmarksInitialized'));
}
Promise.all(promises).then(() => {
_converse.api.rooms.open(jid);
});
}
_converse.router.route('converse/room?jid=:jid', openRoom);
function openChatRoom (settings, bring_to_foreground) {
/* Opens a chat room, making sure that certain attributes
* are correct, for example that the "type" is set to
* "chatroom".
......@@ -367,7 +386,7 @@
settings.id = settings.jid;
settings.box_id = b64_sha1(settings.jid)
return _converse.chatboxviews.showChat(settings, bring_to_foreground);
};
}
_converse.ChatRoom = _converse.ChatBox.extend({
......@@ -823,7 +842,11 @@
affiliations = [affiliations];
}
return new Promise((resolve, reject) => {
const promises = _.map(affiliations, _.partial(this.requestMemberList, this.model.get('jid')));
const promises = _.map(
affiliations,
_.partial(this.requestMemberList, this.model.get('jid'))
);
Promise.all(promises).then(
_.flow(this.marshallAffiliationIQs.bind(this), resolve),
_.flow(this.marshallAffiliationIQs.bind(this), resolve)
......@@ -1243,6 +1266,9 @@
* reason for leaving.
*/
this.hide();
if (Backbone.history.getFragment() === "converse/room?jid="+this.model.get('jid')) {
_converse.router.navigate('');
}
this.occupantsview.model.reset();
this.occupantsview.model.browserStorage._clear();
if (_converse.connection.connected) {
......@@ -2637,7 +2663,7 @@
ev.preventDefault();
const data = this.parseRoomDataFromEvent(ev);
if (!_.isUndefined(data)) {
_converse.openChatRoom(data);
openChatRoom(data);
}
},
......@@ -2685,7 +2711,7 @@
}
}
if (result === true) {
const chatroom = _converse.openChatRoom({
const chatroom = openChatRoom({
'jid': room_jid,
'password': $x.attr('password')
});
......@@ -2724,6 +2750,7 @@
Strophe.LogLevel.ERROR);
}
});
_converse.emit('roomsAutoJoined');
}
_converse.on('chatBoxesFetched', autoJoinRooms);
......@@ -2775,9 +2802,9 @@
if (_.isUndefined(jids)) {
throw new TypeError('rooms.open: You need to provide at least one JID');
} else if (_.isString(jids)) {
return _converse.getChatRoom(jids, attrs, _converse.openChatRoom);
return _converse.getChatRoom(jids, attrs, openChatRoom);
}
return _.map(jids, _.partial(_converse.getChatRoom, _, attrs, _converse.openChatRoom));
return _.map(jids, _.partial(_converse.getChatRoom, _, attrs, openChatRoom));
},
'get' (jids, attrs, create) {
if (_.isString(attrs)) {
......
......@@ -59,7 +59,7 @@
// relevant objects or classes.
//
// New functions which don't exist yet can also be added.
LoginPanel: {
render: function (cfg) {
......@@ -80,9 +80,6 @@
ControlBoxView: {
events: {
},
initialize () {
this.__super__.initialize.apply(this, arguments);
this.model.on('change:active-form', this.showLoginOrRegisterForm.bind(this))
......@@ -102,7 +99,6 @@
}
},
renderRegistrationPanel () {
const { _converse } = this.__super__;
if (_converse.allow_registration) {
......@@ -149,21 +145,15 @@
providers_link: 'https://xmpp.net/directory.php', // Link to XMPP providers shown on registration page
});
_converse.RegistrationRouter = Backbone.Router.extend({
initialize () {
this.route('converse-login', _.partial(this.setActiveForm, 'login'));
this.route('converse-register', _.partial(this.setActiveForm, 'register'));
},
setActiveForm (value) {
_converse.api.waitUntil('controlboxInitialized').then(() => {
const controlbox = _converse.chatboxes.get('controlbox')
controlbox.set({'active-form': value});
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}
});
const router = new _converse.RegistrationRouter();
function setActiveForm (value) {
_converse.api.waitUntil('controlboxInitialized').then(() => {
const controlbox = _converse.chatboxes.get('controlbox')
controlbox.set({'active-form': value});
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}
_converse.router.route('converse/login', _.partial(setActiveForm, 'login'));
_converse.router.route('converse/register', _.partial(setActiveForm, 'register'));
_converse.RegisterPanel = Backbone.View.extend({
......@@ -425,11 +415,14 @@
);
this.abortRegistration();
} else if (status_code === Strophe.Status.REGISTERED) {
router.navigate(); // Strip the URL fragment
_converse.log("Registered successfully.");
_converse.connection.reset();
this.showSpinner();
if (_.includes(["converse/login", "converse/register"], Backbone.history.getFragment())) {
_converse.router.navigate('', {'replace': true});
}
if (this.fields.password && this.fields.username) {
// automatically log the user in
_converse.connection.connect(
......@@ -464,7 +457,7 @@
form.insertAdjacentHTML(
'beforeend',
tpl_form_input({
'label': key,
'label': key,
'name': key,
'placeholder': key,
'required': true,
......
......@@ -31,7 +31,7 @@
function (iq, jid) {
_converse.log(
`Error while retrieving vcard for ${jid}`,
Strophe.LogLevel.ERROR
Strophe.LogLevel.WARN
);
_converse.createRequestingContactFromVCard(presence, iq, jid);
}
......
<div class="switch-form">
<p>{{{ __("Don't have a chat account?") }}}</p>
<p><a class="register-account toggle-register-login" href="#converse-register">{{{__("Create an account")}}}</a></p>
<p><a class="register-account toggle-register-login" href="#converse/register">{{{__("Create an account")}}}</a></p>
</div>
......@@ -17,6 +17,6 @@
<div class="switch-form">
<p>{{{ __("Already have a chat account?") }}}</p>
<p>
<a class="login-here toggle-register-login" href="#converse-login">{{{__("Log in here")}}}</a>
<a class="login-here toggle-register-login" href="#converse/login">{{{__("Log in here")}}}</a>
</p>
</div>
......@@ -266,6 +266,10 @@
}
};
u.isValidJID = function (jid) {
return _.filter(jid.split('@')).length === 2 && !jid.startsWith('@') && !jid.endsWith('@');
};
u.isSameBareJID = function (jid1, jid2) {
return Strophe.getBareJidFromJid(jid1).toLowerCase() ===
Strophe.getBareJidFromJid(jid2).toLowerCase();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment