Compare commits

..

55 Commits

Author SHA1 Message Date
Richard van der Hoff ad5d07caf8 0.4.2 2016-03-17 00:58:33 +00:00
Richard van der Hoff b2d7abc0a1 Prepare changelog for v0.4.2 2016-03-17 00:58:16 +00:00
Richard van der Hoff cc475e6392 add jsdoc as dev dependency 2016-03-17 00:58:16 +00:00
Richard van der Hoff e4c6717bd5 Don't fail release if dist dir already exists 2016-03-17 00:58:16 +00:00
Richard van der Hoff 53f813207e Add option to skip changelog generation 2016-03-17 00:45:39 +00:00
Richard van der Hoff 873fde27ac Don't error if changelog is unchanged 2016-03-17 00:33:01 +00:00
Richard van der Hoff 8d9d638953 release.sh: fix 'read' syntax 2016-03-17 00:22:37 +00:00
Richard van der Hoff 2f93490054 Don't create release branch if we're already there 2016-03-17 00:19:30 +00:00
Richard van der Hoff e22efc9dd5 release.sh: chmod +x 2016-03-16 23:40:44 +00:00
Richard van der Hoff e7ac80cf2b Script to do releases 2016-03-16 23:12:38 +00:00
Richard van der Hoff 4436087777 Use npm version to do release stuff 2016-03-16 23:12:38 +00:00
Matthew Hodgson f7bc11361c trivially add content.currently_active in m.presence events. 2016-03-16 22:35:55 +00:00
Matthew Hodgson a68b61dafe oops, revert accidental merge 2016-03-16 17:33:30 +00:00
Matthew Hodgson 84c9876b3a if synapse handed us profile data in the leave event, then use it. unbreaks overzealous tests 2016-03-16 17:32:47 +00:00
Matthew Hodgson de864c489a make sure we show display names & avatars on parts, and use the right type of content for displaynames for member events in general. fixes https://github.com/vector-im/vector-web/issues/1140 and https://github.com/vector-im/vector-web/issues/873 and a bunch more 2016-03-16 17:32:47 +00:00
Matthew Hodgson 2c277f7d96 Merge pull request #108 from matrix-org/matthew/fix-displaynames
Matthew/fix displaynames
2016-03-16 17:31:41 +00:00
Kegan Dougal d0560f594d Set the right .sender value for m.room.member events 2016-03-16 17:18:33 +00:00
Matthew Hodgson 60b6310494 typo 2016-03-16 16:47:25 +00:00
Matthew Hodgson abd27f9b75 failing test for https://github.com/vector-im/vector-web/issues/1140 2016-03-16 16:45:23 +00:00
Matthew Hodgson 3d316959f9 Revert this as it just doesn't work - our events are always m.room.members at this point 2016-03-16 16:44:22 +00:00
Matthew Hodgson 8aa3b79501 Merge pull request #107 from matrix-org/revert-106-matthew/fix-displaynames
Revert "make sure we show display names & avatars on parts, and use the right…"
2016-03-16 14:49:38 +00:00
Matthew Hodgson f35409700a Revert "make sure we show display names & avatars on parts, and use the right…" 2016-03-16 14:49:29 +00:00
Matthew Hodgson b009739b9e Merge pull request #106 from matrix-org/matthew/fix-displaynames
make sure we show display names & avatars on parts, and use the right…
2016-03-16 14:39:59 +00:00
Matthew Hodgson f007af741e Merge pull request #104 from matrix-org/matthew/may-client-send-state
Add RoomState.mayClientSendStateEvent()
2016-03-16 14:35:27 +00:00
Matthew Hodgson 3db4d9488b oops, normal events should use the chronologically earlier content, but membership changes should use the current content. 2016-03-16 14:31:03 +00:00
Matthew Hodgson 6b0fa84697 if synapse handed us profile data in the leave event, then use it. unbreaks overzealous tests 2016-03-16 14:14:14 +00:00
Matthew Hodgson 98b0cf2560 make sure we show display names & avatars on parts, and use the right type of content for displaynames for member events in general. fixes https://github.com/vector-im/vector-web/issues/1140 and https://github.com/vector-im/vector-web/issues/873 and a bunch more 2016-03-16 13:51:55 +00:00
Matthew Hodgson 372759b6e4 fix lint 2016-03-16 13:43:38 +00:00
Matthew Hodgson ec29b4ffeb Add RoomState.mayClientSendStateEvent() 2016-03-16 13:08:36 +00:00
Matthew Hodgson 95494933fd Merge pull request #103 from matrix-org/matthew/peek-presence
make presence work when peeking.
2016-03-16 11:55:25 +00:00
Matthew Hodgson 6fff29c07b oops, that map should be a forEach 2016-03-16 11:54:56 +00:00
David Baker 6f7ed93b87 Merge pull request #100 from matrix-org/dbkr/session_logged_out
Add Session.logged_out event
2016-03-16 10:44:01 +00:00
David Baker 8e903c0531 Merge pull request #94 from matrix-org/dbkr/may_send_state_event
Add maySendStateEvent method, ported from react-sdk (but fixed).
2016-03-16 10:39:55 +00:00
David Baker b90984a7f6 Use member.powerLevel instead of duplicating the user power level calculation. 2016-03-16 10:38:16 +00:00
David Baker 57006b7366 Check member hasn't left the room 2016-03-16 10:35:29 +00:00
Matthew Hodgson db9ba52873 make presence work when peeking. fixes https://github.com/vector-im/vector-web/issues/780 2016-03-15 21:50:18 +00:00
Richard van der Hoff 08b49c733a Merge pull request #101 from matrix-org/rav/remove_crypto_specialcase
Clean up a codepath that was only used for crypto messages
2016-03-15 17:26:54 +00:00
David Baker 0f38764709 No point throwing the exception if we return the original promise 2016-03-15 16:17:41 +00:00
Richard van der Hoff 6040b50ceb Fix another unit test
We ought to set the transaction_id in this test too
2016-03-15 15:49:21 +00:00
Richard van der Hoff b88a207bde Fix broken unit test
Fix broken unit tests which expected echoes to get matched up when
transaction_ids weren't set
2016-03-15 15:39:29 +00:00
Richard van der Hoff 07bbe358ea Clean up a codepath that was only used for crypto messages
Transmission of encrypted messages was happening somewhat differently to
normal messages. In particular, we weren't copying the 'unsigned' field when we
got the remote-echo, which meant the 'sync' code didn't correctly match up the
echo with the original.

