mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 21:24:59 +08:00
Merge branch 'develop' into wmwragg/direct-chat-sublist
This commit is contained in:
commit
6d1f9003e2
221
CHANGELOG.md
221
CHANGELOG.md
@ -1,3 +1,224 @@
|
||||
Changes in [0.6.4-r1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.6.4-r1) (2016-08-12)
|
||||
=========================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.6.4...v0.6.4-r1)
|
||||
* Fix inviting multiple people
|
||||
|
||||
Changes in [0.6.4](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.6.4) (2016-08-11)
|
||||
===================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.6.3...v0.6.4)
|
||||
|
||||
* Only show Autocomplete if autocomplete is enabled
|
||||
[\#411](https://github.com/matrix-org/matrix-react-sdk/pull/411)
|
||||
* Wmwragg/room tag menu
|
||||
[\#402](https://github.com/matrix-org/matrix-react-sdk/pull/402)
|
||||
* Move guest registration into the login logic
|
||||
[\#407](https://github.com/matrix-org/matrix-react-sdk/pull/407)
|
||||
* Better support for inviting multiple people
|
||||
[\#403](https://github.com/matrix-org/matrix-react-sdk/pull/403)
|
||||
* Refactor login token
|
||||
[\#406](https://github.com/matrix-org/matrix-react-sdk/pull/406)
|
||||
* Use the current HS for guest login
|
||||
[\#405](https://github.com/matrix-org/matrix-react-sdk/pull/405)
|
||||
* Various fixes and improvements to emojification.
|
||||
[\#395](https://github.com/matrix-org/matrix-react-sdk/pull/395)
|
||||
* Fix settings resetting on refresh
|
||||
[\#404](https://github.com/matrix-org/matrix-react-sdk/pull/404)
|
||||
* Avoid flashing up login screen during guest registration
|
||||
[\#401](https://github.com/matrix-org/matrix-react-sdk/pull/401)
|
||||
* Cancel calls to rate-limited funcs on unmount
|
||||
[\#400](https://github.com/matrix-org/matrix-react-sdk/pull/400)
|
||||
* Move rehydration of MatrixClients from MatrixClientPeg to SessionLoader
|
||||
[\#399](https://github.com/matrix-org/matrix-react-sdk/pull/399)
|
||||
* Don't show integrations header if setting not on
|
||||
[\#398](https://github.com/matrix-org/matrix-react-sdk/pull/398)
|
||||
* Start to factor out session-loading magic
|
||||
[\#397](https://github.com/matrix-org/matrix-react-sdk/pull/397)
|
||||
* Hack around a react warning
|
||||
[\#396](https://github.com/matrix-org/matrix-react-sdk/pull/396)
|
||||
* Add config to hide the labs section
|
||||
[\#393](https://github.com/matrix-org/matrix-react-sdk/pull/393)
|
||||
* Dbkr/scalar
|
||||
[\#392](https://github.com/matrix-org/matrix-react-sdk/pull/392)
|
||||
* Wmwragg/mute mention state fix
|
||||
[\#390](https://github.com/matrix-org/matrix-react-sdk/pull/390)
|
||||
* Fix long freeze when opening 'historical' section
|
||||
[\#391](https://github.com/matrix-org/matrix-react-sdk/pull/391)
|
||||
* Refactor UI error effects
|
||||
[\#388](https://github.com/matrix-org/matrix-react-sdk/pull/388)
|
||||
* Implement account deactivation
|
||||
[\#381](https://github.com/matrix-org/matrix-react-sdk/pull/381)
|
||||
* Don't leave isRoomPublished as undefined
|
||||
[\#389](https://github.com/matrix-org/matrix-react-sdk/pull/389)
|
||||
* Call the logout API when we log out
|
||||
[\#377](https://github.com/matrix-org/matrix-react-sdk/pull/377)
|
||||
* feat: code cleanup & emoji replacement in composer
|
||||
[\#335](https://github.com/matrix-org/matrix-react-sdk/pull/335)
|
||||
* Add more logging to TimelinePanel-test
|
||||
[\#387](https://github.com/matrix-org/matrix-react-sdk/pull/387)
|
||||
* DevicesPanel: use device_id as a placeholder
|
||||
[\#386](https://github.com/matrix-org/matrix-react-sdk/pull/386)
|
||||
* MemberDeviceInfo: Use the device name, where available
|
||||
[\#385](https://github.com/matrix-org/matrix-react-sdk/pull/385)
|
||||
* Wmwragg/mention state menu
|
||||
[\#369](https://github.com/matrix-org/matrix-react-sdk/pull/369)
|
||||
* fix upload for video or image files where sniffing fails
|
||||
[\#383](https://github.com/matrix-org/matrix-react-sdk/pull/383)
|
||||
* fix: allow up/down normally for no completions
|
||||
[\#384](https://github.com/matrix-org/matrix-react-sdk/pull/384)
|
||||
* fix: autocomplete to use tab instead of return
|
||||
[\#382](https://github.com/matrix-org/matrix-react-sdk/pull/382)
|
||||
* strip (IRC) displayname suffix from autocomplete
|
||||
[\#375](https://github.com/matrix-org/matrix-react-sdk/pull/375)
|
||||
* Include rooms with 1 person invited
|
||||
[\#379](https://github.com/matrix-org/matrix-react-sdk/pull/379)
|
||||
* Fix 'start new direct chat'
|
||||
[\#378](https://github.com/matrix-org/matrix-react-sdk/pull/378)
|
||||
* Fix warnings from MessageComposer
|
||||
[\#376](https://github.com/matrix-org/matrix-react-sdk/pull/376)
|
||||
* New voice and video call buttons
|
||||
[\#371](https://github.com/matrix-org/matrix-react-sdk/pull/371)
|
||||
* Silence some more react warnings
|
||||
[\#373](https://github.com/matrix-org/matrix-react-sdk/pull/373)
|
||||
* Fix warnings emanating from Velociraptor elements
|
||||
[\#372](https://github.com/matrix-org/matrix-react-sdk/pull/372)
|
||||
* Wmwragg/button updates
|
||||
[\#353](https://github.com/matrix-org/matrix-react-sdk/pull/353)
|
||||
* Implement device management UI
|
||||
[\#370](https://github.com/matrix-org/matrix-react-sdk/pull/370)
|
||||
* Factor EditableTextContainer out of ChangeDisplayName
|
||||
[\#368](https://github.com/matrix-org/matrix-react-sdk/pull/368)
|
||||
* Stop the Avatar classes setting properties on <span>s
|
||||
[\#367](https://github.com/matrix-org/matrix-react-sdk/pull/367)
|
||||
* Remove relayoutOnUpdate prop on gemini-scrollbar
|
||||
[\#366](https://github.com/matrix-org/matrix-react-sdk/pull/366)
|
||||
* Fix bug where vector freezes on power level event
|
||||
[\#364](https://github.com/matrix-org/matrix-react-sdk/pull/364)
|
||||
* Refactor MatrixClientPeg
|
||||
[\#361](https://github.com/matrix-org/matrix-react-sdk/pull/361)
|
||||
* Fix 'start chat' button on MemberInfo
|
||||
[\#363](https://github.com/matrix-org/matrix-react-sdk/pull/363)
|
||||
* Bump dependency versions
|
||||
[\#362](https://github.com/matrix-org/matrix-react-sdk/pull/362)
|
||||
* Fix tab complete order properly
|
||||
[\#360](https://github.com/matrix-org/matrix-react-sdk/pull/360)
|
||||
* Add removeListener for account data listener
|
||||
[\#359](https://github.com/matrix-org/matrix-react-sdk/pull/359)
|
||||
* Set the device_id on pre-login MatrixClient
|
||||
[\#358](https://github.com/matrix-org/matrix-react-sdk/pull/358)
|
||||
* Wmwragg/mention state indicator round 2
|
||||
[\#357](https://github.com/matrix-org/matrix-react-sdk/pull/357)
|
||||
* Support for disabling/enabling URL previews per-user, per-room and per-user-
|
||||
per-room
|
||||
[\#356](https://github.com/matrix-org/matrix-react-sdk/pull/356)
|
||||
* Use HS proxy API for requestToken on adding email
|
||||
[\#336](https://github.com/matrix-org/matrix-react-sdk/pull/336)
|
||||
* Error if email already in use when resetting pw
|
||||
[\#337](https://github.com/matrix-org/matrix-react-sdk/pull/337)
|
||||
* Fix enourmous video bug
|
||||
[\#355](https://github.com/matrix-org/matrix-react-sdk/pull/355)
|
||||
* Add support for sending uploaded content as m.video
|
||||
[\#354](https://github.com/matrix-org/matrix-react-sdk/pull/354)
|
||||
* Order tab complete by most recently spoke
|
||||
[\#341](https://github.com/matrix-org/matrix-react-sdk/pull/341)
|
||||
* Wmwragg/spinner fix
|
||||
[\#350](https://github.com/matrix-org/matrix-react-sdk/pull/350)
|
||||
* Now showing three dots when hovering over the badge
|
||||
[\#352](https://github.com/matrix-org/matrix-react-sdk/pull/352)
|
||||
* Fix unpublishing room in room settings
|
||||
[\#351](https://github.com/matrix-org/matrix-react-sdk/pull/351)
|
||||
* Fix race when creating rooms where invite list can be blank
|
||||
[\#347](https://github.com/matrix-org/matrix-react-sdk/pull/347)
|
||||
* improve wording of MemberInfo's start chat button.
|
||||
[\#348](https://github.com/matrix-org/matrix-react-sdk/pull/348)
|
||||
* Revert "Amends react template and removes opening image in lightbox"
|
||||
[\#346](https://github.com/matrix-org/matrix-react-sdk/pull/346)
|
||||
* Wmwragg/modal restyle
|
||||
[\#345](https://github.com/matrix-org/matrix-react-sdk/pull/345)
|
||||
* Amends react template and removes opening image in lightbox
|
||||
[\#343](https://github.com/matrix-org/matrix-react-sdk/pull/343)
|
||||
* Remove the member list loading hack
|
||||
[\#344](https://github.com/matrix-org/matrix-react-sdk/pull/344)
|
||||
* CSS classes to colour offline users differently
|
||||
[\#342](https://github.com/matrix-org/matrix-react-sdk/pull/342)
|
||||
* Listen for the new lastPreseceTs event
|
||||
[\#340](https://github.com/matrix-org/matrix-react-sdk/pull/340)
|
||||
* Fix filtering user list by ID
|
||||
[\#339](https://github.com/matrix-org/matrix-react-sdk/pull/339)
|
||||
* Update tab completion list when we have a room
|
||||
[\#338](https://github.com/matrix-org/matrix-react-sdk/pull/338)
|
||||
* JS code style guide
|
||||
[\#330](https://github.com/matrix-org/matrix-react-sdk/pull/330)
|
||||
* Error on registration if email taken
|
||||
[\#334](https://github.com/matrix-org/matrix-react-sdk/pull/334)
|
||||
* feat: render unicode emoji as emojione images
|
||||
[\#332](https://github.com/matrix-org/matrix-react-sdk/pull/332)
|
||||
* feat: unblacklist img tags with data URIs
|
||||
[\#333](https://github.com/matrix-org/matrix-react-sdk/pull/333)
|
||||
* Autocomplete fixes
|
||||
[\#331](https://github.com/matrix-org/matrix-react-sdk/pull/331)
|
||||
* Better autocomplete
|
||||
[\#296](https://github.com/matrix-org/matrix-react-sdk/pull/296)
|
||||
* feat: add and configure eslint
|
||||
[\#329](https://github.com/matrix-org/matrix-react-sdk/pull/329)
|
||||
* Fix user links
|
||||
[\#326](https://github.com/matrix-org/matrix-react-sdk/pull/326)
|
||||
* Fix ordering of Memberlist
|
||||
[\#327](https://github.com/matrix-org/matrix-react-sdk/pull/327)
|
||||
* Display an error message if room not found
|
||||
[\#325](https://github.com/matrix-org/matrix-react-sdk/pull/325)
|
||||
* Implement device blocking
|
||||
[\#324](https://github.com/matrix-org/matrix-react-sdk/pull/324)
|
||||
* Remove /encrypt command
|
||||
[\#322](https://github.com/matrix-org/matrix-react-sdk/pull/322)
|
||||
* RoomSettings: add encryption setting
|
||||
[\#321](https://github.com/matrix-org/matrix-react-sdk/pull/321)
|
||||
* Fix a pair of warnings from RoomSettings
|
||||
[\#320](https://github.com/matrix-org/matrix-react-sdk/pull/320)
|
||||
* RoomSettings: refactor permissions calculations
|
||||
[\#319](https://github.com/matrix-org/matrix-react-sdk/pull/319)
|
||||
* Fix https://github.com/vector-im/vector-web/issues/1679
|
||||
[\#318](https://github.com/matrix-org/matrix-react-sdk/pull/318)
|
||||
* Fix /join to be consistent with the other code
|
||||
[\#317](https://github.com/matrix-org/matrix-react-sdk/pull/317)
|
||||
* UserSettings: fix the displayed version of the react-sdk
|
||||
[\#316](https://github.com/matrix-org/matrix-react-sdk/pull/316)
|
||||
* Show canonical alias in URL bar
|
||||
[\#314](https://github.com/matrix-org/matrix-react-sdk/pull/314)
|
||||
* Some basic tests for RoomView
|
||||
[\#313](https://github.com/matrix-org/matrix-react-sdk/pull/313)
|
||||
* Support for making devices unverified
|
||||
[\#315](https://github.com/matrix-org/matrix-react-sdk/pull/315)
|
||||
* Fix eventListener warning
|
||||
[\#312](https://github.com/matrix-org/matrix-react-sdk/pull/312)
|
||||
* Fix peeking and member list vanishing
|
||||
[\#307](https://github.com/matrix-org/matrix-react-sdk/pull/307)
|
||||
* Use different keys for new MessageComposerInput
|
||||
[\#311](https://github.com/matrix-org/matrix-react-sdk/pull/311)
|
||||
* Fix RTE escaping, HTML output with breaks
|
||||
[\#310](https://github.com/matrix-org/matrix-react-sdk/pull/310)
|
||||
* Fix cursor bug, persist editor mode & rte default
|
||||
[\#308](https://github.com/matrix-org/matrix-react-sdk/pull/308)
|
||||
* Rich Text Editor
|
||||
[\#292](https://github.com/matrix-org/matrix-react-sdk/pull/292)
|
||||
* Hide e2e features if not enabled
|
||||
[\#306](https://github.com/matrix-org/matrix-react-sdk/pull/306)
|
||||
* Add experimental "Labs" section to settings
|
||||
[\#305](https://github.com/matrix-org/matrix-react-sdk/pull/305)
|
||||
* Make the room directory join rooms by alias
|
||||
[\#304](https://github.com/matrix-org/matrix-react-sdk/pull/304)
|
||||
* Factor out common parts of room creation
|
||||
[\#303](https://github.com/matrix-org/matrix-react-sdk/pull/303)
|
||||
* Fix spinner-of-doom in member info for guests
|
||||
[\#302](https://github.com/matrix-org/matrix-react-sdk/pull/302)
|
||||
* Support for marking devices as verified
|
||||
[\#300](https://github.com/matrix-org/matrix-react-sdk/pull/300)
|
||||
* Make the config optional
|
||||
[\#301](https://github.com/matrix-org/matrix-react-sdk/pull/301)
|
||||
* Pass brand parameter down to Notifications
|
||||
[\#299](https://github.com/matrix-org/matrix-react-sdk/pull/299)
|
||||
* Second attempt at fixing the Velocity memory leak
|
||||
[\#298](https://github.com/matrix-org/matrix-react-sdk/pull/298)
|
||||
|
||||
Changes in [0.6.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.6.3) (2016-06-03)
|
||||
===================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.6.2...v0.6.3)
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "matrix-react-sdk",
|
||||
"version": "0.6.3",
|
||||
"version": "0.6.4-r1",
|
||||
"description": "SDK for matrix.org using React",
|
||||
"author": "matrix.org",
|
||||
"repository": {
|
||||
@ -40,7 +40,7 @@
|
||||
"linkifyjs": "^2.0.0-beta.4",
|
||||
"lodash": "^4.13.1",
|
||||
"marked": "^0.3.5",
|
||||
"matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
|
||||
"matrix-js-sdk": "0.5.5",
|
||||
"optimist": "^0.6.1",
|
||||
"q": "^1.4.1",
|
||||
"react": "^15.2.1",
|
||||
|
@ -273,8 +273,11 @@ function _onAction(payload) {
|
||||
break;
|
||||
case 'incoming_call':
|
||||
if (module.exports.getAnyActiveCall()) {
|
||||
payload.call.hangup("busy");
|
||||
return; // don't allow >1 call to be received, hangup newer one.
|
||||
// ignore multiple incoming calls. in future, we may want a line-1/line-2 setup.
|
||||
// we avoid rejecting with "busy" in case the user wants to answer it on a different device.
|
||||
// in future we could signal a "local busy" as a warning to the caller.
|
||||
// see https://github.com/vector-im/vector-web/issues/1964
|
||||
return;
|
||||
}
|
||||
|
||||
// if the runtime env doesn't do VoIP, stop here.
|
||||
|
@ -69,7 +69,7 @@ var sanitizeHtmlParams = {
|
||||
allowedAttributes: {
|
||||
// custom ones first:
|
||||
font: [ 'color' ], // custom to matrix
|
||||
a: [ 'href', 'name', 'target' ], // remote target: custom to matrix
|
||||
a: [ 'href', 'name', 'target', 'rel' ], // remote target: custom to matrix
|
||||
// We don't currently allow img itself by default, but this
|
||||
// would make sense if we did
|
||||
img: [ 'src' ],
|
||||
@ -81,7 +81,7 @@ var sanitizeHtmlParams = {
|
||||
allowedSchemesByTag: {
|
||||
img: [ 'data' ],
|
||||
},
|
||||
|
||||
|
||||
transformTags: { // custom to matrix
|
||||
// add blank targets to all hyperlinks except vector URLs
|
||||
'a': function(tagName, attribs) {
|
||||
@ -92,6 +92,7 @@ var sanitizeHtmlParams = {
|
||||
else {
|
||||
attribs.target = '_blank';
|
||||
}
|
||||
attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/
|
||||
return { tagName: tagName, attribs : attribs };
|
||||
},
|
||||
},
|
||||
|
@ -69,6 +69,7 @@ export function loadSession(opts) {
|
||||
let enableGuest = opts.enableGuest || false;
|
||||
const guestHsUrl = opts.guestHsUrl;
|
||||
const guestIsUrl = opts.guestIsUrl;
|
||||
const defaultDeviceDisplayName = opts.defaultDeviceDisplayName;
|
||||
|
||||
if (fragmentQueryParams.client_secret && fragmentQueryParams.sid) {
|
||||
// this happens during email validation: the email contains a link to the
|
||||
@ -87,7 +88,7 @@ export function loadSession(opts) {
|
||||
if (!realQueryParams.homeserver) {
|
||||
console.warn("Cannot log in with token: can't determine HS URL to use");
|
||||
} else {
|
||||
return _loginWithToken(realQueryParams);
|
||||
return _loginWithToken(realQueryParams, defaultDeviceDisplayName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,23 +112,29 @@ export function loadSession(opts) {
|
||||
}
|
||||
|
||||
if (enableGuest) {
|
||||
return _registerAsGuest(guestHsUrl, guestIsUrl);
|
||||
return _registerAsGuest(guestHsUrl, guestIsUrl, defaultDeviceDisplayName);
|
||||
}
|
||||
|
||||
// fall back to login screen
|
||||
return q();
|
||||
}
|
||||
|
||||
function _loginWithToken(queryParams) {
|
||||
function _loginWithToken(queryParams, defaultDeviceDisplayName) {
|
||||
// create a temporary MatrixClient to do the login
|
||||
var client = Matrix.createClient({
|
||||
baseUrl: queryParams.homeserver,
|
||||
});
|
||||
|
||||
return client.loginWithToken(queryParams.loginToken).then(function(data) {
|
||||
return client.login(
|
||||
"m.login.token", {
|
||||
token: queryParams.loginToken,
|
||||
initial_device_display_name: defaultDeviceDisplayName,
|
||||
},
|
||||
).then(function(data) {
|
||||
console.log("Logged in with token");
|
||||
setLoggedIn({
|
||||
userId: data.user_id,
|
||||
deviceId: data.device_id,
|
||||
accessToken: data.access_token,
|
||||
homeserverUrl: queryParams.homeserver,
|
||||
identityServerUrl: queryParams.identityServer,
|
||||
@ -139,14 +146,26 @@ function _loginWithToken(queryParams) {
|
||||
});
|
||||
}
|
||||
|
||||
function _registerAsGuest(hsUrl, isUrl) {
|
||||
function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) {
|
||||
console.log("Doing guest login on %s", hsUrl);
|
||||
|
||||
MatrixClientPeg.replaceUsingUrls(hsUrl, isUrl);
|
||||
return MatrixClientPeg.get().registerGuest().then((creds) => {
|
||||
// TODO: we should probably de-duplicate this and Signup.Login.loginAsGuest.
|
||||
// Not really sure where the right home for it is.
|
||||
|
||||
// create a temporary MatrixClient to do the login
|
||||
var client = Matrix.createClient({
|
||||
baseUrl: hsUrl,
|
||||
});
|
||||
|
||||
return client.registerGuest({
|
||||
body: {
|
||||
initial_device_display_name: defaultDeviceDisplayName,
|
||||
},
|
||||
}).then((creds) => {
|
||||
console.log("Registered as guest: %s", creds.user_id);
|
||||
setLoggedIn({
|
||||
userId: creds.user_id,
|
||||
deviceId: creds.device_id,
|
||||
accessToken: creds.access_token,
|
||||
homeserverUrl: hsUrl,
|
||||
identityServerUrl: isUrl,
|
||||
@ -166,6 +185,7 @@ function _restoreFromLocalStorage() {
|
||||
const is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org';
|
||||
const access_token = localStorage.getItem("mx_access_token");
|
||||
const user_id = localStorage.getItem("mx_user_id");
|
||||
const device_id = localStorage.getItem("mx_device_id");
|
||||
|
||||
let is_guest;
|
||||
if (localStorage.getItem("mx_is_guest") !== null) {
|
||||
@ -179,6 +199,7 @@ function _restoreFromLocalStorage() {
|
||||
console.log("Restoring session for %s", user_id);
|
||||
setLoggedIn({
|
||||
userId: user_id,
|
||||
deviceId: device_id,
|
||||
accessToken: access_token,
|
||||
homeserverUrl: hs_url,
|
||||
identityServerUrl: is_url,
|
||||
@ -206,10 +227,19 @@ export function setLoggedIn(credentials) {
|
||||
try {
|
||||
localStorage.setItem("mx_hs_url", credentials.homeserverUrl);
|
||||
localStorage.setItem("mx_is_url", credentials.identityServerUrl);
|
||||
|
||||
localStorage.setItem("mx_user_id", credentials.userId);
|
||||
localStorage.setItem("mx_access_token", credentials.accessToken);
|
||||
localStorage.setItem("mx_is_guest", JSON.stringify(credentials.guest));
|
||||
|
||||
// if we didn't get a deviceId from the login, leave mx_device_id unset,
|
||||
// rather than setting it to "undefined".
|
||||
//
|
||||
// (in this case MatrixClient doesn't bother with the crypto stuff
|
||||
// - that's fine for us).
|
||||
if (credentials.deviceId) {
|
||||
localStorage.setItem("mx_device_id", credentials.deviceId);
|
||||
}
|
||||
|
||||
console.log("Session persisted for %s", credentials.userId);
|
||||
} catch (e) {
|
||||
console.warn("Error using local storage: can't persist session!", e);
|
||||
@ -286,7 +316,7 @@ export function onLoggedOut() {
|
||||
if (hsUrl) window.localStorage.setItem("mx_hs_url", hsUrl);
|
||||
if (isUrl) window.localStorage.setItem("mx_is_url", isUrl);
|
||||
}
|
||||
_stopMatrixClient();
|
||||
stopMatrixClient();
|
||||
|
||||
dis.dispatch({action: 'on_logged_out'});
|
||||
}
|
||||
@ -294,11 +324,14 @@ export function onLoggedOut() {
|
||||
/**
|
||||
* Stop all the background processes related to the current client
|
||||
*/
|
||||
function _stopMatrixClient() {
|
||||
export function stopMatrixClient() {
|
||||
Notifier.stop();
|
||||
UserActivity.stop();
|
||||
Presence.stop();
|
||||
MatrixClientPeg.get().stopClient();
|
||||
MatrixClientPeg.get().removeAllListeners();
|
||||
MatrixClientPeg.unset();
|
||||
var cli = MatrixClientPeg.get();
|
||||
if (cli) {
|
||||
cli.stopClient();
|
||||
cli.removeAllListeners();
|
||||
MatrixClientPeg.unset();
|
||||
}
|
||||
}
|
||||
|
@ -21,21 +21,11 @@ import utils from 'matrix-js-sdk/lib/utils';
|
||||
|
||||
const localStorage = window.localStorage;
|
||||
|
||||
function deviceId() {
|
||||
// XXX: is Math.random()'s deterministicity a problem here?
|
||||
var id = Math.floor(Math.random()*16777215).toString(16);
|
||||
id = "W" + "000000".substring(id.length) + id;
|
||||
if (localStorage) {
|
||||
id = localStorage.getItem("mx_device_id") || id;
|
||||
localStorage.setItem("mx_device_id", id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
interface MatrixClientCreds {
|
||||
homeserverUrl: string,
|
||||
identityServerUrl: string,
|
||||
userId: string,
|
||||
deviceId: string,
|
||||
accessToken: string,
|
||||
guest: boolean,
|
||||
}
|
||||
@ -67,26 +57,12 @@ class MatrixClientPeg {
|
||||
this.matrixClient = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace this MatrixClientPeg's client with a client instance that has
|
||||
* Home Server / Identity Server URLs but no credentials
|
||||
*/
|
||||
replaceUsingUrls(hs_url, is_url) {
|
||||
this._replaceClient(hs_url, is_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace this MatrixClientPeg's client with a client instance that has
|
||||
* Home Server / Identity Server URLs and active credentials
|
||||
*/
|
||||
replaceUsingCreds(creds: MatrixClientCreds) {
|
||||
this._replaceClient(
|
||||
creds.homeserverUrl,
|
||||
creds.identityServerUrl,
|
||||
creds.userId,
|
||||
creds.accessToken,
|
||||
creds.guest,
|
||||
);
|
||||
this._createClient(creds);
|
||||
}
|
||||
|
||||
start() {
|
||||
@ -96,32 +72,29 @@ class MatrixClientPeg {
|
||||
this.get().startClient(opts);
|
||||
}
|
||||
|
||||
_replaceClient(hs_url, is_url, user_id, access_token, isGuest) {
|
||||
this._createClient(hs_url, is_url, user_id, access_token, isGuest);
|
||||
}
|
||||
|
||||
getCredentials(): MatrixClientCreds {
|
||||
return {
|
||||
homeserverUrl: this.matrixClient.baseUrl,
|
||||
identityServerUrl: this.matrixClient.idBaseUrl,
|
||||
userId: this.matrixClient.credentials.userId,
|
||||
deviceId: this.matrixClient.getDeviceId(),
|
||||
accessToken: this.matrixClient.getAccessToken(),
|
||||
guest: this.matrixClient.isGuest(),
|
||||
};
|
||||
}
|
||||
|
||||
_createClient(hs_url, is_url, user_id, access_token, isGuest) {
|
||||
_createClient(creds: MatrixClientCreds) {
|
||||
var opts = {
|
||||
baseUrl: hs_url,
|
||||
idBaseUrl: is_url,
|
||||
accessToken: access_token,
|
||||
userId: user_id,
|
||||
baseUrl: creds.homeserverUrl,
|
||||
idBaseUrl: creds.identityServerUrl,
|
||||
accessToken: creds.accessToken,
|
||||
userId: creds.userId,
|
||||
deviceId: creds.deviceId,
|
||||
timelineSupport: true,
|
||||
};
|
||||
|
||||
if (localStorage) {
|
||||
opts.sessionStore = new Matrix.WebStorageSessionStore(localStorage);
|
||||
opts.deviceId = deviceId();
|
||||
}
|
||||
|
||||
this.matrixClient = Matrix.createClient(opts);
|
||||
@ -130,7 +103,7 @@ class MatrixClientPeg {
|
||||
// potential number of event listeners is quite high.
|
||||
this.matrixClient.setMaxListeners(500);
|
||||
|
||||
this.matrixClient.setGuest(Boolean(isGuest));
|
||||
this.matrixClient.setGuest(Boolean(creds.guest));
|
||||
}
|
||||
}
|
||||
|
||||
|
164
src/RoomNotifs.js
Normal file
164
src/RoomNotifs.js
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import MatrixClientPeg from './MatrixClientPeg';
|
||||
import PushProcessor from 'matrix-js-sdk/lib/pushprocessor';
|
||||
import q from 'q';
|
||||
|
||||
export const ALL_MESSAGES_LOUD = 'all_messages_loud';
|
||||
export const ALL_MESSAGES = 'all_messages';
|
||||
export const MENTIONS_ONLY = 'mentions_only';
|
||||
export const MUTE = 'mute';
|
||||
|
||||
export function getRoomNotifsState(roomId) {
|
||||
if (MatrixClientPeg.get().isGuest()) return RoomNotifs.ALL_MESSAGES;
|
||||
|
||||
// look through the override rules for a rule affecting this room:
|
||||
// if one exists, it will take precedence.
|
||||
const muteRule = findOverrideMuteRule(roomId);
|
||||
if (muteRule) {
|
||||
return MUTE;
|
||||
}
|
||||
|
||||
// for everything else, look at the room rule.
|
||||
const roomRule = MatrixClientPeg.get().getRoomPushRule('global', roomId);
|
||||
|
||||
// XXX: We have to assume the default is to notify for all messages
|
||||
// (in particular this will be 'wrong' for one to one rooms because
|
||||
// they will notify loudly for all messages)
|
||||
if (!roomRule || !roomRule.enabled) return ALL_MESSAGES;
|
||||
|
||||
// a mute at the room level will still allow mentions
|
||||
// to notify
|
||||
if (isMuteRule(roomRule)) return MENTIONS_ONLY;
|
||||
|
||||
const actionsObject = PushProcessor.actionListToActionsObject(roomRule.actions);
|
||||
if (actionsObject.tweaks.sound) return ALL_MESSAGES_LOUD;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function setRoomNotifsState(roomId, newState) {
|
||||
if (newState == MUTE) {
|
||||
return setRoomNotifsStateMuted(roomId);
|
||||
} else {
|
||||
return setRoomNotifsStateUnmuted(roomId, newState);
|
||||
}
|
||||
}
|
||||
|
||||
function setRoomNotifsStateMuted(roomId) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const promises = [];
|
||||
|
||||
// delete the room rule
|
||||
const roomRule = cli.getRoomPushRule('global', roomId);
|
||||
if (roomRule) {
|
||||
promises.push(cli.deletePushRule('global', 'room', roomRule.rule_id));
|
||||
}
|
||||
|
||||
// add/replace an override rule to squelch everything in this room
|
||||
// NB. We use the room ID as the name of this rule too, although this
|
||||
// is an override rule, not a room rule: it still pertains to this room
|
||||
// though, so using the room ID as the rule ID is logical and prevents
|
||||
// duplicate copies of the rule.
|
||||
promises.push(cli.addPushRule('global', 'override', roomId, {
|
||||
conditions: [
|
||||
{
|
||||
kind: 'event_match',
|
||||
key: 'room_id',
|
||||
pattern: roomId,
|
||||
}
|
||||
],
|
||||
actions: [
|
||||
'dont_notify',
|
||||
]
|
||||
}));
|
||||
|
||||
return q.all(promises);
|
||||
}
|
||||
|
||||
function setRoomNotifsStateUnmuted(roomId, newState) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const promises = [];
|
||||
|
||||
const overrideMuteRule = findOverrideMuteRule(roomId);
|
||||
if (overrideMuteRule) {
|
||||
promises.push(cli.deletePushRule('global', 'override', overrideMuteRule.rule_id));
|
||||
}
|
||||
|
||||
if (newState == 'all_messages') {
|
||||
const roomRule = cli.getRoomPushRule('global', roomId);
|
||||
if (roomRule) {
|
||||
promises.push(cli.deletePushRule('global', 'room', roomRule.rule_id));
|
||||
}
|
||||
} else if (newState == 'mentions_only') {
|
||||
promises.push(cli.addPushRule('global', 'room', roomId, {
|
||||
actions: [
|
||||
'dont_notify',
|
||||
]
|
||||
}));
|
||||
// https://matrix.org/jira/browse/SPEC-400
|
||||
promises.push(cli.setPushRuleEnabled('global', 'room', roomId, true));
|
||||
} else if ('all_messages_loud') {
|
||||
promises.push(cli.addPushRule('global', 'room', roomId, {
|
||||
actions: [
|
||||
'notify',
|
||||
{
|
||||
set_tweak: 'sound',
|
||||
value: 'default',
|
||||
}
|
||||
]
|
||||
}));
|
||||
// https://matrix.org/jira/browse/SPEC-400
|
||||
promises.push(cli.setPushRuleEnabled('global', 'room', roomId, true));
|
||||
}
|
||||
|
||||
return q.all(promises);
|
||||
}
|
||||
|
||||
function findOverrideMuteRule(roomId) {
|
||||
for (const rule of MatrixClientPeg.get().pushRules['global'].override) {
|
||||
if (isRuleForRoom(roomId, rule)) {
|
||||
if (isMuteRule(rule) && rule.enabled) {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isRuleForRoom(roomId, rule) {
|
||||
if (rule.conditions.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
const cond = rule.conditions[0];
|
||||
if (
|
||||
cond.kind == 'event_match' &&
|
||||
cond.key == 'room_id' &&
|
||||
cond.pattern == roomId
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isMuteRule(rule) {
|
||||
return (
|
||||
rule.actions.length == 1 &&
|
||||
rule.actions[0] == 'dont_notify'
|
||||
);
|
||||
}
|
||||
|
@ -34,8 +34,10 @@ class ScalarAuthClient {
|
||||
defer.reject(err);
|
||||
} else if (response.statusCode / 100 !== 2) {
|
||||
defer.reject({statusCode: response.statusCode});
|
||||
} else if (!body || !body.scalar_token) {
|
||||
defer.reject(new Error("Missing scalar_token in response"));
|
||||
} else {
|
||||
defer.resolve(body.access_token);
|
||||
defer.resolve(body.scalar_token);
|
||||
}
|
||||
});
|
||||
|
||||
|
109
src/Signup.js
109
src/Signup.js
@ -1,4 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
import Matrix from "matrix-js-sdk";
|
||||
|
||||
var MatrixClientPeg = require("./MatrixClientPeg");
|
||||
var SignupStages = require("./SignupStages");
|
||||
var dis = require("./dispatcher");
|
||||
@ -11,9 +14,10 @@ const EMAIL_STAGE_TYPE = "m.login.email.identity";
|
||||
* storage of HS/IS URLs.
|
||||
*/
|
||||
class Signup {
|
||||
constructor(hsUrl, isUrl) {
|
||||
constructor(hsUrl, isUrl, opts) {
|
||||
this._hsUrl = hsUrl;
|
||||
this._isUrl = isUrl;
|
||||
this._defaultDeviceDisplayName = opts.defaultDeviceDisplayName;
|
||||
}
|
||||
|
||||
getHomeserverUrl() {
|
||||
@ -31,14 +35,25 @@ class Signup {
|
||||
setIdentityServerUrl(isUrl) {
|
||||
this._isUrl = isUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a temporary MatrixClient, which can be used for login or register
|
||||
* requests.
|
||||
*/
|
||||
_createTemporaryClient() {
|
||||
return Matrix.createClient({
|
||||
baseUrl: this._hsUrl,
|
||||
idBaseUrl: this._isUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registration logic class
|
||||
*/
|
||||
class Register extends Signup {
|
||||
constructor(hsUrl, isUrl) {
|
||||
super(hsUrl, isUrl);
|
||||
constructor(hsUrl, isUrl, opts) {
|
||||
super(hsUrl, isUrl, opts);
|
||||
this.setStep("START");
|
||||
this.data = null; // from the server
|
||||
// random other stuff (e.g. query params, NOT params from the server)
|
||||
@ -106,19 +121,11 @@ class Register extends Signup {
|
||||
this.email = email;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
|
||||
// feels a bit wrong to be clobbering the global client for something we
|
||||
// don't even know if it'll work, but we'll leave this here for now to
|
||||
// not complicate matters further. It would be nicer to isolate this
|
||||
// logic entirely from the rest of the app though.
|
||||
MatrixClientPeg.replaceUsingUrls(
|
||||
this._hsUrl,
|
||||
this._isUrl
|
||||
);
|
||||
return this._tryRegister();
|
||||
const client = this._createTemporaryClient();
|
||||
return this._tryRegister(client);
|
||||
}
|
||||
|
||||
_tryRegister(authDict, poll_for_success) {
|
||||
_tryRegister(client, authDict, poll_for_success) {
|
||||
var self = this;
|
||||
|
||||
var bindEmail;
|
||||
@ -129,7 +136,8 @@ class Register extends Signup {
|
||||
bindEmail = true;
|
||||
}
|
||||
|
||||
return MatrixClientPeg.get().register(
|
||||
// TODO need to figure out how to send the device display name to /register.
|
||||
return client.register(
|
||||
this.username, this.password, this.params.sessionId, authDict, bindEmail,
|
||||
this.guestAccessToken
|
||||
).then(function(result) {
|
||||
@ -152,7 +160,7 @@ class Register extends Signup {
|
||||
console.log("Active flow => %s", JSON.stringify(flow));
|
||||
var flowStage = self.firstUncompletedStage(flow);
|
||||
if (flowStage != self.activeStage) {
|
||||
return self.startStage(flowStage).catch(function(err) {
|
||||
return self._startStage(client, flowStage).catch(function(err) {
|
||||
self.setStep('START');
|
||||
throw err;
|
||||
});
|
||||
@ -161,7 +169,7 @@ class Register extends Signup {
|
||||
}
|
||||
if (poll_for_success) {
|
||||
return q.delay(5000).then(function() {
|
||||
return self._tryRegister(authDict, poll_for_success);
|
||||
return self._tryRegister(client, authDict, poll_for_success);
|
||||
});
|
||||
} else {
|
||||
throw new Error("Authorisation failed!");
|
||||
@ -201,7 +209,7 @@ class Register extends Signup {
|
||||
return completed.indexOf(stageType) !== -1;
|
||||
}
|
||||
|
||||
startStage(stageName) {
|
||||
_startStage(client, stageName) {
|
||||
var self = this;
|
||||
this.setStep(`STEP_${stageName}`);
|
||||
var StageClass = SignupStages[stageName];
|
||||
@ -210,12 +218,12 @@ class Register extends Signup {
|
||||
throw new Error("Unknown stage: " + stageName);
|
||||
}
|
||||
|
||||
var stage = new StageClass(MatrixClientPeg.get(), this);
|
||||
var stage = new StageClass(client, this);
|
||||
this.activeStage = stage;
|
||||
return stage.complete().then(function(request) {
|
||||
if (request.auth) {
|
||||
console.log("Stage %s is returning an auth dict", stageName);
|
||||
return self._tryRegister(request.auth, request.poll_for_success);
|
||||
return self._tryRegister(client, request.auth, request.poll_for_success);
|
||||
}
|
||||
else {
|
||||
// never resolve the promise chain. This is for things like email auth
|
||||
@ -263,14 +271,6 @@ class Register extends Signup {
|
||||
}
|
||||
|
||||
recheckState() {
|
||||
// feels a bit wrong to be clobbering the global client for something we
|
||||
// don't even know if it'll work, but we'll leave this here for now to
|
||||
// not complicate matters further. It would be nicer to isolate this
|
||||
// logic entirely from the rest of the app though.
|
||||
MatrixClientPeg.replaceUsingUrls(
|
||||
this._hsUrl,
|
||||
this._isUrl
|
||||
);
|
||||
// We've been given a bunch of data from a previous register step,
|
||||
// this only happens for email auth currently. It's kinda ming we need
|
||||
// to know this though. A better solution would be to ask the stages if
|
||||
@ -281,7 +281,8 @@ class Register extends Signup {
|
||||
);
|
||||
|
||||
if (this.params.hasEmailInfo) {
|
||||
this.registrationPromise = this.startStage(EMAIL_STAGE_TYPE);
|
||||
const client = this._createTemporaryClient();
|
||||
this.registrationPromise = this._startStage(client, EMAIL_STAGE_TYPE);
|
||||
}
|
||||
return this.registrationPromise;
|
||||
}
|
||||
@ -296,8 +297,8 @@ class Register extends Signup {
|
||||
|
||||
|
||||
class Login extends Signup {
|
||||
constructor(hsUrl, isUrl, fallbackHsUrl) {
|
||||
super(hsUrl, isUrl);
|
||||
constructor(hsUrl, isUrl, fallbackHsUrl, opts) {
|
||||
super(hsUrl, isUrl, opts);
|
||||
this._fallbackHsUrl = fallbackHsUrl;
|
||||
this._currentFlowIndex = 0;
|
||||
this._flows = [];
|
||||
@ -305,15 +306,8 @@ class Login extends Signup {
|
||||
|
||||
getFlows() {
|
||||
var self = this;
|
||||
// feels a bit wrong to be clobbering the global client for something we
|
||||
// don't even know if it'll work, but we'll leave this here for now to
|
||||
// not complicate matters further. It would be nicer to isolate this
|
||||
// logic entirely from the rest of the app though.
|
||||
MatrixClientPeg.replaceUsingUrls(
|
||||
this._hsUrl,
|
||||
this._isUrl
|
||||
);
|
||||
return MatrixClientPeg.get().loginFlows().then(function(result) {
|
||||
var client = this._createTemporaryClient();
|
||||
return client.loginFlows().then(function(result) {
|
||||
self._flows = result.flows;
|
||||
self._currentFlowIndex = 0;
|
||||
// technically the UI should display options for all flows for the
|
||||
@ -334,10 +328,15 @@ class Login extends Signup {
|
||||
}
|
||||
|
||||
loginAsGuest() {
|
||||
MatrixClientPeg.replaceUsingUrls(this._hsUrl, this._isUrl);
|
||||
return MatrixClientPeg.get().registerGuest().then((creds) => {
|
||||
var client = this._createTemporaryClient();
|
||||
return client.registerGuest({
|
||||
body: {
|
||||
initial_device_display_name: this._defaultDeviceDisplayName,
|
||||
},
|
||||
}).then((creds) => {
|
||||
return {
|
||||
userId: creds.user_id,
|
||||
deviceId: creds.device_id,
|
||||
accessToken: creds.access_token,
|
||||
homeserverUrl: this._hsUrl,
|
||||
identityServerUrl: this._isUrl,
|
||||
@ -357,7 +356,8 @@ class Login extends Signup {
|
||||
var self = this;
|
||||
var isEmail = username.indexOf("@") > 0;
|
||||
var loginParams = {
|
||||
password: pass
|
||||
password: pass,
|
||||
initial_device_display_name: this._defaultDeviceDisplayName,
|
||||
};
|
||||
if (isEmail) {
|
||||
loginParams.medium = 'email';
|
||||
@ -366,11 +366,13 @@ class Login extends Signup {
|
||||
loginParams.user = username;
|
||||
}
|
||||
|
||||
return MatrixClientPeg.get().login('m.login.password', loginParams).then(function(data) {
|
||||
var client = this._createTemporaryClient();
|
||||
return client.login('m.login.password', loginParams).then(function(data) {
|
||||
return q({
|
||||
homeserverUrl: self._hsUrl,
|
||||
identityServerUrl: self._isUrl,
|
||||
userId: data.user_id,
|
||||
deviceId: data.device_id,
|
||||
accessToken: data.access_token
|
||||
});
|
||||
}, function(error) {
|
||||
@ -384,25 +386,20 @@ class Login extends Signup {
|
||||
'Incorrect username and/or password.'
|
||||
);
|
||||
if (self._fallbackHsUrl) {
|
||||
// as per elsewhere, it would be much nicer to not replace the global
|
||||
// client just to try an alternate HS
|
||||
MatrixClientPeg.replaceUsingUrls(
|
||||
self._fallbackHsUrl,
|
||||
self._isUrl
|
||||
);
|
||||
return MatrixClientPeg.get().login('m.login.password', loginParams).then(function(data) {
|
||||
var fbClient = Matrix.createClient({
|
||||
baseUrl: self._fallbackHsUrl,
|
||||
idBaseUrl: this._isUrl,
|
||||
});
|
||||
|
||||
return fbClient.login('m.login.password', loginParams).then(function(data) {
|
||||
return q({
|
||||
homeserverUrl: self._fallbackHsUrl,
|
||||
identityServerUrl: self._isUrl,
|
||||
userId: data.user_id,
|
||||
deviceId: data.device_id,
|
||||
accessToken: data.access_token
|
||||
});
|
||||
}, function(fallback_error) {
|
||||
// We also have to put the default back again if it fails...
|
||||
MatrixClientPeg.replaceUsingUrls(
|
||||
this._hsUrl,
|
||||
this._isUrl
|
||||
);
|
||||
// throw the original error
|
||||
throw error;
|
||||
});
|
||||
|
@ -94,7 +94,7 @@ CommandEntry.fromCommands = function(commandArray) {
|
||||
|
||||
class MemberEntry extends Entry {
|
||||
constructor(member) {
|
||||
super(member.name || member.userId);
|
||||
super((member.name || member.userId).replace(' (IRC)', ''));
|
||||
this.member = member;
|
||||
this.kind = 'member';
|
||||
}
|
||||
|
@ -58,6 +58,10 @@ module.exports = React.createClass({
|
||||
|
||||
// called when the session load completes
|
||||
onLoadCompleted: React.PropTypes.func,
|
||||
|
||||
// displayname, if any, to set on the device when logging
|
||||
// in/registering.
|
||||
defaultDeviceDisplayName: React.PropTypes.string,
|
||||
},
|
||||
|
||||
PageTypes: {
|
||||
@ -185,6 +189,7 @@ module.exports = React.createClass({
|
||||
enableGuest: this.props.enableGuest,
|
||||
guestHsUrl: this.getCurrentHsUrl(),
|
||||
guestIsUrl: this.getCurrentIsUrl(),
|
||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||
}).done(()=>{
|
||||
// stuff this through the dispatcher so that it happens
|
||||
// after the on_logged_in action.
|
||||
@ -193,7 +198,7 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this._stopMatrixClient();
|
||||
Lifecycle.stopMatrixClient();
|
||||
dis.unregister(this.dispatcherRef);
|
||||
document.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("focus", this.onFocus);
|
||||
@ -601,16 +606,6 @@ module.exports = React.createClass({
|
||||
});
|
||||
},
|
||||
|
||||
// stop all the background processes related to the current client
|
||||
_stopMatrixClient: function() {
|
||||
Notifier.stop();
|
||||
UserActivity.stop();
|
||||
Presence.stop();
|
||||
MatrixClientPeg.get().stopClient();
|
||||
MatrixClientPeg.get().removeAllListeners();
|
||||
MatrixClientPeg.unset();
|
||||
},
|
||||
|
||||
onKeyDown: function(ev) {
|
||||
/*
|
||||
// Remove this for now as ctrl+alt = alt-gr so this breaks keyboards which rely on alt-gr for numbers
|
||||
@ -935,10 +930,8 @@ module.exports = React.createClass({
|
||||
var NewVersionBar = sdk.getComponent('globals.NewVersionBar');
|
||||
var ForgotPassword = sdk.getComponent('structures.login.ForgotPassword');
|
||||
|
||||
// work out the HS URL prompts we should show for
|
||||
|
||||
console.log("rendering; loading="+this.state.loading+"; screen="+this.state.screen +
|
||||
"; logged_in="+this.state.logged_in+"; ready="+this.state.ready);
|
||||
// console.log("rendering; loading="+this.state.loading+"; screen="+this.state.screen +
|
||||
// "; logged_in="+this.state.logged_in+"; ready="+this.state.ready);
|
||||
|
||||
if (this.state.loading) {
|
||||
var Spinner = sdk.getComponent('elements.Spinner');
|
||||
@ -1051,6 +1044,7 @@ module.exports = React.createClass({
|
||||
customHsUrl={this.getCurrentHsUrl()}
|
||||
customIsUrl={this.getCurrentIsUrl()}
|
||||
registrationUrl={this.props.registrationUrl}
|
||||
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
||||
onLoggedIn={this.onRegistered}
|
||||
onLoginClick={this.onLoginClick}
|
||||
onRegisterClick={this.onRegisterClick}
|
||||
@ -1077,6 +1071,7 @@ module.exports = React.createClass({
|
||||
customHsUrl={this.getCurrentHsUrl()}
|
||||
customIsUrl={this.getCurrentIsUrl()}
|
||||
fallbackHsUrl={this.getFallbackHsUrl()}
|
||||
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
||||
onForgotPasswordClick={this.onForgotPasswordClick}
|
||||
enableGuest={this.props.enableGuest}
|
||||
onCancelClick={this.guestCreds ? this.onReturnToGuestClick : null}
|
||||
|
@ -44,6 +44,8 @@ module.exports = React.createClass({
|
||||
// different home server without confusing users.
|
||||
fallbackHsUrl: React.PropTypes.string,
|
||||
|
||||
defaultDeviceDisplayName: React.PropTypes.string,
|
||||
|
||||
// login shouldn't know or care how registration is done.
|
||||
onRegisterClick: React.PropTypes.func.isRequired,
|
||||
|
||||
@ -136,7 +138,9 @@ module.exports = React.createClass({
|
||||
|
||||
var fallbackHsUrl = hsUrl == this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
|
||||
|
||||
var loginLogic = new Signup.Login(hsUrl, isUrl, fallbackHsUrl);
|
||||
var loginLogic = new Signup.Login(hsUrl, isUrl, fallbackHsUrl, {
|
||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||
});
|
||||
this._loginLogic = loginLogic;
|
||||
|
||||
loginLogic.getFlows().then(function(flows) {
|
||||
|
@ -45,6 +45,9 @@ module.exports = React.createClass({
|
||||
email: React.PropTypes.string,
|
||||
username: React.PropTypes.string,
|
||||
guestAccessToken: React.PropTypes.string,
|
||||
|
||||
defaultDeviceDisplayName: React.PropTypes.string,
|
||||
|
||||
// registration shouldn't know or care how login is done.
|
||||
onLoginClick: React.PropTypes.func.isRequired,
|
||||
onCancelClick: React.PropTypes.func
|
||||
@ -71,7 +74,9 @@ module.exports = React.createClass({
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
// attach this to the instance rather than this.state since it isn't UI
|
||||
this.registerLogic = new Signup.Register(
|
||||
this.props.customHsUrl, this.props.customIsUrl
|
||||
this.props.customHsUrl, this.props.customIsUrl, {
|
||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||
}
|
||||
);
|
||||
this.registerLogic.setClientSecret(this.props.clientSecret);
|
||||
this.registerLogic.setSessionId(this.props.sessionId);
|
||||
@ -154,6 +159,7 @@ module.exports = React.createClass({
|
||||
}
|
||||
self.props.onLoggedIn({
|
||||
userId: response.user_id,
|
||||
deviceId: response.device_id,
|
||||
homeserverUrl: self.registerLogic.getHomeserverUrl(),
|
||||
identityServerUrl: self.registerLogic.getIdentityServerUrl(),
|
||||
accessToken: response.access_token
|
||||
@ -279,7 +285,7 @@ module.exports = React.createClass({
|
||||
|
||||
var returnToAppJsx;
|
||||
if (this.props.onCancelClick) {
|
||||
returnToAppJsx =
|
||||
returnToAppJsx =
|
||||
<a className="mx_Login_create" onClick={this.props.onCancelClick} href="#">
|
||||
Return to app
|
||||
</a>
|
||||
|
@ -60,7 +60,7 @@ module.exports = React.createClass({
|
||||
return (
|
||||
<span className="mx_MFileBody">
|
||||
<div className="mx_MImageBody_download">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank" rel="noopener">
|
||||
<TintableSvg src="img/download.svg" width="12" height="14"/>
|
||||
Download {text}
|
||||
</a>
|
||||
|
@ -134,7 +134,7 @@ module.exports = React.createClass({
|
||||
onMouseLeave={this.onImageLeave} />
|
||||
</a>
|
||||
<div className="mx_MImageBody_download">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank" rel="noopener">
|
||||
<TintableSvg src="img/download.svg" width="12" height="14"/>
|
||||
Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
|
||||
</a>
|
||||
|
@ -123,7 +123,7 @@ module.exports = React.createClass({
|
||||
<div className="mx_LinkPreviewWidget" >
|
||||
{ img }
|
||||
<div className="mx_LinkPreviewWidget_caption">
|
||||
<div className="mx_LinkPreviewWidget_title"><a href={ this.props.link } target="_blank">{ p["og:title"] }</a></div>
|
||||
<div className="mx_LinkPreviewWidget_title"><a href={ this.props.link } target="_blank" rel="noopener">{ p["og:title"] }</a></div>
|
||||
<div className="mx_LinkPreviewWidget_siteName">{ p["og:site_name"] ? (" - " + p["og:site_name"]) : null }</div>
|
||||
<div className="mx_LinkPreviewWidget_description" ref="description">
|
||||
{ p["og:description"] }
|
||||
|
@ -67,6 +67,11 @@ module.exports = React.createClass({
|
||||
componentWillMount: function() {
|
||||
this._cancelDeviceList = null;
|
||||
|
||||
// only display the devices list if our client supports E2E *and* the
|
||||
// feature is enabled in the user settings
|
||||
this._enableDevices = MatrixClientPeg.get().isCryptoEnabled() &&
|
||||
UserSettingsStore.isFeatureEnabled("e2e_encryption");
|
||||
|
||||
this.setState({
|
||||
existingOneToOneRoomId: this.getExistingOneToOneRoomId()
|
||||
});
|
||||
@ -147,6 +152,10 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
onDeviceVerificationChanged: function(userId, device) {
|
||||
if (!this._enableDevices) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (userId == this.props.member.userId) {
|
||||
// no need to re-download the whole thing; just update our copy of
|
||||
// the list.
|
||||
@ -170,6 +179,10 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
_downloadDeviceList: function(member) {
|
||||
if (!this._enableDevices) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cancelled = false;
|
||||
this._cancelDeviceList = function() { cancelled = true; }
|
||||
|
||||
@ -532,7 +545,7 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
_renderDevices: function() {
|
||||
if (!UserSettingsStore.isFeatureEnabled("e2e_encryption")) {
|
||||
if (!this._enableDevices) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,7 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
_doInvite(address) {
|
||||
Invite.inviteToRoom(self.props.roomId, address).catch((err) => {
|
||||
Invite.inviteToRoom(this.props.roomId, address).catch((err) => {
|
||||
if (err !== null) {
|
||||
console.error("Failed to invite: %s", JSON.stringify(err));
|
||||
if (err.errcode == 'M_FORBIDDEN') {
|
||||
@ -196,7 +196,7 @@ module.exports = React.createClass({
|
||||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
self.setState({
|
||||
this.setState({
|
||||
inviting: false
|
||||
});
|
||||
// XXX: hacky focus on the invite box
|
||||
@ -207,7 +207,7 @@ module.exports = React.createClass({
|
||||
}
|
||||
}, 0);
|
||||
}).done();
|
||||
self.setState({
|
||||
this.setState({
|
||||
inviting: true
|
||||
});
|
||||
},
|
||||
@ -283,7 +283,7 @@ module.exports = React.createClass({
|
||||
if (inputs.length == 1) {
|
||||
// for a single address, we just send the invite
|
||||
promise.done(() => {
|
||||
this.doInvite(inputs[0]);
|
||||
this._doInvite(inputs[0]);
|
||||
});
|
||||
} else {
|
||||
// if there are several, display the confirmation/progress dialog
|
||||
|
@ -47,16 +47,6 @@ module.exports = React.createClass({
|
||||
tags[tagName] = ['yep'];
|
||||
});
|
||||
|
||||
var areNotifsMuted = false;
|
||||
if (!MatrixClientPeg.get().isGuest()) {
|
||||
var roomPushRule = MatrixClientPeg.get().getRoomPushRule("global", this.props.room.roomId);
|
||||
if (roomPushRule) {
|
||||
if (0 <= roomPushRule.actions.indexOf("dont_notify")) {
|
||||
areNotifsMuted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: this._yankValueFromEvent("m.room.name", "name"),
|
||||
topic: this._yankValueFromEvent("m.room.topic", "topic"),
|
||||
@ -66,7 +56,6 @@ module.exports = React.createClass({
|
||||
power_levels_changed: false,
|
||||
tags_changed: false,
|
||||
tags: tags,
|
||||
areNotifsMuted: areNotifsMuted,
|
||||
// isRoomPublished is loaded async in componentWillMount so when the component
|
||||
// inits, the saved value will always be undefined, however getInitialState()
|
||||
// is also called from the saving code so we must return the correct value here
|
||||
@ -188,12 +177,6 @@ module.exports = React.createClass({
|
||||
}
|
||||
|
||||
|
||||
if (this.state.areNotifsMuted !== originalState.areNotifsMuted) {
|
||||
promises.push(MatrixClientPeg.get().setRoomMutePushRule(
|
||||
"global", roomId, this.state.areNotifsMuted
|
||||
));
|
||||
}
|
||||
|
||||
// power levels
|
||||
var powerLevels = this._getPowerLevels();
|
||||
if (powerLevels) {
|
||||
@ -647,12 +630,6 @@ module.exports = React.createClass({
|
||||
{ tagsSection }
|
||||
|
||||
<div className="mx_RoomSettings_toggles">
|
||||
<label>
|
||||
<input type="checkbox" disabled={ cli.isGuest() }
|
||||
onChange={this._onToggle.bind(this, "areNotifsMuted", true, false)}
|
||||
defaultChecked={this.state.areNotifsMuted}/>
|
||||
'Mention only' notifications for this room
|
||||
</label>
|
||||
<div className="mx_RoomSettings_settings">
|
||||
<h3>Who can access this room?</h3>
|
||||
{ inviteGuestWarning }
|
||||
|
@ -22,6 +22,7 @@ var dis = require("../../../dispatcher");
|
||||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
var sdk = require('../../../index');
|
||||
var ContextualMenu = require('../../structures/ContextualMenu');
|
||||
var RoomNotifs = require('../../../RoomNotifs');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomTile',
|
||||
@ -43,43 +44,41 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var areNotifsMuted = false;
|
||||
var cli = MatrixClientPeg.get();
|
||||
if (!cli.isGuest()) {
|
||||
var roomPushRule = cli.getRoomPushRule("global", this.props.room.roomId);
|
||||
if (roomPushRule) {
|
||||
if (0 <= roomPushRule.actions.indexOf("dont_notify")) {
|
||||
areNotifsMuted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return({
|
||||
hover : false,
|
||||
badgeHover : false,
|
||||
notificationTagMenu: false,
|
||||
roomTagMenu: false,
|
||||
areNotifsMuted: areNotifsMuted,
|
||||
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
|
||||
});
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
switch (payload.action) {
|
||||
case 'notification_change':
|
||||
// Is the notification about this room?
|
||||
if (payload.roomId === this.props.room.roomId) {
|
||||
this.setState( { areNotifsMuted : payload.areNotifsMuted });
|
||||
}
|
||||
break;
|
||||
_shouldShowNotifBadge: function() {
|
||||
const showBadgeInStates = [RoomNotifs.ALL_MESSAGES, RoomNotifs.ALL_MESSAGES_LOUD];
|
||||
return showBadgeInStates.indexOf(this.state.notifState) > -1;
|
||||
},
|
||||
|
||||
_shouldShowMentionBadge: function() {
|
||||
return this.state.notifState != RoomNotifs.MUTE;
|
||||
},
|
||||
|
||||
onAccountData: function(accountDataEvent) {
|
||||
if (accountDataEvent.getType() == 'm.push_rules') {
|
||||
this.setState({
|
||||
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
componentWillMount: function() {
|
||||
MatrixClientPeg.get().on("accountData", this.onAccountData);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
var cli = MatrixClientPeg.get();
|
||||
if (cli) {
|
||||
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
||||
}
|
||||
},
|
||||
|
||||
onClick: function() {
|
||||
@ -179,15 +178,19 @@ module.exports = React.createClass({
|
||||
var notificationCount = this.props.room.getUnreadNotificationCount();
|
||||
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
|
||||
|
||||
const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
|
||||
const mentionBadges = this.props.highlight && this._shouldShowMentionBadge();
|
||||
const badges = notifBadges || mentionBadges;
|
||||
|
||||
var classes = classNames({
|
||||
'mx_RoomTile': true,
|
||||
'mx_RoomTile_selected': this.props.selected,
|
||||
'mx_RoomTile_unread': this.props.unread,
|
||||
'mx_RoomTile_unreadNotify': notificationCount > 0 && !this.state.areNotifsMuted,
|
||||
'mx_RoomTile_highlight': this.props.highlight,
|
||||
'mx_RoomTile_unreadNotify': notifBadges,
|
||||
'mx_RoomTile_highlight': mentionBadges,
|
||||
'mx_RoomTile_invited': (me && me.membership == 'invite'),
|
||||
'mx_RoomTile_notificationTagMenu': this.state.notificationTagMenu,
|
||||
'mx_RoomTile_noBadges': !(this.props.highlight || (notificationCount > 0 && !this.state.areNotifsMuted))
|
||||
'mx_RoomTile_noBadges': !badges,
|
||||
});
|
||||
|
||||
var avatarClasses = classNames({
|
||||
@ -214,7 +217,7 @@ module.exports = React.createClass({
|
||||
|
||||
if (this.state.badgeHover || this.state.notificationTagMenu) {
|
||||
badgeContent = "\u00B7\u00B7\u00B7";
|
||||
} else if (this.props.highlight || (notificationCount > 0 && !this.state.areNotifsMuted)) {
|
||||
} else if (badges) {
|
||||
var limitedCount = (notificationCount > 99) ? '99+' : notificationCount;
|
||||
badgeContent = notificationCount ? limitedCount : '!';
|
||||
} else {
|
||||
@ -230,7 +233,7 @@ module.exports = React.createClass({
|
||||
var nameClasses = classNames({
|
||||
'mx_RoomTile_name': true,
|
||||
'mx_RoomTile_invite': this.props.isInvite,
|
||||
'mx_RoomTile_badgeShown': this.props.highlight || (notificationCount > 0 && !this.state.areNotifsMuted) || this.state.badgeHover || this.state.notificationTagMenu,
|
||||
'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.notificationTagMenu,
|
||||
});
|
||||
|
||||
if (this.props.selected) {
|
||||
|
@ -52,7 +52,7 @@ export default class DevicesPanel extends React.Component {
|
||||
(error) => {
|
||||
if (this._unmounted) { return; }
|
||||
var errtxt;
|
||||
if (err.httpStatus == 404) {
|
||||
if (error.httpStatus == 404) {
|
||||
// 404 probably means the HS doesn't yet support the API.
|
||||
errtxt = "Your home server does not support device management.";
|
||||
} else {
|
||||
@ -127,6 +127,7 @@ export default class DevicesPanel extends React.Component {
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className="mx_DevicesPanel_header">
|
||||
<div className="mx_DevicesPanel_deviceId">ID</div>
|
||||
<div className="mx_DevicesPanel_deviceName">Name</div>
|
||||
<div className="mx_DevicesPanel_deviceLastSeen">Last seen</div>
|
||||
<div className="mx_DevicesPanel_deviceButtons"></div>
|
||||
|
@ -109,6 +109,9 @@ export default class DevicesPanelEntry extends React.Component {
|
||||
|
||||
return (
|
||||
<div className="mx_DevicesPanel_device">
|
||||
<div className="mx_DevicesPanel_deviceId">
|
||||
{device.device_id}
|
||||
</div>
|
||||
<div className="mx_DevicesPanel_deviceName">
|
||||
<EditableTextContainer initialValue={device.display_name}
|
||||
onSubmit={this._onDisplayNameChanged}
|
||||
|
@ -137,6 +137,10 @@ matrixLinkify.options = {
|
||||
}
|
||||
},
|
||||
|
||||
linkAttributes: {
|
||||
rel: 'noopener',
|
||||
},
|
||||
|
||||
target: function(href, type) {
|
||||
if (type === 'url') {
|
||||
if (href.match(matrixLinkify.VECTOR_URL_PATTERN)) {
|
||||
|
@ -50,8 +50,7 @@ module.exports.stubClient = function() {
|
||||
//
|
||||
// 'sandbox.restore()' doesn't work correctly on inherited methods,
|
||||
// so we do this for each method
|
||||
var methods = ['get', 'unset', 'replaceUsingUrls',
|
||||
'replaceUsingCreds'];
|
||||
var methods = ['get', 'unset', 'replaceUsingCreds'];
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
sandbox.stub(peg, methods[i]);
|
||||
}
|
||||
@ -184,4 +183,3 @@ module.exports.mkStubRoom = function() {
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user