Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ad5d07caf8 | |||
| b2d7abc0a1 | |||
| cc475e6392 | |||
| e4c6717bd5 | |||
| 53f813207e | |||
| 873fde27ac | |||
| 8d9d638953 | |||
| 2f93490054 | |||
| e22efc9dd5 | |||
| e7ac80cf2b | |||
| 4436087777 | |||
| f7bc11361c | |||
| a68b61dafe | |||
| 84c9876b3a | |||
| de864c489a | |||
| 2c277f7d96 | |||
| d0560f594d | |||
| 60b6310494 | |||
| abd27f9b75 | |||
| 3d316959f9 | |||
| 8aa3b79501 | |||
| f35409700a | |||
| b009739b9e | |||
| f007af741e | |||
| 3db4d9488b | |||
| 6b0fa84697 | |||
| 98b0cf2560 | |||
| 372759b6e4 | |||
| ec29b4ffeb | |||
| 95494933fd | |||
| 6fff29c07b | |||
| 6f7ed93b87 | |||
| 8e903c0531 | |||
| b90984a7f6 | |||
| 57006b7366 | |||
| db9ba52873 | |||
| 08b49c733a | |||
| 0f38764709 | |||
| 6040b50ceb | |||
| b88a207bde | |||
| 07bbe358ea | |||
| 39a5765888 | |||
| 9f91995f4e | |||
| 85f2754300 | |||
| 5833654aa6 | |||
| 899ff6cea2 | |||
| 3752429b65 | |||
| d13fbd0e3e | |||
| 5e18c84e53 | |||
| fc1d5c86f9 | |||
| c3ea913ae8 | |||
| f2336aaedf | |||
| 16ab2fd82a | |||
| f324e4c72f | |||
| 9328a12ccb |
@@ -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
@@ -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`.
|
||||
|
||||
Vendored
+15577
File diff suppressed because it is too large
Load Diff
+7
File diff suppressed because one or more lines are too long
+20
-26
@@ -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
@@ -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;
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user