The separate codepath was becoming a thorn in my side, so fix things up to
bring it back in line.
2016-03-15 15:07:26 +00:00
David Baker 39a5765888 Test tghat Session.logged_out is fired 2016-03-15 14:50:37 +00:00
David Baker 9f91995f4e Fix tests by returning the original promise to avoid the extra trip around the event loop. 2016-03-15 14:15:15 +00:00
David Baker 85f2754300 Make the client object be an event emitter rather than a matrixclient to avoid us being tempted to gut wrench stuff directly into the Matrix Client. 2016-03-15 11:05:05 +00:00
David Baker 5833654aa6 Add Session.logged_out event that fires whenever the current session is no longer valid and the user needs to log in again. Also null check _syncApi before trying to stop it. 2016-03-15 10:45:08 +00:00
David Baker 899ff6cea2 Merge pull request #99 from matrix-org/dbkr/sync_dont_tightloop
Add a delay before we start polling the connectivity check endpoint
2016-03-15 10:34:44 +00:00
David Baker 3752429b65 Fix the tests to tick the clock to 'wait' for sync retries. 2016-03-14 17:49:36 +00:00
David Baker d13fbd0e3e fix lint 2016-03-14 17:13:01 +00:00
David Baker 5e18c84e53 Add a delay before we start polling the connectivity check endpoint to avoid tightlooping if the conn check succeeds but /sync etc fails. 2016-03-14 16:50:00 +00:00
Richard van der Hoff fc1d5c86f9 Merge pull request #98 from matrix-org/rav/keep_paginating
Try again if a pagination request gives us no new messages
2016-03-14 15:48:17 +00:00
Richard van der Hoff c3ea913ae8 Try again if a pagination request gives us no new messages
This is basically a workaround for https://matrix.org/jira/browse/SYN-645: if
we knew about all of the events already, we want to try again.

