mirror of
https://github.com/SchildiChat/element-web.git
synced 2024-10-01 01:26:12 -04:00
Merge remote-tracking branch 'origin/develop' into dbkr/add_finnish
This commit is contained in:
commit
133e17c1db
47
CHANGELOG.md
47
CHANGELOG.md
@ -1,3 +1,50 @@
|
||||
Changes in [0.12.6](https://github.com/vector-im/riot-web/releases/tag/v0.12.6) (2017-09-21)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.12.5...v0.12.6)
|
||||
|
||||
* Use matrix-js-sdk v0.8.4 to fix build
|
||||
|
||||
Changes in [0.12.5](https://github.com/vector-im/riot-web/releases/tag/v0.12.5) (2017-09-21)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.12.4...v0.12.5)
|
||||
|
||||
* Use react-sdk v0.10.5 to fix build
|
||||
|
||||
Changes in [0.12.4](https://github.com/vector-im/riot-web/releases/tag/v0.12.4) (2017-09-20)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.12.4-rc.1...v0.12.4)
|
||||
|
||||
* No changes
|
||||
|
||||
Changes in [0.12.4-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.12.4-rc.1) (2017-09-19)
|
||||
======================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.12.3...v0.12.4-rc.1)
|
||||
|
||||
* Fix test for new behaviour of 'joining' flag
|
||||
[\#5053](https://github.com/vector-im/riot-web/pull/5053)
|
||||
* fix really dumb blunder/typo preventing system from going to sleep.
|
||||
[\#5080](https://github.com/vector-im/riot-web/pull/5080)
|
||||
* T3chguy/devtools
|
||||
[\#4735](https://github.com/vector-im/riot-web/pull/4735)
|
||||
* CSS for unignore button in UserSettings
|
||||
[\#5042](https://github.com/vector-im/riot-web/pull/5042)
|
||||
* Fix alias on home page for identity room
|
||||
[\#5044](https://github.com/vector-im/riot-web/pull/5044)
|
||||
* generic contextual menu for tooltip/responses
|
||||
[\#4989](https://github.com/vector-im/riot-web/pull/4989)
|
||||
* Update from Weblate.
|
||||
[\#5018](https://github.com/vector-im/riot-web/pull/5018)
|
||||
* Avoid re-rendering RoomList on room switch
|
||||
[\#5015](https://github.com/vector-im/riot-web/pull/5015)
|
||||
* Fix menu on change keyboard language issue #4345
|
||||
[\#4623](https://github.com/vector-im/riot-web/pull/4623)
|
||||
* Make isInvite default to false
|
||||
[\#4999](https://github.com/vector-im/riot-web/pull/4999)
|
||||
* Revert "Implement sticky date separators"
|
||||
[\#4991](https://github.com/vector-im/riot-web/pull/4991)
|
||||
* Implement sticky date separators
|
||||
[\#4939](https://github.com/vector-im/riot-web/pull/4939)
|
||||
|
||||
Changes in [0.12.3](https://github.com/vector-im/riot-web/releases/tag/v0.12.3) (2017-09-06)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.12.3-rc.3...v0.12.3)
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "src/electron-main.js",
|
||||
"version": "0.12.3",
|
||||
"version": "0.12.6",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "Vector Creations Ltd.",
|
||||
"dependencies": {
|
||||
|
@ -84,7 +84,7 @@ let powerSaveBlockerId;
|
||||
electron.ipcMain.on('app_onAction', function(ev, payload) {
|
||||
switch (payload.action) {
|
||||
case 'call_state':
|
||||
if (powerSaveBlockerId && powerSaveBlockerId.isStarted(powerSaveBlockerId)) {
|
||||
if (powerSaveBlockerId && electron.powerSaveBlocker.isStarted(powerSaveBlockerId)) {
|
||||
if (payload.state === 'ended') {
|
||||
electron.powerSaveBlocker.stop(powerSaveBlockerId);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "electron_app/src/electron-main.js",
|
||||
"version": "0.12.3",
|
||||
"version": "0.12.6",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "Vector Creations Ltd.",
|
||||
"repository": {
|
||||
@ -47,7 +47,7 @@
|
||||
"lint": "eslint src/",
|
||||
"lintall": "eslint src/ test/",
|
||||
"clean": "rimraf lib webapp electron_app/dist",
|
||||
"prepublish": "npm run build:compile",
|
||||
"prepublish": "npm run clean && npm run build:compile",
|
||||
"test": "karma start --single-run=true --autoWatch=false --browsers ChromeHeadless",
|
||||
"test-multi": "karma start"
|
||||
},
|
||||
@ -66,8 +66,8 @@
|
||||
"gfm.css": "^1.1.1",
|
||||
"highlight.js": "^9.0.0",
|
||||
"linkifyjs": "^2.1.3",
|
||||
"matrix-js-sdk": "0.8.2",
|
||||
"matrix-react-sdk": "0.10.3",
|
||||
"matrix-js-sdk": "0.8.4",
|
||||
"matrix-react-sdk": "0.10.6",
|
||||
"modernizr": "^3.1.0",
|
||||
"pako": "^1.0.5",
|
||||
"prop-types": "^15.5.10",
|
||||
|
@ -249,7 +249,7 @@
|
||||
<span class="mx_HomePage_desc">_t("Implementing VoIP services with Matrix")</span>
|
||||
</div>
|
||||
<div class="mx_HomePage_room">
|
||||
<a href="#/room/#identity:matrix.org">
|
||||
<a href="#/room/#matrix-identity:matrix.org">
|
||||
<img class="mx_HomePage_icon" src="home/rooms/identity.jpg">
|
||||
<span class="mx_HomePage_name">Matrix Identity</span>
|
||||
</a>
|
||||
|
@ -14,6 +14,7 @@ import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import shutil
|
||||
import glob
|
||||
|
||||
try:
|
||||
# python3
|
||||
@ -66,7 +67,7 @@ class Deployer:
|
||||
self.bundles_path = None
|
||||
self.should_clean = False
|
||||
# filename -> symlink path e.g 'config.localhost.json' => '../localhost/config.json'
|
||||
self.config_locations = {}
|
||||
self.symlink_paths = {}
|
||||
self.verify_signature = True
|
||||
|
||||
def deploy(self, tarball, extract_path):
|
||||
@ -98,11 +99,11 @@ class Deployer:
|
||||
|
||||
print ("Extracted into: %s" % extracted_dir)
|
||||
|
||||
if self.config_locations:
|
||||
for config_filename, config_loc in self.config_locations.iteritems():
|
||||
if self.symlink_paths:
|
||||
for link_path, file_path in self.symlink_paths.iteritems():
|
||||
create_relative_symlink(
|
||||
target=config_loc,
|
||||
linkname=os.path.join(extracted_dir, config_filename)
|
||||
target=file_path,
|
||||
linkname=os.path.join(extracted_dir, link_path)
|
||||
)
|
||||
|
||||
if self.bundles_path:
|
||||
@ -165,9 +166,10 @@ if __name__ == "__main__":
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"--config", nargs='?', default='./config.json', help=(
|
||||
"Write a symlink at config.json in the extracted tarball to this \
|
||||
location. (Default: '%(default)s')"
|
||||
"--include", nargs='*', default='./config*.json', help=(
|
||||
"Symlink these files into the root of the deployed tarball. \
|
||||
Useful for config files and home pages. Supports glob syntax. \
|
||||
(Default: '%(default)s')"
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
@ -182,8 +184,8 @@ if __name__ == "__main__":
|
||||
deployer.packages_path = args.packages_dir
|
||||
deployer.bundles_path = args.bundles_dir
|
||||
deployer.should_clean = args.clean
|
||||
deployer.config_locations = {
|
||||
"config.json": args.config,
|
||||
}
|
||||
|
||||
for include in args.include:
|
||||
deployer.symlink_paths.update({ os.path.basename(pth): pth for pth in glob.iglob(include) })
|
||||
|
||||
deployer.deploy(args.tarball, args.extract_path)
|
||||
|
@ -15,6 +15,7 @@ import json, requests, tarfile, argparse, os, errno
|
||||
import time
|
||||
import traceback
|
||||
from urlparse import urljoin
|
||||
import glob
|
||||
|
||||
from flask import Flask, jsonify, request, abort
|
||||
|
||||
@ -188,15 +189,12 @@ if __name__ == "__main__":
|
||||
)
|
||||
)
|
||||
|
||||
def _raise(ex):
|
||||
raise ex
|
||||
|
||||
# --config config.json=../../config.json --config config.localhost.json=./localhost.json
|
||||
# --include ../../config.json ./localhost.json homepages/*
|
||||
parser.add_argument(
|
||||
"--config", action="append", dest="configs",
|
||||
type=lambda kv: kv.split("=", 1) if "=" in kv else _raise(Exception("Missing =")), help=(
|
||||
"A list of configs to symlink into the extracted tarball. \
|
||||
For example, --config config.json=../config.json config2.json=../test/config.json"
|
||||
"--include", nargs='*', default='./config*.json', help=(
|
||||
"Symlink these files into the root of the deployed tarball. \
|
||||
Useful for config files and home pages. Supports glob syntax. \
|
||||
(Default: '%(default)s')"
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
@ -220,7 +218,9 @@ if __name__ == "__main__":
|
||||
deployer = Deployer()
|
||||
deployer.bundles_path = args.bundles_dir
|
||||
deployer.should_clean = args.clean
|
||||
deployer.config_locations = dict(args.configs) if args.configs else {}
|
||||
|
||||
for include in args.include:
|
||||
deployer.symlink_paths.update({ os.path.basename(pth): pth for pth in glob.iglob(include) })
|
||||
|
||||
|
||||
# we don't pgp-sign jenkins artifacts; instead we rely on HTTPS access to
|
||||
@ -234,13 +234,13 @@ if __name__ == "__main__":
|
||||
deploy_tarball(args.tarball_uri, build_dir)
|
||||
else:
|
||||
print(
|
||||
"Listening on port %s. Extracting to %s%s. Symlinking to %s. Jenkins URL: %s. Config locations: %s" %
|
||||
"Listening on port %s. Extracting to %s%s. Symlinking to %s. Jenkins URL: %s. Include files: %s" %
|
||||
(args.port,
|
||||
arg_extract_path,
|
||||
" (clean after)" if deployer.should_clean else "",
|
||||
arg_symlink,
|
||||
arg_jenkins_url,
|
||||
deployer.config_locations,
|
||||
deployer.symlink_paths,
|
||||
)
|
||||
)
|
||||
app.run(host="0.0.0.0", port=args.port, debug=True)
|
||||
|
@ -1,5 +1,7 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -14,8 +16,6 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
@ -26,26 +26,31 @@ import Analytics from 'matrix-react-sdk/lib/Analytics';
|
||||
import rate_limited_func from 'matrix-react-sdk/lib/ratelimitedfunc';
|
||||
import Modal from 'matrix-react-sdk/lib/Modal';
|
||||
import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
|
||||
import { showGroupInviteDialog } from 'matrix-react-sdk/lib/GroupInvite';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RightPanel',
|
||||
|
||||
propTypes: {
|
||||
// TODO: This should not be a prop, it should be received from the RoomViewStore
|
||||
// TODO: We're trying to move away from these being props, but we need to know
|
||||
// whether we should be displaying a room or group member list
|
||||
roomId: React.PropTypes.string, // if showing panels for a given room, this is set
|
||||
groupId: React.PropTypes.string, // if showing panels for a given group, this is set
|
||||
collapsed: React.PropTypes.bool, // currently unused property to request for a minimized view of the panel
|
||||
},
|
||||
|
||||
Phase: {
|
||||
MemberList: 'MemberList',
|
||||
RoomMemberList: 'RoomMemberList',
|
||||
GroupMemberList: 'GroupMemberList',
|
||||
FilePanel: 'FilePanel',
|
||||
NotificationPanel: 'NotificationPanel',
|
||||
MemberInfo: 'MemberInfo',
|
||||
RoomMemberInfo: 'RoomMemberInfo',
|
||||
GroupMemberInfo: 'GroupMemberInfo',
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
var cli = MatrixClientPeg.get();
|
||||
const cli = MatrixClientPeg.get();
|
||||
cli.on("RoomState.members", this.onRoomStateMember);
|
||||
},
|
||||
|
||||
@ -57,14 +62,20 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
phase: this.Phase.MemberList
|
||||
};
|
||||
if (this.props.groupId) {
|
||||
return {
|
||||
phase: this.Phase.GroupMemberList,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
phase: this.Phase.RoomMemberList,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
onMemberListButtonClick: function() {
|
||||
Analytics.trackEvent('Right Panel', 'Member List Button', 'click');
|
||||
this.setState({ phase: this.Phase.MemberList });
|
||||
this.setState({ phase: this.Phase.RoomMemberList });
|
||||
},
|
||||
|
||||
onFileListButtonClick: function() {
|
||||
@ -89,19 +100,23 @@ module.exports = React.createClass({
|
||||
return;
|
||||
}
|
||||
|
||||
// call ChatInviteDialog
|
||||
dis.dispatch({
|
||||
action: 'view_invite',
|
||||
roomId: this.props.roomId,
|
||||
});
|
||||
if (this.state.phase === this.Phase.GroupMemberList) {
|
||||
showGroupInviteDialog(this.props.groupId);
|
||||
} else {
|
||||
// call AddressPickerDialog
|
||||
dis.dispatch({
|
||||
action: 'view_invite',
|
||||
roomId: this.props.roomId,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onRoomStateMember: function(ev, state, member) {
|
||||
// redraw the badge on the membership list
|
||||
if (this.state.phase == this.Phase.MemberList && member.roomId === this.props.roomId) {
|
||||
if (this.state.phase == this.Phase.RoomMemberList && member.roomId === this.props.roomId) {
|
||||
this._delayedUpdate();
|
||||
}
|
||||
else if (this.state.phase === this.Phase.MemberInfo && member.roomId === this.props.roomId &&
|
||||
else if (this.state.phase === this.Phase.RoomMemberInfo && member.roomId === this.props.roomId &&
|
||||
member.userId === this.state.member.userId) {
|
||||
// refresh the member info (e.g. new power level)
|
||||
this._delayedUpdate();
|
||||
@ -119,39 +134,55 @@ module.exports = React.createClass({
|
||||
});
|
||||
if (payload.member) {
|
||||
this.setState({
|
||||
phase: this.Phase.MemberInfo,
|
||||
phase: this.Phase.RoomMemberInfo,
|
||||
member: payload.member,
|
||||
});
|
||||
} else {
|
||||
if (this.props.roomId) {
|
||||
this.setState({
|
||||
phase: this.Phase.RoomMemberList
|
||||
});
|
||||
} else if (this.props.groupId) {
|
||||
this.setState({
|
||||
phase: this.Phase.GroupMemberList,
|
||||
groupId: payload.groupId,
|
||||
member: payload.member,
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.setState({
|
||||
phase: this.Phase.MemberList
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (payload.action === "view_room") {
|
||||
if (this.state.phase === this.Phase.MemberInfo) {
|
||||
this.setState({
|
||||
phase: this.Phase.MemberList
|
||||
});
|
||||
}
|
||||
} else if (payload.action === "view_group") {
|
||||
this.setState({
|
||||
phase: this.Phase.GroupMemberList,
|
||||
groupId: payload.groupId,
|
||||
member: null,
|
||||
});
|
||||
} else if (payload.action === "view_group_user") {
|
||||
this.setState({
|
||||
phase: this.Phase.GroupMemberInfo,
|
||||
groupId: payload.groupId,
|
||||
member: payload.member,
|
||||
});
|
||||
} else if (payload.action === "view_room") {
|
||||
this.setState({
|
||||
phase: this.Phase.RoomMemberList
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var MemberList = sdk.getComponent('rooms.MemberList');
|
||||
var NotificationPanel = sdk.getComponent('structures.NotificationPanel');
|
||||
var FilePanel = sdk.getComponent('structures.FilePanel');
|
||||
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
var buttonGroup;
|
||||
var inviteGroup;
|
||||
var panel;
|
||||
const MemberList = sdk.getComponent('rooms.MemberList');
|
||||
const GroupMemberList = sdk.getComponent('groups.GroupMemberList');
|
||||
const NotificationPanel = sdk.getComponent('structures.NotificationPanel');
|
||||
const FilePanel = sdk.getComponent('structures.FilePanel');
|
||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
let inviteGroup;
|
||||
let panel;
|
||||
|
||||
var filesHighlight;
|
||||
var membersHighlight;
|
||||
var notificationsHighlight;
|
||||
let filesHighlight;
|
||||
let membersHighlight;
|
||||
let notificationsHighlight;
|
||||
if (!this.props.collapsed) {
|
||||
if (this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) {
|
||||
if (this.state.phase == this.Phase.RoomMemberList || this.state.phase === this.Phase.RoomMemberInfo) {
|
||||
membersHighlight = <div className="mx_RightPanel_headerButton_highlight"></div>;
|
||||
}
|
||||
else if (this.state.phase == this.Phase.FilePanel) {
|
||||
@ -162,11 +193,11 @@ module.exports = React.createClass({
|
||||
}
|
||||
}
|
||||
|
||||
var membersBadge;
|
||||
if ((this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) && this.props.roomId) {
|
||||
var cli = MatrixClientPeg.get();
|
||||
var room = cli.getRoom(this.props.roomId);
|
||||
var user_is_in_room;
|
||||
let membersBadge;
|
||||
if ((this.state.phase == this.Phase.RoomMemberList || this.state.phase === this.Phase.RoomMemberInfo) && this.props.roomId) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const room = cli.getRoom(this.props.roomId);
|
||||
let user_is_in_room;
|
||||
if (room) {
|
||||
membersBadge = room.getJoinedMembers().length;
|
||||
user_is_in_room = room.hasMembershipState(
|
||||
@ -186,48 +217,72 @@ module.exports = React.createClass({
|
||||
|
||||
}
|
||||
|
||||
let headerButtons = [];
|
||||
if (this.props.roomId) {
|
||||
buttonGroup =
|
||||
<div className="mx_RightPanel_headerButtonGroup">
|
||||
<AccessibleButton className="mx_RightPanel_headerButton"
|
||||
title={ _t('Members') } onClick={ this.onMemberListButtonClick }>
|
||||
<div className="mx_RightPanel_headerButton_badge">{ membersBadge ? membersBadge : <span> </span>}</div>
|
||||
<TintableSvg src="img/icons-people.svg" width="25" height="25"/>
|
||||
{ membersHighlight }
|
||||
</AccessibleButton>
|
||||
<AccessibleButton
|
||||
className="mx_RightPanel_headerButton mx_RightPanel_filebutton"
|
||||
title={ _t('Files') } onClick={ this.onFileListButtonClick }>
|
||||
<div className="mx_RightPanel_headerButton_badge"> </div>
|
||||
<TintableSvg src="img/icons-files.svg" width="25" height="25"/>
|
||||
{ filesHighlight }
|
||||
</AccessibleButton>
|
||||
<AccessibleButton
|
||||
className="mx_RightPanel_headerButton mx_RightPanel_notificationbutton"
|
||||
title={ _t('Notifications') } onClick={ this.onNotificationListButtonClick }>
|
||||
<div className="mx_RightPanel_headerButton_badge"> </div>
|
||||
<TintableSvg src="img/icons-notifications.svg" width="25" height="25"/>
|
||||
{ notificationsHighlight }
|
||||
</AccessibleButton>
|
||||
<div className="mx_RightPanel_headerButton mx_RightPanel_collapsebutton" title={ _t("Hide panel") } onClick={ this.onCollapseClick }>
|
||||
<TintableSvg src="img/minimise.svg" width="10" height="16"/>
|
||||
</div>
|
||||
</div>;
|
||||
headerButtons.push(
|
||||
<AccessibleButton className="mx_RightPanel_headerButton" key="_membersButton"
|
||||
title={ _t('Members') } onClick={ this.onMemberListButtonClick }>
|
||||
<div className="mx_RightPanel_headerButton_badge">{ membersBadge ? membersBadge : <span> </span>}</div>
|
||||
<TintableSvg src="img/icons-people.svg" width="25" height="25"/>
|
||||
{ membersHighlight }
|
||||
</AccessibleButton>
|
||||
);
|
||||
headerButtons.push(
|
||||
<AccessibleButton
|
||||
className="mx_RightPanel_headerButton mx_RightPanel_filebutton" key="_filesButton"
|
||||
title={ _t('Files') } onClick={ this.onFileListButtonClick }>
|
||||
<div className="mx_RightPanel_headerButton_badge"> </div>
|
||||
<TintableSvg src="img/icons-files.svg" width="25" height="25"/>
|
||||
{ filesHighlight }
|
||||
</AccessibleButton>
|
||||
);
|
||||
headerButtons.push(
|
||||
<AccessibleButton
|
||||
className="mx_RightPanel_headerButton mx_RightPanel_notificationbutton" key="_notifsButton"
|
||||
title={ _t('Notifications') } onClick={ this.onNotificationListButtonClick }>
|
||||
<div className="mx_RightPanel_headerButton_badge"> </div>
|
||||
<TintableSvg src="img/icons-notifications.svg" width="25" height="25"/>
|
||||
{ notificationsHighlight }
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.roomId || this.props.groupId) {
|
||||
// Hiding the right panel hides it completely and relies on an 'expand' button
|
||||
// being put in the RoomHeader or GroupView header, so only show the minimise
|
||||
// button on these 2 screens or you won't be able to re-expand the panel.
|
||||
headerButtons.push(
|
||||
<div className="mx_RightPanel_headerButton mx_RightPanel_collapsebutton" key="_minimizeButton"
|
||||
title={ _t("Hide panel") } onClick={ this.onCollapseClick }
|
||||
>
|
||||
<TintableSvg src="img/minimise.svg" width="10" height="16"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.props.collapsed) {
|
||||
if(this.props.roomId && this.state.phase == this.Phase.MemberList) {
|
||||
if (this.props.roomId && this.state.phase == this.Phase.RoomMemberList) {
|
||||
panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />
|
||||
}
|
||||
else if(this.state.phase == this.Phase.MemberInfo) {
|
||||
var MemberInfo = sdk.getComponent('rooms.MemberInfo');
|
||||
} else if (this.props.groupId && this.state.phase == this.Phase.GroupMemberList) {
|
||||
panel = <GroupMemberList groupId={this.props.groupId} key={this.props.groupId} />;
|
||||
inviteGroup = (
|
||||
<AccessibleButton className="mx_RightPanel_invite" onClick={ this.onInviteButtonClick } >
|
||||
<div className="mx_RightPanel_icon" >
|
||||
<TintableSvg src="img/icon-invite-people.svg" width="35" height="35" />
|
||||
</div>
|
||||
<div className="mx_RightPanel_message">{ _t('Invite to this group') }</div>
|
||||
</AccessibleButton>
|
||||
);
|
||||
} else if (this.state.phase == this.Phase.RoomMemberInfo) {
|
||||
const MemberInfo = sdk.getComponent('rooms.MemberInfo');
|
||||
panel = <MemberInfo member={this.state.member} key={this.props.roomId || this.state.member.userId} />
|
||||
}
|
||||
else if (this.state.phase == this.Phase.NotificationPanel) {
|
||||
panel = <NotificationPanel />
|
||||
}
|
||||
else if (this.state.phase == this.Phase.FilePanel) {
|
||||
panel = <FilePanel roomId={this.props.roomId} />
|
||||
} else if (this.state.phase == this.Phase.GroupMemberInfo) {
|
||||
const GroupMemberInfo = sdk.getComponent('groups.GroupMemberInfo');
|
||||
panel = <GroupMemberInfo groupMember={this.state.member} groupId={this.props.groupId} key={this.state.member.user_id} />;
|
||||
} else if (this.state.phase == this.Phase.NotificationPanel) {
|
||||
panel = <NotificationPanel />;
|
||||
} else if (this.state.phase == this.Phase.FilePanel) {
|
||||
panel = <FilePanel roomId={this.props.roomId} />;
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,7 +290,7 @@ module.exports = React.createClass({
|
||||
panel = <div className="mx_RightPanel_blank"></div>;
|
||||
}
|
||||
|
||||
var classes = "mx_RightPanel mx_fadable";
|
||||
let classes = "mx_RightPanel mx_fadable";
|
||||
if (this.props.collapsed) {
|
||||
classes += " collapsed";
|
||||
}
|
||||
@ -243,7 +298,9 @@ module.exports = React.createClass({
|
||||
return (
|
||||
<aside className={classes} style={{ opacity: this.props.opacity }}>
|
||||
<div className="mx_RightPanel_header">
|
||||
{ buttonGroup }
|
||||
<div className="mx_RightPanel_headerButtonGroup">
|
||||
{headerButtons}
|
||||
</div>
|
||||
</div>
|
||||
{ panel }
|
||||
<div className="mx_RightPanel_footer">
|
||||
|
@ -88,6 +88,7 @@ var RoomSubList = React.createClass({
|
||||
searchFilter: React.PropTypes.string,
|
||||
emptyContent: React.PropTypes.node, // content shown if the list is empty
|
||||
headerItems: React.PropTypes.node, // content shown in the sublist header
|
||||
extraTiles: React.PropTypes.arrayOf(React.PropTypes.node), // extra elements added beneath tiles
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@ -102,6 +103,7 @@ var RoomSubList = React.createClass({
|
||||
return {
|
||||
onHeaderClick: function() {}, // NOP
|
||||
onShowMoreRooms: function() {}, // NOP
|
||||
extraTiles: [],
|
||||
isInvite: false,
|
||||
};
|
||||
},
|
||||
@ -534,13 +536,14 @@ var RoomSubList = React.createClass({
|
||||
var label = this.props.collapsed ? null : this.props.label;
|
||||
|
||||
let content;
|
||||
if (this.state.sortedList.length == 0 && !this.props.searchFilter) {
|
||||
if (this.state.sortedList.length == 0 && !this.props.searchFilter && !this.props.extraTiles) {
|
||||
content = this.props.emptyContent;
|
||||
} else {
|
||||
content = this.makeRoomTiles();
|
||||
content.push(...this.props.extraTiles);
|
||||
}
|
||||
|
||||
if (this.state.sortedList.length > 0 || this.props.editable) {
|
||||
if (this.state.sortedList.length > 0 || this.props.extraTiles.length > 0 || this.props.editable) {
|
||||
var subList;
|
||||
var classes = "mx_RoomSubList";
|
||||
|
||||
|
350
src/components/views/dialogs/DevtoolsDialog.js
Normal file
350
src/components/views/dialogs/DevtoolsDialog.js
Normal file
@ -0,0 +1,350 @@
|
||||
/*
|
||||
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
|
||||
|
||||
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 React from 'react';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||
|
||||
class SendCustomEvent extends React.Component {
|
||||
static propTypes = {
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onBack: React.PropTypes.func.isRequired,
|
||||
|
||||
eventType: React.PropTypes.string.isRequired,
|
||||
evContent: React.PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
eventType: '',
|
||||
evContent: '{\n\n}',
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this._send = this._send.bind(this);
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this._onChange = this._onChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
message: null,
|
||||
input_eventType: this.props.eventType,
|
||||
input_evContent: this.props.evContent,
|
||||
};
|
||||
}
|
||||
|
||||
onBack() {
|
||||
if (this.state.message) {
|
||||
this.setState({ message: null });
|
||||
} else {
|
||||
this.props.onBack();
|
||||
}
|
||||
}
|
||||
|
||||
_buttons() {
|
||||
return <div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
{!this.state.message && <button onClick={this._send}>{ _t('Send') }</button>}
|
||||
</div>;
|
||||
}
|
||||
|
||||
send(content) {
|
||||
return MatrixClientPeg.get().sendEvent(this.props.roomId, this.state.input_eventType, content);
|
||||
}
|
||||
|
||||
async _send() {
|
||||
if (this.state.input_eventType === '') {
|
||||
this.setState({ message: _t('You must specify an event type!') });
|
||||
return;
|
||||
}
|
||||
|
||||
let message;
|
||||
try {
|
||||
const content = JSON.parse(this.state.input_evContent);
|
||||
await this.send(content);
|
||||
message = _t('Event sent!');
|
||||
} catch (e) {
|
||||
message = _t('Failed to send custom event.') + ' (' + e.toString() + ')';
|
||||
}
|
||||
this.setState({ message });
|
||||
}
|
||||
|
||||
_additionalFields() {
|
||||
return <div/>;
|
||||
}
|
||||
|
||||
_onChange(e) {
|
||||
this.setState({[`input_${e.target.id}`]: e.target.value});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.message) {
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
{this.state.message}
|
||||
</div>
|
||||
{this._buttons()}
|
||||
</div>;
|
||||
}
|
||||
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
{this._additionalFields()}
|
||||
<div className="mx_TextInputDialog_label">
|
||||
<label htmlFor="eventType"> { _t('Event Type') } </label>
|
||||
</div>
|
||||
<div>
|
||||
<input id="eventType" onChange={this._onChange} value={this.state.input_eventType} className="mx_TextInputDialog_input" size="64" />
|
||||
</div>
|
||||
|
||||
<div className="mx_TextInputDialog_label">
|
||||
<label htmlFor="evContent"> { _t('Event Content') } </label>
|
||||
</div>
|
||||
<div>
|
||||
<textarea id="evContent" onChange={this._onChange} value={this.state.input_evContent} className="mx_TextInputDialog_input" cols="63" rows="5" />
|
||||
</div>
|
||||
</div>
|
||||
{this._buttons()}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class SendCustomStateEvent extends SendCustomEvent {
|
||||
static propTypes = {
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onBack: React.PropTypes.func.isRequired,
|
||||
|
||||
eventType: React.PropTypes.string.isRequired,
|
||||
evContent: React.PropTypes.string.isRequired,
|
||||
stateKey: React.PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
eventType: '',
|
||||
evContent: '{\n\n}',
|
||||
stateKey: '',
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state['input_stateKey'] = this.props.stateKey;
|
||||
}
|
||||
|
||||
send(content) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
return cli.sendStateEvent(this.props.roomId, this.state.input_eventType, content, this.state.input_stateKey);
|
||||
}
|
||||
|
||||
_additionalFields() {
|
||||
return <div>
|
||||
<div className="mx_TextInputDialog_label">
|
||||
<label htmlFor="stateKey"> { _t('State Key') } </label>
|
||||
</div>
|
||||
<div>
|
||||
<input id="stateKey" onChange={this._onChange} value={this.state.input_stateKey} className="mx_TextInputDialog_input" size="64" />
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class RoomStateExplorer extends React.Component {
|
||||
static propTypes = {
|
||||
setMode: React.PropTypes.func.isRequired,
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onBack: React.PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
|
||||
this.roomStateEvents = room.currentState.events;
|
||||
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.editEv = this.editEv.bind(this);
|
||||
this.onQuery = this.onQuery.bind(this);
|
||||
}
|
||||
|
||||
state = {
|
||||
query: '',
|
||||
eventType: null,
|
||||
event: null,
|
||||
};
|
||||
|
||||
browseEventType(eventType) {
|
||||
return () => {
|
||||
this.setState({ eventType });
|
||||
};
|
||||
}
|
||||
|
||||
onViewSourceClick(event) {
|
||||
return () => {
|
||||
this.setState({ event });
|
||||
};
|
||||
}
|
||||
|
||||
onBack() {
|
||||
if (this.state.event) {
|
||||
this.setState({ event: null });
|
||||
} else if (this.state.eventType) {
|
||||
this.setState({ eventType: null });
|
||||
} else {
|
||||
this.props.onBack();
|
||||
}
|
||||
}
|
||||
|
||||
editEv() {
|
||||
const ev = this.state.event;
|
||||
this.props.setMode(SendCustomStateEvent, {
|
||||
eventType: ev.getType(),
|
||||
evContent: JSON.stringify(ev.getContent(), null, '\t'),
|
||||
stateKey: ev.getStateKey(),
|
||||
});
|
||||
}
|
||||
|
||||
onQuery(ev) {
|
||||
this.setState({ query: ev.target.value });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.event) {
|
||||
return <div className="mx_ViewSource">
|
||||
<div className="mx_Dialog_content">
|
||||
<pre>{JSON.stringify(this.state.event.event, null, 2)}</pre>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
<button onClick={this.editEv}>{ _t('Edit') }</button>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
|
||||
if (this.state.eventType === null) {
|
||||
Object.keys(this.roomStateEvents).forEach((evType) => {
|
||||
// Skip this entry if does not contain search query
|
||||
if (this.state.query && !evType.includes(this.state.query)) return;
|
||||
|
||||
const stateGroup = this.roomStateEvents[evType];
|
||||
const stateKeys = Object.keys(stateGroup);
|
||||
|
||||
let onClickFn;
|
||||
if (stateKeys.length > 1) {
|
||||
onClickFn = this.browseEventType(evType);
|
||||
} else if (stateKeys.length === 1) {
|
||||
onClickFn = this.onViewSourceClick(stateGroup[stateKeys[0]]);
|
||||
}
|
||||
|
||||
rows.push(<button className="mx_DevTools_RoomStateExplorer_button" key={evType} onClick={onClickFn}>
|
||||
{ evType }
|
||||
</button>);
|
||||
});
|
||||
} else {
|
||||
const evType = this.state.eventType;
|
||||
const stateGroup = this.roomStateEvents[evType];
|
||||
Object.keys(stateGroup).forEach((stateKey) => {
|
||||
// Skip this entry if does not contain search query
|
||||
if (this.state.query && !stateKey.includes(this.state.query)) return;
|
||||
|
||||
const ev = stateGroup[stateKey];
|
||||
rows.push(<button className="mx_DevTools_RoomStateExplorer_button" key={stateKey}
|
||||
onClick={this.onViewSourceClick(ev)}>
|
||||
{ stateKey }
|
||||
</button>);
|
||||
});
|
||||
}
|
||||
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
<input onChange={this.onQuery} placeholder={_t('Filter results')} size="64" className="mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query" value={this.state.query} />
|
||||
{rows}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default class DevtoolsDialog extends React.Component {
|
||||
static propTypes = {
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onFinished: React.PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
mode: null,
|
||||
modeArgs: {},
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.setMode = this.setMode.bind(this);
|
||||
this.onCancel = this.onCancel.bind(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._unmounted = true;
|
||||
}
|
||||
|
||||
_setMode(mode) {
|
||||
return () => {
|
||||
this.setMode(mode);
|
||||
};
|
||||
}
|
||||
|
||||
setMode(mode, modeArgs={}) {
|
||||
this.setState({ mode, modeArgs });
|
||||
}
|
||||
|
||||
onBack() {
|
||||
this.setState({ mode: null });
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
|
||||
render() {
|
||||
let body;
|
||||
|
||||
if (this.state.mode) {
|
||||
body =
|
||||
<this.state.mode {...this.props} {...this.state.modeArgs} onBack={this.onBack} setMode={this.setMode} />;
|
||||
} else {
|
||||
body = <div>
|
||||
<div className="mx_Dialog_content">
|
||||
<button onClick={this._setMode(SendCustomEvent)}>{ _t('Send Custom Event') }</button>
|
||||
<button onClick={this._setMode(SendCustomStateEvent)}>{ _t('Send Custom State Event') }</button>
|
||||
<button onClick={this._setMode(RoomStateExplorer)}>{ _t('Explore Room State') }</button>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onCancel}>{ _t('Cancel') }</button>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
return (
|
||||
<BaseDialog className="mx_QuestionDialog" onFinished={this.props.onFinished} title={_t('Developer Tools')}>
|
||||
<div>Room ID: { this.props.roomId }</div>
|
||||
{ body }
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
"All Rooms": "All Rooms",
|
||||
"All notifications are currently disabled for all targets.": "All notifications are currently disabled for all targets.",
|
||||
"An error occurred whilst saving your email notification preferences.": "An error occurred whilst saving your email notification preferences.",
|
||||
"Back": "Back",
|
||||
"Bug report sent": "Bug report sent",
|
||||
"Call invitation": "Call invitation",
|
||||
"Cancel": "Cancel",
|
||||
@ -26,6 +27,7 @@
|
||||
"delete the alias.": "delete the alias.",
|
||||
"Delete the room alias %(alias)s and remove %(name)s from the directory?": "Delete the room alias %(alias)s and remove %(name)s from the directory?",
|
||||
"Describe your problem here.": "Describe your problem here.",
|
||||
"Developer Tools": "Developer Tools",
|
||||
"Direct Chat": "Direct Chat",
|
||||
"Directory": "Directory",
|
||||
"Dismiss": "Dismiss",
|
||||
@ -50,12 +52,14 @@
|
||||
"Failed to get public room list": "Failed to get public room list",
|
||||
"Failed to join the room": "Failed to join the room",
|
||||
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",
|
||||
"Failed to send custom event.": "Failed to send custom event.",
|
||||
"Failed to send report: ": "Failed to send report: ",
|
||||
"Failed to set direct chat tag": "Failed to set direct chat tag",
|
||||
"Failed to set Direct Message status of room": "Failed to set Direct Message status of room",
|
||||
"Favourite": "Favourite",
|
||||
"Fetching third party location failed": "Fetching third party location failed",
|
||||
"Files": "Files",
|
||||
"Filter results": "Filter results",
|
||||
"Filter room names": "Filter room names",
|
||||
"Forget": "Forget",
|
||||
"Forward Message": "Forward Message",
|
||||
@ -117,6 +121,9 @@
|
||||
"Search for a room": "Search for a room",
|
||||
"Send": "Send",
|
||||
"Send logs": "Send logs",
|
||||
"Send Custom Event": "Send Custom Event",
|
||||
"Send Custom State Event": "Send Custom State Event",
|
||||
"Explore Room State": "Explore Room State",
|
||||
"Settings": "Settings",
|
||||
"Source URL": "Source URL",
|
||||
"Sorry, your browser is <b>not</b> able to run Riot.": "Sorry, your browser is <b>not</b> able to run Riot.",
|
||||
@ -150,6 +157,7 @@
|
||||
"You are not receiving desktop notifications": "You are not receiving desktop notifications",
|
||||
"You are Rioting as a guest. <a>Register</a> or <a>sign in</a> to access more rooms and features!": "You are Rioting as a guest. <a>Register</a> or <a>sign in</a> to access more rooms and features!",
|
||||
"You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply": "You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply",
|
||||
"You must specify an event type!": "You must specify an event type!",
|
||||
"Thank you!": "Thank you!",
|
||||
"Sunday": "Sunday",
|
||||
"Monday": "Monday",
|
||||
@ -164,6 +172,10 @@
|
||||
"Warning": "Warning",
|
||||
"Checking for an update...": "Checking for an update...",
|
||||
"Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).",
|
||||
"Event sent!": "Event sent!",
|
||||
"Event Type": "Event Type",
|
||||
"Event Content": "Event Content",
|
||||
"State Key": "State Key",
|
||||
"No update available.": "No update available.",
|
||||
"Downloading update...": "Downloading update...",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "You need to be using HTTPS to place a screen-sharing call.",
|
||||
@ -207,5 +219,6 @@
|
||||
"Remember, you can always set an email address in user settings if you change your mind.": "Remember, you can always set an email address in user settings if you change your mind.",
|
||||
"To return to your account in future you need to <u>set a password</u>": "To return to your account in future you need to <u>set a password</u>",
|
||||
"Set Password": "Set Password",
|
||||
"Couldn't load home page": "Couldn't load home page"
|
||||
"Couldn't load home page": "Couldn't load home page",
|
||||
"Invite to this group": "Invite to this group"
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
@import "./matrix-react-sdk/views/elements/_ProgressBar.scss";
|
||||
@import "./matrix-react-sdk/views/elements/_RichText.scss";
|
||||
@import "./matrix-react-sdk/views/elements/_RoleButton.scss";
|
||||
@import "./matrix-react-sdk/views/groups/_GroupInviteTile.scss";
|
||||
@import "./matrix-react-sdk/views/login/_InteractiveAuthEntryComponents.scss";
|
||||
@import "./matrix-react-sdk/views/login/_ServerConfig.scss";
|
||||
@import "./matrix-react-sdk/views/messages/_MEmoteBody.scss";
|
||||
@ -74,6 +75,7 @@
|
||||
@import "./vector-web/views/context_menus/_MessageContextMenu.scss";
|
||||
@import "./vector-web/views/context_menus/_RoomTileContextMenu.scss";
|
||||
@import "./vector-web/views/dialogs/_ChangelogDialog.scss";
|
||||
@import "./vector-web/views/dialogs/_DevtoolsDialog.scss";
|
||||
@import "./vector-web/views/dialogs/_SetEmailDialog.scss";
|
||||
@import "./vector-web/views/dialogs/_SetPasswordDialog.scss";
|
||||
@import "./vector-web/views/directory/_NetworkDropdown.scss";
|
||||
|
@ -70,8 +70,12 @@ limitations under the License.
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mx_GroupView_saveButton, .mx_GroupView_cancelButton {
|
||||
display: table-cell;
|
||||
.mx_GroupView_header_rightCol {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mx_GroupView_textButton {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.mx_GroupView_header_groupid {
|
||||
@ -126,6 +130,26 @@ limitations under the License.
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.mx_GroupView_membershipSection {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 11px;
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
color: $greyed-fg-color;
|
||||
}
|
||||
|
||||
.mx_GroupView_membershipSection_description {
|
||||
/* To match textButton */
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
.mx_GroupView_membershipSection .mx_GroupView_textButton {
|
||||
margin-right: 0px;
|
||||
margin-top: 0px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.mx_GroupView_featuredThings {
|
||||
margin-top: 20px;
|
||||
}
|
||||
@ -142,9 +166,30 @@ limitations under the License.
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mx_GroupView_featuredThings_container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mx_GroupView_featuredThings_addButton,
|
||||
.mx_GroupView_featuredThing {
|
||||
cursor: pointer;
|
||||
display: table-cell;
|
||||
text-align: center;
|
||||
|
||||
width: 100px;
|
||||
margin: 0px 20px;
|
||||
}
|
||||
|
||||
.mx_GroupView_featuredThing .mx_BaseAvatar {
|
||||
/* To prevent misalignment with mx_TintableSvg (in addButton) */
|
||||
vertical-align: initial;
|
||||
}
|
||||
|
||||
.mx_GroupView_featuredThings_addButton object {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mx_GroupView_featuredThing_name {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.mx_GroupView_uploadInput {
|
||||
|
@ -80,6 +80,16 @@ limitations under the License.
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_UserSettings_button.mx_UserSettings_buttonSmall {
|
||||
height: 36px;
|
||||
padding: 4px;
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
font-size: 12px;
|
||||
margin-right: 5px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.mx_UserSettings_button.danger {
|
||||
background-color: $warning-color;
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2017 New Vector 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.
|
||||
*/
|
||||
|
||||
.mx_GroupInviteTile {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
display: block;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.mx_GroupInviteTile_nameContainer {
|
||||
display: inline-block;
|
||||
width: 180px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.mx_GroupInviteTile_avatarContainer {
|
||||
display: inline-block;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 16px;
|
||||
padding-right: 6px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.mx_GroupInviteTile_name {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 165px;
|
||||
vertical-align: middle;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 3px;
|
||||
color: $roomtile-name-color;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mx_GroupInviteTile_badge {
|
||||
display: inline-block;
|
||||
min-width: 15px;
|
||||
height: 15px;
|
||||
position: absolute;
|
||||
right: 8px; /*gutter */
|
||||
top: 9px;
|
||||
border-radius: 8px;
|
||||
color: $accent-fg-color;
|
||||
background-color: $group-alert-color;
|
||||
font-weight: 600;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
padding-top: 1px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
@ -45,7 +45,6 @@ limitations under the License.
|
||||
|
||||
.mx_EventTile .mx_SenderProfile {
|
||||
color: $primary-fg-color;
|
||||
opacity: 0.5;
|
||||
font-size: 14px;
|
||||
display: block; /* anti-zalgo, with overflow hidden */
|
||||
overflow-y: hidden;
|
||||
@ -57,6 +56,15 @@ limitations under the License.
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.mx_EventTile .mx_SenderProfile .mx_SenderProfile_name,
|
||||
.mx_EventTile .mx_SenderProfile .mx_SenderProfile_aux {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.mx_EventTile .mx_SenderProfile .mx_Flair {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.mx_EventTile .mx_MessageTimestamp {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
|
@ -21,9 +21,7 @@ limitations under the License.
|
||||
}
|
||||
|
||||
.mx_RoomSettings_leaveButton,
|
||||
.mx_RoomSettings_unbanButton,
|
||||
.mx_RoomSettings_integrationsButton,
|
||||
.mx_RoomSettings_integrationsButton_error {
|
||||
.mx_RoomSettings_unbanButton {
|
||||
position: relative;
|
||||
height: 36px;
|
||||
background-color: $accent-color;
|
||||
@ -36,25 +34,37 @@ limitations under the License.
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.mx_RoomSettings_integrationsButton_error {
|
||||
position: relative;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.mx_RoomSettings_integrationsButton_error img {
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
top: -5px;
|
||||
}
|
||||
.mx_RoomSettings_leaveButton,
|
||||
.mx_RoomSettings_integrationsButton,
|
||||
.mx_RoomSettings_integrationsButton_error {
|
||||
float: right;
|
||||
}
|
||||
.mx_RoomSettings_integrationsButton_error {
|
||||
pointer: not-allowed;
|
||||
.mx_RoomSettings_integrationsButton_error .mx_RoomSettings_integrationsButton_errorPopup {
|
||||
display: none;
|
||||
}
|
||||
.mx_RoomSettings_integrationsButton_error:hover .mx_RoomSettings_integrationsButton_errorPopup {
|
||||
display: inline;
|
||||
}
|
||||
.mx_RoomSettings_integrationsButton_errorPopup {
|
||||
position: absolute;
|
||||
top: 110%;
|
||||
left: -26%;
|
||||
width: 150%;
|
||||
left: -125%;
|
||||
width: 348%;
|
||||
padding: 2%;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5em;
|
||||
border-radius: 5px;
|
||||
background-color: $accent-color;
|
||||
color: $accent-fg-color;
|
||||
text-align: center;
|
||||
}
|
||||
.mx_RoomSettings_unbanButton {
|
||||
display: inline;
|
||||
|
@ -22,6 +22,8 @@ $warning-color: #ff0064;
|
||||
$mention-user-pill-bg-color: #ff0064;
|
||||
$other-user-pill-bg-color: rgba(0, 0, 0, 0.1);
|
||||
|
||||
$group-alert-color: #774f7e;
|
||||
|
||||
$preview-bar-bg-color: #f7f7f7;
|
||||
|
||||
// left-panel style muted accent color
|
||||
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
.mx_DevTools_RoomStateExplorer_button, .mx_DevTools_RoomStateExplorer_query {
|
||||
margin-bottom: 10px;
|
||||
}
|
@ -176,8 +176,9 @@ describe('joining a room', function () {
|
||||
|
||||
return Promise.delay(1);
|
||||
}).then(() => {
|
||||
// We've joined, expect this to false
|
||||
expect(roomView.state.joining).toBe(false);
|
||||
// NB. we don't expect the 'joining' flag to reset at any point:
|
||||
// it will stay set and we observe whether we have Room object for
|
||||
// the room and whether our member event shows we're joined.
|
||||
|
||||
// now send the room down the /sync pipe
|
||||
httpBackend.when('GET', '/sync').
|
||||
|
Loading…
Reference in New Issue
Block a user