Fixes the second half of https://github.com/vector-im/vector-web/issues/1014
2016-03-14 14:47:29 +00:00
Richard van der Hoff f2336aaedf Merge to master before develop
This avoids picking up things which have been landed on develop since the
release process started.
2016-03-11 12:17:29 +00:00
Richard van der Hoff 16ab2fd82a Add uglifyjs to devDependencies
The release script requires it, so we better install it
2016-03-11 12:15:00 +00:00
David Baker f324e4c72f lint 2016-03-03 17:48:23 +00:00
David Baker 9328a12ccb Add maySendStateEvent method, ported from react-sdk (but fixed). Plus tests. 2016-03-03 17:44:27 +00:00
19 changed files with 16055 additions and 60 deletions
+21
View File
@@ -1,3 +1,24 @@
[0.4.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.4.2) (2016-03-17)
=====================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.4.1...v0.4.2)
* Try again if a pagination request gives us no new messages
[\#98](https://github.com/matrix-org/matrix-js-sdk/pull/98)
* Add a delay before we start polling the connectivity check endpoint
[\#99](https://github.com/matrix-org/matrix-js-sdk/pull/99)
* Clean up a codepath that was only used for crypto messages
[\#101](https://github.com/matrix-org/matrix-js-sdk/pull/101)
* Add maySendStateEvent method, ported from react-sdk (but fixed).
[\#94](https://github.com/matrix-org/matrix-js-sdk/pull/94)
* Add Session.logged_out event
[\#100](https://github.com/matrix-org/matrix-js-sdk/pull/100)
* make presence work when peeking.
[\#103](https://github.com/matrix-org/matrix-js-sdk/pull/103)
* Add RoomState.mayClientSendStateEvent()
[\#104](https://github.com/matrix-org/matrix-js-sdk/pull/104)
* Fix displaynames for member join events
[\#108](https://github.com/matrix-org/matrix-js-sdk/pull/108)
Changes in 0.4.1
================
+9 -7
View File
@@ -1,12 +1,14 @@
There is a script `release.sh` which does the following, but if you need to do
a release manually, here are the steps:
- `git checkout -b release-v0.x.x`
- Update `CHANGELOG.md` and bump `version` in `package.json`
- Create `dist` entry by running `npm run release`.
- `git add dist/0.x.x`
- Commit your changes.
- Merge `release-v0.x.x` onto `develop`.
- Merge `develop` onto `master`.
- Tag the commit: `git tag -a -m "v0.x.x"`
- Update `CHANGELOG.md`
- `npm version 0.x.x`
- Merge `release-v0.x.x` onto `master`.
- Push `master`.
- Push the tag: `git push --tags`
- `npm publish`
- Generate documentation: `npm run gendoc` (this outputs HTML to `.jsdoc`)
- Copy the documentation from `.jsdoc` to the `gh-pages` branch and update `index.html`
- Merge `master` onto `develop`.
- Push `develop`.
+15577
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+20 -26
View File
@@ -162,7 +162,7 @@ function MatrixClient(opts) {
this.credentials = {
userId: (opts.userId || null)
};
this._http = new httpApi.MatrixHttpApi(httpOpts);
this._http = new httpApi.MatrixHttpApi(this, httpOpts);
this.callList = {
// callId: MatrixCall
};
@@ -1112,6 +1112,7 @@ function _decryptMessage(client, event) {
room_id: payload.room_id,
user_id: event.getSender(),
event_id: event.getId(),
unsigned: event.getUnsigned(),
type: payload.type,
content: payload.content
}, "encrypted");
@@ -1129,6 +1130,7 @@ function _badEncryptedMessage(event, reason) {
room_id: event.getRoomId(),
user_id: event.getSender(),
event_id: event.getId(),
unsigned: event.getUnsigned(),
content: {
msgtype: "m.bad.encrypted",
body: reason,
@@ -1170,29 +1172,7 @@ function _sendEvent(client, room, event, callback) {
// FIXME: This manipulation of the room should probably be done
// inside the room class, not by the client.
var timeline = room.getTimelineForEvent(eventId);
if (timeline) {
// we've already received the event via the event stream.
// update it and remove the fake event.
//
// (This codepath is only useful for encrypted events; for
// normal events, everything here is already done when we
// got the echo).
var matchingEvent =
utils.findElement(timeline.getEvents(), function(ev) {
return ev.getId() === eventId;
}, true);
if (event.encryptedType) {
// Replace the content and type of the event with the
// plaintext that we sent to the server.
// TODO: Persist the changes if we storing events somewhere
// otherthan in memory.
matchingEvent.event.content = event.event.content;
matchingEvent.event.type = event.event.type;
}
room.removeEvents([localEventId]);
matchingEvent.status = null; // make sure it's still marked as sent
}
else {
if (!timeline) {
// we haven't yet received the event from the stream; we
// need to update the fake event with the right event id.
//
@@ -2995,8 +2975,10 @@ MatrixClient.prototype.startClient = function(opts) {
MatrixClient.prototype.stopClient = function() {
this.clientRunning = false;
// TODO: f.e. Room => self.store.storeRoom(room) ?
this._syncApi.stop();
this._syncApi = null;
if (this._syncApi) {
this._syncApi.stop();
this._syncApi = null;
}
};
function setupCallEventHandler(client) {
@@ -3432,6 +3414,18 @@ module.exports.CRYPTO_ENABLED = CRYPTO_ENABLED;
* });
*/
/**
* Fires whenever the login session the JS SDK is using is no
* longer valid and the user must log in again.
* NB. This only fires when action is required from the user, not
* when then login session can be renewed by using a refresh token.
* @event module:client~MatrixClient#"Session.logged_out"
* @example
* matrixClient.on("Session.logged_out", function(call){
* // show the login screen
* });
*/
// EventEmitter JSDocs
/**
+15 -2
View File
@@ -45,6 +45,7 @@ module.exports.PREFIX_IDENTITY_V1 = "/_matrix/identity/api/v1";
/**
* Construct a MatrixHttpApi.
* @constructor
* @param {EventEmitter} event_emitter The event emitter to use for emitting events
* @param {Object} opts The options to use for this HTTP API.
* @param {string} opts.baseUrl Required. The base client-server URL e.g.
* 'http://localhost:8008'.
@@ -60,9 +61,10 @@ module.exports.PREFIX_IDENTITY_V1 = "/_matrix/identity/api/v1";
* @param {Object} opts.extraParams Optional. Extra query parameters to send on
* requests.
*/
module.exports.MatrixHttpApi = function MatrixHttpApi(opts) {
module.exports.MatrixHttpApi = function MatrixHttpApi(event_emitter, opts) {
utils.checkObjectHasKeys(opts, ["baseUrl", "request", "prefix"]);
opts.onlyData = opts.onlyData || false;
this.event_emitter = event_emitter;
this.opts = opts;
this.uploads = [];
};
@@ -268,7 +270,18 @@ module.exports.MatrixHttpApi.prototype = {
authedRequest: function(callback, method, path, queryParams, data, localTimeoutMs) {
if (!queryParams) { queryParams = {}; }
queryParams.access_token = this.opts.accessToken;
return this.request(callback, method, path, queryParams, data, localTimeoutMs);
var self = this;
var request_promise = this.request(
callback, method, path, queryParams, data, localTimeoutMs
);
request_promise.catch(function(err) {
if (err.errcode == 'M_UNKNOWN_TOKEN') {
self.event_emitter.emit("Session.logged_out");
}
});
// return the original promise, otherwise tests break due to it having to
// go around the event loop one more time to process the result of the request
return request_promise;
},
/**
+6 -1
View File
@@ -227,7 +227,12 @@ EventTimeline.prototype.addEvent = function(event, atStart, spliceBeforeLocalEch
// it again if the prop wasn't previously set. It may also mean that
// the sender/target is updated (if the event set was a room member event)
// so we want to use the *updated* member (new avatar/name) instead.
if (!event.sender || event.getType() === "m.room.member") {
//
// However, we do NOT want to do this on member events if we're going
// back in time, else we'll set the .sender value for BEFORE the given
// member event, whereas we want to set the .sender value for the ACTUAL
// member event itself.
if (!event.sender || (event.getType() === "m.room.member" && !atStart)) {
setEventMetadata(event, stateContext, atStart);
}
}
+2
View File
@@ -143,6 +143,8 @@ module.exports.MatrixEvent.prototype = {
/**
* Get either 'content' or 'prev_content' depending on if this event is
* 'forward-looking' or not. This can be modified via event.forwardLooking.
* In practice, this means we get the chronologically earlier content value
* for this event (this method should surely be called getEarlierContent)
* <strong>This method is experimental and may change.</strong>
* @return {Object} event.content if this event is forward-looking, else
* event.prev_content.
+74
View File
@@ -145,6 +145,21 @@ RoomState.prototype.setStateEvents = function(stateEvents) {
if (event.getType() === "m.room.member") {
var userId = event.getStateKey();
// leave events apparently elide the displayname or avatar_url,
// so let's fake one up so that we don't leak user ids
// into the timeline
if (event.getContent().membership === "leave" ||
event.getContent().membership === "ban")
{
event.getContent().avatar_url =
event.getContent().avatar_url ||
event.getPrevContent().avatar_url;
event.getContent().displayname =
event.getContent().displayname ||
event.getPrevContent().displayname;
}
var member = self.members[userId];
if (!member) {
member = new RoomMember(event.getRoomId(), userId);
@@ -224,6 +239,65 @@ RoomState.prototype.getUserIdsWithDisplayName = function(displayName) {
return this._displayNameToUserIds[displayName] || [];
};
/**
* Returns true if the given MatrixClient has permission to send a state
* event of type `stateEventType` into this room.
* @param {string} type The type of state events to test
* @param {MatrixClient} The client to test permission for
* @return {boolean} true if the given client should be permitted to send
* the given type of state event into this room,
* according to the room's state.
*/
RoomState.prototype.mayClientSendStateEvent = function(stateEventType, cli) {
if (cli.isGuest()) {
return false;
}
return this.maySendStateEvent(stateEventType, cli.credentials.userId);
};
/**
* Returns true if the given user ID has permission to send a state
* event of type `stateEventType` into this room.
* @param {string} type The type of state events to test
* @param {string} userId The user ID of the user to test permission for
* @return {boolean} true if the given user ID should be permitted to send
* the given type of state event into this room,
* according to the room's state.
*/
RoomState.prototype.maySendStateEvent = function(stateEventType, userId) {
var member = this.getMember(userId);
if (!member || member.membership == 'leave') { return false; }
var power_levels_event = this.getStateEvents('m.room.power_levels', '');
var power_levels;
var events_levels = {};
var default_user_level = 0;
var user_levels = [];
var state_default = 0;
if (power_levels_event) {
power_levels = power_levels_event.getContent();
events_levels = power_levels.events || {};
default_user_level = parseInt(power_levels.users_default || 0);
user_levels = power_levels.users || {};
if (power_levels.state_default !== undefined) {
state_default = power_levels.state_default;
} else {
state_default = 50;
}
}
var state_event_level = state_default;
if (events_levels[stateEventType] !== undefined) {
state_event_level = events_levels[stateEventType];
}
return member.powerLevel >= state_event_level;
};
/**
* The RoomState class.
*/
+6 -1
View File
@@ -621,8 +621,13 @@ Room.prototype._addLiveEvents = function(events) {
this._eventIdToTimeline[events[i].getId()] = existingTimeline;
}
// replace the event source
// replace the event source, but preserve the original content
// and type in case it was encrypted (we won't be able to
// decrypt it, even though we sent it.)
var existingSource = existingEvent.event;
existingEvent.event = events[i].event;
existingEvent.event.content = existingSource.content;
existingEvent.event.type = existingSource.type;
// successfully sent.
existingEvent.status = null;
+4
View File
@@ -31,6 +31,8 @@ limitations under the License.
* @prop {string} avatarUrl The 'avatar_url' of the user if known.
* @prop {string} presence The presence enum if known.
* @prop {Number} lastActiveAgo The last time the user performed some action in ms.
* @prop {Boolean} currentlyActive Whether we should consider lastActiveAgo to be
* an approximation and that the user should be seen as active 'now'
* @prop {Object} events The events describing this user.
* @prop {MatrixEvent} events.presence The m.presence event for this user.
*/
@@ -40,6 +42,7 @@ function User(userId) {
this.displayName = userId;
this.avatarUrl = null;
this.lastActiveAgo = 0;
this.currentlyActive = false;
this.events = {
presence: null,
profile: null
@@ -79,6 +82,7 @@ User.prototype.setPresenceEvent = function(event) {
this.displayName = event.getContent().displayname;
this.avatarUrl = event.getContent().avatar_url;
this.lastActiveAgo = event.getContent().last_active_ago;
this.currentlyActive = event.getContent().currently_active;
if (eventsToFire.length > 0) {
this._updateModifiedTime();
+60 -4
View File
@@ -225,6 +225,25 @@ SyncApi.prototype.peek = function(roomId) {
response.messages.chunk, client.getEventMapper()
);
// XXX: copypasted from /sync until we kill off this
// minging v1 API stuff)
// handle presence events (User objects)
if (response.presence && utils.isArray(response.presence)) {
response.presence.map(client.getEventMapper()).forEach(
function(presenceEvent) {
var user = client.store.getUser(presenceEvent.getContent().user_id);
if (user) {
user.setPresenceEvent(presenceEvent);
}
else {
user = createNewUser(client, presenceEvent.getContent().user_id);
user.setPresenceEvent(presenceEvent);
client.store.storeUser(user);
}
client.emit("event", presenceEvent);
});
}
// set the pagination token before adding the events in case people
// fire off pagination requests in response to the Room.timeline
// events.
@@ -279,6 +298,31 @@ SyncApi.prototype._peekPoll = function(roomId, token) {
timeout: 30 * 1000,
from: token
}, undefined, 50 * 1000).done(function(res) {
// We have a problem that we get presence both from /events and /sync
// however, /sync only returns presence for users in rooms
// you're actually joined to.
// in order to be sure to get presence for all of the users in the
// peeked room, we handle presence explicitly here. This may result
// in duplicate presence events firing for some users, which is a
// performance drain, but such is life.
// XXX: copypasted from /sync until we can kill this minging v1 stuff.
res.chunk.filter(function(e) {
return e.type === "m.presence";
}).map(self.client.getEventMapper()).forEach(function(presenceEvent) {
var user = self.client.store.getUser(presenceEvent.getContent().user_id);
if (user) {
user.setPresenceEvent(presenceEvent);
}
else {
user = createNewUser(self.client, presenceEvent.getContent().user_id);
user.setPresenceEvent(presenceEvent);
self.client.store.storeUser(user);
}
self.client.emit("event", presenceEvent);
});
// strip out events which aren't for the given room_id (e.g presence)
var events = res.chunk.filter(function(e) {
return e.room_id === roomId;
@@ -382,7 +426,7 @@ SyncApi.prototype.stop = function() {
*/
SyncApi.prototype.retryImmediately = function() {
if (!this._connectionReturnedDefer) { return false; }
this._startKeepAlives();
this._startKeepAlives(0);
return true;
};
@@ -686,13 +730,25 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
};
/**
* Starts polling the connectivity check endpoint
* @param {number} delay How long to delay until the first poll.
* defaults to a short, randomised interval (to prevent
* tightlooping if /versions succeeds but /sync etc. fail).
* @return {promise}
*/
SyncApi.prototype._startKeepAlives = function() {
SyncApi.prototype._startKeepAlives = function(delay) {
if (delay === undefined) {
delay = 5000 + Math.floor(Math.random() * 5000);
}
if (this._keepAliveTimer !== null) {
clearTimeout(this._keepAliveTimer);
}
this._pokeKeepAlive();
var self = this;
self._keepAliveTimer = setTimeout(
self._pokeKeepAlive.bind(self),
delay
);
if (!this._connectionReturnedDefer) {
this._connectionReturnedDefer = q.defer();
}
@@ -948,7 +1004,7 @@ SyncApi.prototype._updateSyncState = function(newState, data) {
*/
SyncApi.prototype._onOnline = function() {
debuglog("Browser thinks we are back online");
this._startKeepAlives();
this._startKeepAlives(0);
};
function createNewUser(client, userId) {
+9 -5
View File
@@ -248,11 +248,15 @@ TimelineWindow.prototype.paginate = function(direction, size, makeRequest) {
// end of timeline
return false;
}
// recurse to advance the index into the results, but before we do, set
// makeRequest=false. It's not an absolute given that the paginate
// request returned events which we can now use, but we certainly don't
// want to get stuck in a tight loop here if things start going wrong.
return self.paginate(direction, size, false);
// recurse to advance the index into the results.
//
// If we don't get any new events, we want to make sure we keep asking
// the server for events for as long as we have a valid pagination
// token. In particular, we want to know if we've actually hit the
// start of the timeline, or if we just happened to know about all of
// the events thanks to https://matrix.org/jira/browse/SYN-645.
return self.paginate(direction, size, true);
});
tl.pendingPaginate = prom;
return prom;
+7 -4
View File
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "0.4.1",
"version": "0.4.2",
"description": "Matrix Client-Server SDK for Javascript",
"main": "index.js",
"scripts": {
@@ -10,8 +10,9 @@
"build": "jshint -c .jshint lib/ && browserify browser-index.js -o dist/browser-matrix-dev.js --ignore-missing",
"watch": "watchify browser-index.js -o dist/browser-matrix-dev.js -v",
"lint": "jshint -c .jshint lib spec && gjslint --unix_mode --disable 0131,0211,0200,0222,0212 --max_line_length 90 -r spec/ -r lib/",
"release": "npm run build && mkdir dist/$npm_package_version && uglifyjs -c -m -o dist/$npm_package_version/browser-matrix-$npm_package_version.min.js dist/browser-matrix-dev.js && cp dist/browser-matrix-dev.js dist/$npm_package_version/browser-matrix-$npm_package_version.js",
"prepublish": "git rev-parse HEAD > git-revision.txt"
"release": "npm run build && mkdir -p dist/$npm_package_version && uglifyjs -c -m -o dist/$npm_package_version/browser-matrix-$npm_package_version.min.js dist/browser-matrix-dev.js && cp dist/browser-matrix-dev.js dist/$npm_package_version/browser-matrix-$npm_package_version.js",
"prepublish": "git rev-parse HEAD > git-revision.txt",
"version": "npm run release && git add dist/$npm_package_version"
},
"repository": {
"url": "https://github.com/matrix-org/matrix-js-sdk"
@@ -32,6 +33,8 @@
"watchify": "^3.2.1",
"istanbul": "^0.3.13",
"jasmine-node": "^1.14.5",
"jshint": "^2.8.0"
"jshint": "^2.8.0",
"jsdoc": "^3.4.0",
"uglifyjs": "^2.4.10"
}
}
Executable
+102
View File
@@ -0,0 +1,102 @@
#!/bin/sh
#
# Script to perform a release of matrix-js-sdk. Performs the steps documented
# in RELEASING.md
#
# Requires githib-changelog-generator; to install, do
# pip install git+https://github.com/matrix-org/github-changelog-generator.git
set -e
USAGE="$0 [-x] vX.Y.Z"
help() {
cat <<EOF
$USAGE
-x: skip updating the changelog
EOF
}
skip_changelog=
while getopts hx f; do
case $f in
h)
help
exit 0
;;
x)
skip_changelog=1
;;
esac
done
shift `expr $OPTIND - 1`
if [ $# -ne 1 ]; then
echo "Usage: $USAGE" >&2
exit 1
fi
tag="$1"
case "$tag" in
v*) ;;
*)
echo 2>&1 "Tag $tag must start with v"
exit 1
;;
esac
# strip leading 'v' to get release
release="${tag#v}"
rel_branch="release-$tag"
cd `dirname $0`
# we might already be on the release branch, in which case, yay
if [ $(git symbolic-ref --short HEAD) != "$rel_branch" ]; then
echo "Creating release branch"
git checkout -b "$rel_branch"
fi
if [ -z "$skip_changelog" ]; then
echo "Generating changelog"
update_changelog "$release"
read -p "Edit CHANGELOG.md manually, or press enter to continue " REPLY
if [ -n "$(git ls-files --modified CHANGELOG.md)" ]; then
echo "Committing updated changelog"
git commit "CHANGELOG.md" -m "Prepare changelog for $tag"
fi
fi
# Bump package.json, build the dist, and tag
echo "npm version"
npm version "$release"
# generate the docs
echo "generating jsdocs"
npm run gendoc
echo "copying jsdocs to gh-pages branch"
git checkout gh-pages
git pull
cp -ar ".jsdoc/matrix-js-sdk/$release" .
perl -i -pe 'BEGIN {$rel=shift} $_ =~ /^<\/ul>/ && print
"<li><a href=\"${rel}/index.html\">Version ${rel}</a></li>\n"' \
$release index.html
git add "$release"
git commit --no-verify -m "Add jsdoc for $release" index.html "$release"
# merge release branch to master
echo "updating master branch"
git checkout master
git pull
git merge --ff-only "$rel_branch"
# push everything to github
git push origin master "$rel_branch" "$tag" "gh-pages"
# publish to npmjs
npm publish
@@ -279,6 +279,24 @@ describe("MatrixClient events", function() {
done();
});
});
it("should emit Session.logged_out on M_UNKNOWN_TOKEN", function(done) {
httpBackend.when("GET", "/sync").respond(401, { errcode: 'M_UNKNOWN_TOKEN' });
var sessionLoggedOutCount = 0;
client.on("Session.logged_out", function(event, member) {
sessionLoggedOutCount++;
});
client.startClient();
httpBackend.flush().done(function() {
expect(sessionLoggedOutCount).toEqual(
1, "Session.logged_out fired wrong number of times"
);
done();
});
});
});
});
+30 -8
View File
@@ -165,6 +165,7 @@ describe("MatrixClient room timelines", function() {
body: "I am a fish", user: userId, room: roomId
});
ev.event_id = eventId;
ev.unsigned = {transaction_id: "txn1"};
setNextSyncData([ev]);
client.on("sync", function(state) {
@@ -194,6 +195,7 @@ describe("MatrixClient room timelines", function() {
body: "I am a fish", user: userId, room: roomId
});
ev.event_id = eventId;
ev.unsigned = {transaction_id: "txn1"};
setNextSyncData([ev]);
client.on("sync", function(state) {
@@ -201,8 +203,7 @@ describe("MatrixClient room timelines", function() {
var room = client.getRoom(roomId);
var promise = client.sendTextMessage(roomId, "I am a fish", "txn1");
httpBackend.flush("/sync", 1).done(function() {
// expect 3rd msg, it doesn't know this is the request is just did
expect(room.timeline.length).toEqual(3);
expect(room.timeline.length).toEqual(2);
httpBackend.flush("/txn1", 1);
promise.done(function() {
expect(room.timeline.length).toEqual(2);
@@ -251,7 +252,23 @@ describe("MatrixClient room timelines", function() {
});
it("should set the right event.sender values", function(done) {
// make an m.room.member event with prev_content
// We're aiming for an eventual timeline of:
//
// 'Old Alice' joined the room
// <Old Alice> I'm old alice
// @alice:localhost changed their name from 'Old Alice' to 'Alice'
// <Alice> I'm alice
// ------^ /messages results above this point, /sync result below
// <Bob> hello
// make an m.room.member event for alice's join
var joinMshipEvent = utils.mkMembership({
mship: "join", user: userId, room: roomId, name: "Old Alice",
url: null
});
// make an m.room.member event with prev_content for alice's nick
// change
var oldMshipEvent = utils.mkMembership({
mship: "join", user: userId, room: roomId, name: userName,
url: "mxc://some/url"
@@ -262,7 +279,8 @@ describe("MatrixClient room timelines", function() {
membership: "join"
};
// set the list of events to return on scrollback
// set the list of events to return on scrollback (/messages)
// N.B. synapse returns /messages in reverse chronological order
sbEvents = [
utils.mkMessage({
user: userId, room: roomId, msg: "I'm alice"
@@ -270,19 +288,23 @@ describe("MatrixClient room timelines", function() {
oldMshipEvent,
utils.mkMessage({
user: userId, room: roomId, msg: "I'm old alice"
})
}),
joinMshipEvent,
];
client.on("sync", function(state) {
if (state !== "PREPARED") { return; }
var room = client.getRoom(roomId);
// sync response
expect(room.timeline.length).toEqual(1);
client.scrollback(room).done(function() {
expect(room.timeline.length).toEqual(4);
var oldMsg = room.timeline[0];
expect(room.timeline.length).toEqual(5);
var joinMsg = room.timeline[0];
expect(joinMsg.sender.name).toEqual("Old Alice");
var oldMsg = room.timeline[1];
expect(oldMsg.sender.name).toEqual("Old Alice");
var newMsg = room.timeline[2];
var newMsg = room.timeline[3];
expect(newMsg.sender.name).toEqual(userName);
done();
});
+5 -2
View File
@@ -223,6 +223,7 @@ describe("MatrixClient", function() {
if (state === "ERROR" && httpLookups.length > 0) {
expect(httpLookups.length).toEqual(2);
expect(client.retryImmediately()).toBe(true);
jasmine.Clock.tick(1);
} else if (state === "PREPARED" && httpLookups.length === 0) {
client.removeListener("sync", syncListener);
done();
@@ -248,6 +249,7 @@ describe("MatrixClient", function() {
expect(client.retryImmediately()).toBe(
true, "retryImmediately returned false"
);
jasmine.Clock.tick(1);
} else if (state === "SYNCING" && httpLookups.length === 0) {
client.removeListener("sync", syncListener);
done();
@@ -269,6 +271,7 @@ describe("MatrixClient", function() {
if (state === "ERROR" && httpLookups.length > 0) {
expect(httpLookups.length).toEqual(3);
expect(client.retryImmediately()).toBe(true);
jasmine.Clock.tick(1);
} else if (state === "PREPARED" && httpLookups.length === 0) {
client.removeListener("sync", syncListener);
done();
@@ -299,8 +302,8 @@ describe("MatrixClient", function() {
client.removeListener("sync", syncListener);
done();
}
// standard retry time is 4s
jasmine.Clock.tick(4001);
// standard retry time is 5 to 10 seconds
jasmine.Clock.tick(10000);
};
}
+83
View File
@@ -279,4 +279,87 @@ describe("RoomState", function() {
);
});
});
describe("maySendStateEvent", function() {
it("should say non-joined members may not send state",
function() {
expect(state.maySendStateEvent(
'm.room.name', "@nobody:nowhere"
)).toEqual(false);
});
it("should say any member may send state with no power level event",
function() {
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
});
it("should say members with power >=50 may send state with power level event " +
"but no state default",
function() {
var powerLevelEvent = {
type: "m.room.power_levels", room: roomId, user: userA, event: true,
content: {
users_default: 10,
// state_default: 50, "intentionally left blank"
events_default: 25,
users: {
}
}
};
powerLevelEvent.content.users[userA] = 50;
state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
expect(state.maySendStateEvent('m.room.name', userB)).toEqual(false);
});
it("should obey state_default",
function() {
var powerLevelEvent = {
type: "m.room.power_levels", room: roomId, user: userA, event: true,
content: {
users_default: 10,
state_default: 30,
events_default: 25,
users: {
}
}
};
powerLevelEvent.content.users[userA] = 30;
powerLevelEvent.content.users[userB] = 29;
state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
expect(state.maySendStateEvent('m.room.name', userB)).toEqual(false);
});
it("should honour explicit event power levels in the power_levels event",
function() {
var powerLevelEvent = {
type: "m.room.power_levels", room: roomId, user: userA, event: true,
content: {
events: {
"m.room.other_thing": 76
},
users_default: 10,
state_default: 50,
events_default: 25,
users: {
}
}
};
powerLevelEvent.content.users[userA] = 80;
powerLevelEvent.content.users[userB] = 50;
state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
expect(state.maySendStateEvent('m.room.name', userB)).toEqual(true);
expect(state.maySendStateEvent('m.room.other_thing', userA)).toEqual(true);
expect(state.maySendStateEvent('m.room.other_thing', userB)).toEqual(false);
});
});
});