diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index 0b4266c0b5..1a12432c87 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -18,7 +18,6 @@ src/components/structures/ScrollPanel.js src/components/structures/SearchBox.js src/components/structures/TimelinePanel.js src/components/structures/UploadBar.js -src/components/structures/UserSettings.js src/components/views/avatars/BaseAvatar.js src/components/views/avatars/MemberAvatar.js src/components/views/create_room/RoomAlias.js diff --git a/.travis.yml b/.travis.yml index 0def6d50f7..0746cc0dff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,22 @@ node_js: addons: chrome: stable install: - - npm install -# install synapse prerequisites for end to end tests - - sudo apt-get install build-essential python2.7-dev libffi-dev python-pip python-setuptools sqlite3 libssl-dev python-virtualenv libjpeg-dev libxslt1-dev -script: - ./scripts/travis.sh + - ./scripts/travis/install-deps.sh +matrix: + include: + - name: Linting Checks + script: + # run the linter, but exclude any files known to have errors or warnings. + - npm run lintwithexclusions + - name: End-to-End Tests + if: branch = develop + install: + - sudo apt-get install build-essential python2.7-dev libffi-dev python-pip python-setuptools sqlite3 libssl-dev python-virtualenv libjpeg-dev libxslt1-dev + script: + - ./scripts/travis/end-to-end-tests.sh + - name: Unit Tests + script: + - ./scripts/travis/unit-tests.sh + - name: Riot-web Unit Tests + script: + - ./scripts/travis/riot-unit-tests.sh diff --git a/package.json b/package.json index 6dc9a6bfcf..8804c0911b 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "start:init": "babel src -d lib --source-maps --copy-files", "lint": "eslint src/", "lintall": "eslint src/ test/", - "lintwithexclusions": "eslint --max-warnings 20 --ignore-path .eslintignore.errorfiles src test", + "lintwithexclusions": "eslint --max-warnings 19 --ignore-path .eslintignore.errorfiles src test", "clean": "rimraf lib", "prepublish": "npm run clean && npm run build && git rev-parse HEAD > git-revision.txt", "test": "karma start --single-run=true --browsers ChromeHeadless", @@ -118,7 +118,7 @@ "babel-preset-react": "^6.24.1", "chokidar": "^1.6.1", "concurrently": "^4.0.1", - "eslint": "^5.8.0", + "eslint": "^5.12.0", "eslint-config-google": "^0.7.1", "eslint-plugin-babel": "^5.2.1", "eslint-plugin-flowtype": "^2.30.0", diff --git a/res/css/_components.scss b/res/css/_components.scss index 1f99699e73..17737aca14 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -5,6 +5,7 @@ @import "./structures/_ContextualMenu.scss"; @import "./structures/_CreateRoom.scss"; @import "./structures/_FilePanel.scss"; +@import "./structures/_GroupGridView.scss"; @import "./structures/_GroupView.scss"; @import "./structures/_HomePage.scss"; @import "./structures/_LeftPanel.scss"; diff --git a/res/css/structures/_GroupGridView.scss b/res/css/structures/_GroupGridView.scss new file mode 100644 index 0000000000..541052175d --- /dev/null +++ b/res/css/structures/_GroupGridView.scss @@ -0,0 +1,130 @@ +/* +Copyright 2017 Vector Creations 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_GroupGridView { + display: flex; + flex-direction: column; +} + +.mx_GroupGridView_rooms { + display: grid; + grid-template-columns: repeat(3, calc(100% / 3)); + grid-template-rows: repeat(2, calc(100% / 2)); + flex: 1 1 0; + min-width: 0; +} + +.mx_GroupGridView_rightPanel { + display: flex; + flex-direction: column; + + .mx_GroupGridView_tabs { + flex: 0 0 52px; + border-bottom: 1px solid $primary-hairline-color; + display: flex; + align-items: center; + + > div { + justify-content: flex-end; + width: 100%; + margin-right: 10px; + } + } + + .mx_RightPanel { + flex: 1 0 auto !important; + } +} + + +.mx_GroupGridView > .mx_MainSplit { + flex: 1 1 0; + display: flex; +} + +.mx_GroupGridView_emptyTile { + display: block; + margin-top: 100px; + text-align: center; + user-select: none; +} + +.mx_GroupGridView_tile { + border-right: 1px solid $panel-divider-color; + border-bottom: 1px solid $panel-divider-color; +} + +.mx_GroupGridView_activeTile { + position: relative; +} + +.mx_GroupGridView_activeTile:before, +.mx_GroupGridView_activeTile:after { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + content: ""; + pointer-events: none; + z-index: 3500; +} + +.mx_GroupGridView_activeTile:before { + border-radius: 14px; + border: 8px solid $gridview-focus-border-glow-color; + margin: -8px; +} + +.mx_GroupGridView_activeTile:after { + border-radius: 8px; + border: 2px solid $gridview-focus-border-color; + margin: -2px; +} + +.mx_GroupGridView_tile > .mx_RoomView { + height: 100%; +} + +.mx_GroupGridView_rooms > *:nth-child(1) { + grid-column: 1; + grid-row: 1; +} + +.mx_GroupGridView_rooms > *:nth-child(2) { + grid-column: 2; + grid-row: 1; +} + +.mx_GroupGridView_rooms > *:nth-child(3) { + grid-column: 3; + grid-row: 1; +} + +.mx_GroupGridView_rooms > *:nth-child(4) { + grid-column: 1; + grid-row: 2; +} + +.mx_GroupGridView_rooms > *:nth-child(5) { + grid-column: 2; + grid-row: 2; +} + +.mx_GroupGridView_rooms > *:nth-child(6) { + grid-column: 3; + grid-row: 2; +} diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index 47cb231b58..941417eccc 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -41,10 +41,6 @@ limitations under the License. cursor: pointer; } -.mx_LeftPanel_callView { - -} - .mx_LeftPanel { flex: 1; overflow-x: hidden; diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index 1ccbd19391..6d8b79ecb2 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -73,14 +73,16 @@ limitations under the License. .mx_MatrixChat > :not(.mx_LeftPanel_container):not(.mx_ResizeHandle) { background-color: $primary-bg-color; - flex: 1; + flex: 1 1 0; + min-width: 0; /* Experimental fix for https://github.com/vector-im/vector-web/issues/947 and https://github.com/vector-im/vector-web/issues/946. Empirically this stops the MessagePanel's width exploding outwards when gemini is in 'prevented' mode */ - overflow-x: auto; + // disabling this for now as it clips the active room rect on the grid view + // overflow-x: auto; /* To fix https://github.com/vector-im/riot-web/issues/3298 where Safari needed height 100% all the way down to the HomePage. Height does not diff --git a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss index 615b61f842..3f4c8d2da4 100644 --- a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss +++ b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss @@ -14,10 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_CreateKeyBackupDialog { - padding-right: 40px; -} - .mx_CreateKeyBackupDialog .mx_Dialog_title { /* TODO: Consider setting this for all dialog titles. */ margin-bottom: 1em; diff --git a/res/css/views/dialogs/keybackup/_NewRecoveryMethodDialog.scss b/res/css/views/dialogs/keybackup/_NewRecoveryMethodDialog.scss index 370f82d9ab..f2ebe59925 100644 --- a/res/css/views/dialogs/keybackup/_NewRecoveryMethodDialog.scss +++ b/res/css/views/dialogs/keybackup/_NewRecoveryMethodDialog.scss @@ -24,7 +24,7 @@ limitations under the License. padding-bottom: 10px; &:before { - mask: url("../../../img/e2e/lock-warning.svg"); + mask: url("../../img/e2e/lock-warning.svg"); mask-repeat: no-repeat; background-color: $primary-fg-color; content: ""; diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index cea4b7897d..e35f4d60f4 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -19,6 +19,11 @@ cursor: pointer; } +/* More specific to override `.markdown-body a` text-decoration */ +.mx_EventTile_content .markdown-body a.mx_Pill { + text-decoration: none; +} + /* More specific to override `.markdown-body a` color */ .mx_EventTile_content .markdown-body a.mx_UserPill, .mx_UserPill { @@ -31,7 +36,9 @@ background-color: $accent-color ! important; } +/* More specific to override `.markdown-body a` color */ .mx_EventTile_highlight .mx_EventTile_content .markdown-body a.mx_UserPill_me, +.mx_EventTile_content .markdown-body a.mx_AtRoomPill, .mx_EventTile_content .mx_AtRoomPill, .mx_MessageComposer_input .mx_AtRoomPill { color: $accent-fg-color; diff --git a/res/css/views/messages/_MImageBody.scss b/res/css/views/messages/_MImageBody.scss index a5a1e66a3b..86bc022829 100644 --- a/res/css/views/messages/_MImageBody.scss +++ b/res/css/views/messages/_MImageBody.scss @@ -56,4 +56,6 @@ limitations under the License. border-radius: 5px; background: $imagebody-giflabel; border: 2px solid $imagebody-giflabel-border; + color: $imagebody-giflabel-color; + pointer-events: none; } diff --git a/res/css/views/rooms/_MemberList.scss b/res/css/views/rooms/_MemberList.scss index 6f9491b22f..567727fb64 100644 --- a/res/css/views/rooms/_MemberList.scss +++ b/res/css/views/rooms/_MemberList.scss @@ -53,6 +53,10 @@ limitations under the License. .mx_MemberList_query, .mx_GroupMemberList_query, .mx_GroupRoomList_query { + flex: 0 0 auto; +} + +.mx_MemberList .gm-scrollbar-container { flex: 1 1 0; } diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 70d505e4ea..56ca715b51 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -54,12 +54,14 @@ limitations under the License. align-items: center; flex: 1; vertical-align: middle; + min-width: 0; } .mx_RoomTile_labelContainer { display: flex; flex-direction: column; flex: 1; + min-width: 0; } .mx_RoomTile_subtext { @@ -99,7 +101,6 @@ limitations under the License. } .mx_RoomTile_name { - flex: 1 5 auto; font-size: 14px; font-weight: 600; padding: 0 6px; diff --git a/res/img/feather-icons/toggle-right-panel.svg b/res/img/feather-icons/toggle-right-panel.svg new file mode 100644 index 0000000000..4cadf89564 --- /dev/null +++ b/res/img/feather-icons/toggle-right-panel.svg @@ -0,0 +1,17 @@ + + \ No newline at end of file diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 636db5b39e..257b723ccf 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -162,8 +162,13 @@ $lightbox-bg-color: #454545; $lightbox-fg-color: #ffffff; $lightbox-border-color: #ffffff; +/*** GroupGridView ***/ +$gridview-focus-border-glow-color: rgba(134, 193, 165, 0.5); +$gridview-focus-border-color: rgba(134, 193, 165, 1); + $imagebody-giflabel: rgba(1, 1, 1, 0.7); $imagebody-giflabel-border: rgba(1, 1, 1, 0.2); +$imagebody-giflabel-color: rgba(0, 0, 0, 1); // unused? $progressbar-color: #000; diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 08a287ad71..732cabf494 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -99,6 +99,7 @@ $lightbox-background-bg-color: #000; $imagebody-giflabel: rgba(0, 0, 0, 0.7); $imagebody-giflabel-border: rgba(0, 0, 0, 0.2); +$imagebody-giflabel-color: rgba(255, 255, 255, 1); $greyed-fg-color: #888; @@ -183,6 +184,9 @@ $lightbox-bg-color: #454545; $lightbox-fg-color: #ffffff; $lightbox-border-color: #ffffff; +/*** GroupGridView ***/ +$gridview-focus-border-glow-color: rgba(134, 193, 165, 0.5); +$gridview-focus-border-color: rgba(134, 193, 165, 1); // unused? $progressbar-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 9fcb58d7f1..10a8fcd1e5 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -175,8 +175,13 @@ $lightbox-bg-color: #454545; $lightbox-fg-color: #ffffff; $lightbox-border-color: #ffffff; +/*** GroupGridView ***/ +$gridview-focus-border-glow-color: rgba(134, 193, 165, 0.5); +$gridview-focus-border-color: rgba(134, 193, 165, 1); + $imagebody-giflabel: rgba(0, 0, 0, 0.7); $imagebody-giflabel-border: rgba(0, 0, 0, 0.2); +$imagebody-giflabel-color: rgba(255, 255, 255, 1); // unused? $progressbar-color: #000; diff --git a/.travis-test-riot.sh b/scripts/travis/build.sh similarity index 61% rename from .travis-test-riot.sh rename to scripts/travis/build.sh index d1c2804b2a..a353e38a06 100755 --- a/.travis-test-riot.sh +++ b/scripts/travis/build.sh @@ -24,18 +24,4 @@ rm -r node_modules/matrix-react-sdk ln -s "$REACT_SDK_DIR" node_modules/matrix-react-sdk npm run build -npm run test popd - -if [ "$TRAVIS_BRANCH" = "develop" ] -then - # run end to end tests - scripts/fetchdep.sh matrix-org matrix-react-end-to-end-tests master - pushd matrix-react-end-to-end-tests - ln -s $REACT_SDK_DIR/$RIOT_WEB_DIR riot/riot-web - # PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ./install.sh - # CHROME_PATH=$(which google-chrome-stable) ./run.sh - ./install.sh - ./run.sh --travis - popd -fi diff --git a/scripts/travis/end-to-end-tests.sh b/scripts/travis/end-to-end-tests.sh new file mode 100755 index 0000000000..361b053d2b --- /dev/null +++ b/scripts/travis/end-to-end-tests.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# script which is run by the travis build (after `npm run test`). +# +# clones riot-web develop and runs the tests against our version of react-sdk. + +set -ev + +RIOT_WEB_DIR=riot-web +REACT_SDK_DIR=`pwd` + +scripts/travis/build.sh +# run end to end tests +scripts/fetchdep.sh matrix-org matrix-react-end-to-end-tests master +pushd matrix-react-end-to-end-tests +ln -s $REACT_SDK_DIR/$RIOT_WEB_DIR riot/riot-web +# PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ./install.sh +# CHROME_PATH=$(which google-chrome-stable) ./run.sh +./install.sh +./run.sh --travis +popd diff --git a/scripts/travis.sh b/scripts/travis/install-deps.sh similarity index 57% rename from scripts/travis.sh rename to scripts/travis/install-deps.sh index 48410ea904..04cd728157 100755 --- a/scripts/travis.sh +++ b/scripts/travis/install-deps.sh @@ -1,7 +1,7 @@ #!/bin/sh set -ex - +npm install scripts/fetchdep.sh matrix-org matrix-js-sdk rm -r node_modules/matrix-js-sdk || true ln -s ../matrix-js-sdk node_modules/matrix-js-sdk @@ -9,9 +9,3 @@ ln -s ../matrix-js-sdk node_modules/matrix-js-sdk cd matrix-js-sdk npm install cd .. - -npm run test -./.travis-test-riot.sh - -# run the linter, but exclude any files known to have errors or warnings. -npm run lintwithexclusions diff --git a/scripts/travis/riot-unit-tests.sh b/scripts/travis/riot-unit-tests.sh new file mode 100755 index 0000000000..a2f0d61112 --- /dev/null +++ b/scripts/travis/riot-unit-tests.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# +# script which is run by the travis build (after `npm run test`). +# +# clones riot-web develop and runs the tests against our version of react-sdk. + +set -ev + +RIOT_WEB_DIR=riot-web + +scripts/travis/build.sh +pushd "$RIOT_WEB_DIR" +npm run test +popd diff --git a/scripts/travis/unit-tests.sh b/scripts/travis/unit-tests.sh new file mode 100755 index 0000000000..a8e0a63b31 --- /dev/null +++ b/scripts/travis/unit-tests.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# +# script which is run by the travis build (after `npm run test`). +# +# clones riot-web develop and runs the tests against our version of react-sdk. + +set -ev + +scripts/travis/build.sh +npm run test diff --git a/src/ActiveRoomObserver.js b/src/ActiveRoomObserver.js index d6fbb460b5..c276cccb5d 100644 --- a/src/ActiveRoomObserver.js +++ b/src/ActiveRoomObserver.js @@ -14,10 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import RoomViewStore from './stores/RoomViewStore'; +import OpenRoomsStore from './stores/OpenRoomsStore'; /** - * Consumes changes from the RoomViewStore and notifies specific things + * Consumes changes from the OpenRoomsStore and notifies specific things * about when the active room changes. Unlike listening for RoomViewStore * changes, you can subscribe to only changes relevant to a particular * room. @@ -28,11 +28,15 @@ import RoomViewStore from './stores/RoomViewStore'; class ActiveRoomObserver { constructor() { this._listeners = {}; - - this._activeRoomId = RoomViewStore.getRoomId(); + const roomStore = OpenRoomsStore.getActiveRoomStore(); + this._activeRoomId = roomStore && roomStore.getRoomId(); // TODO: We could self-destruct when the last listener goes away, or at least // stop listening. - this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate.bind(this)); + this._roomStoreToken = OpenRoomsStore.addListener(this._onOpenRoomsStoreUpdate.bind(this)); + } + + getActiveRoomId() { + return this._activeRoomId; } addListener(roomId, listener) { @@ -51,23 +55,23 @@ class ActiveRoomObserver { } } - _emit(roomId) { + _emit(roomId, newActiveRoomId) { if (!this._listeners[roomId]) return; for (const l of this._listeners[roomId]) { - l.call(); + l.call(l, newActiveRoomId); } } - _onRoomViewStoreUpdate() { + _onOpenRoomsStoreUpdate() { + const activeRoomStore = OpenRoomsStore.getActiveRoomStore(); + const newActiveRoomId = activeRoomStore && activeRoomStore.getRoomId(); // emit for the old room ID - if (this._activeRoomId) this._emit(this._activeRoomId); - + if (this._activeRoomId) this._emit(this._activeRoomId, newActiveRoomId); // update our cache - this._activeRoomId = RoomViewStore.getRoomId(); - + this._activeRoomId = newActiveRoomId; // and emit for the new one - if (this._activeRoomId) this._emit(this._activeRoomId); + if (this._activeRoomId) this._emit(this._activeRoomId, this._activeRoomId); } } diff --git a/src/PageTypes.js b/src/PageTypes.js index 60111723fb..e4e1916c8b 100644 --- a/src/PageTypes.js +++ b/src/PageTypes.js @@ -19,6 +19,7 @@ limitations under the License. export default { HomePage: "home_page", RoomView: "room_view", + GroupGridView: "group_grid_view", UserSettings: "user_settings", RoomDirectory: "room_directory", UserView: "user_view", diff --git a/src/UserActivity.js b/src/UserActivity.js index 4e3667274c..145b23e36e 100644 --- a/src/UserActivity.js +++ b/src/UserActivity.js @@ -44,6 +44,7 @@ class UserActivity { * Can be called multiple times with the same already running timer, which is a NO-OP. * Can be called before the user becomes active, in which case it is only started * later on when the user does become active. + * @param {Timer} timer the timer to use */ timeWhileActive(timer) { // important this happens first diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index c593a9b3ea..c5a7ff558d 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -1,5 +1,5 @@ /* -Copyright 2018 New Vector Ltd +Copyright 2018, 2019 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. @@ -21,7 +21,7 @@ import { scorePassword } from '../../../../utils/PasswordScorer'; import FileSaver from 'file-saver'; -import { _t, _td } from '../../../../languageHandler'; +import { _t } from '../../../../languageHandler'; const PHASE_PASSPHRASE = 0; const PHASE_PASSPHRASE_CONFIRM = 1; @@ -102,7 +102,7 @@ export default React.createClass({ info = await MatrixClientPeg.get().createKeyBackupVersion( this._keyBackupInfo, ); - await MatrixClientPeg.get().backupAllGroupSessions(info.version); + await MatrixClientPeg.get().scheduleAllGroupSessionsForBackup(); this.setState({ phase: PHASE_DONE, }); @@ -344,7 +344,10 @@ export default React.createClass({ _renderPhaseShowKey: function() { let bodyText; if (this.state.setPassPhrase) { - bodyText = _t("As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase."); + bodyText = _t( + "As a safety net, you can use it to restore your encrypted message " + + "history if you forget your Recovery Passphrase.", + ); } else { bodyText = _t("As a safety net, you can use it to restore your encrypted message history."); } @@ -352,7 +355,7 @@ export default React.createClass({ return
{_t("Make a copy of this Recovery Key and keep it safe.")}
{bodyText}
-+
{_t(text)}
{_t("Backup created")}
-{_t("Your encryption keys are now being backed up to your Homeserver.")}
+{_t( + "Your encryption keys are now being backed up in the background " + + "to your Homeserver. The initial backup could take several minutes. " + + "You can view key backup upload progress in Settings.")}
{_t( diff --git a/src/autocomplete/CommunityProvider.js b/src/autocomplete/CommunityProvider.js index b85c09b320..d164fab46a 100644 --- a/src/autocomplete/CommunityProvider.js +++ b/src/autocomplete/CommunityProvider.js @@ -61,7 +61,7 @@ export default class CommunityProvider extends AutocompleteProvider { if (command) { const joinedGroups = cli.getGroups().filter(({myMembership}) => myMembership === 'join'); - const groups = (await Promise.all(joinedGroups.map(async({groupId}) => { + const groups = (await Promise.all(joinedGroups.map(async ({groupId}) => { try { return FlairStore.getGroupProfileCached(cli, groupId); } catch (e) { // if FlairStore failed, fall back to just groupId diff --git a/src/components/structures/GroupGridView.js b/src/components/structures/GroupGridView.js new file mode 100644 index 0000000000..a1a9e1b183 --- /dev/null +++ b/src/components/structures/GroupGridView.js @@ -0,0 +1,127 @@ +/* +Copyright 2017 Vector Creations Ltd. +Copyright 2017, 2018 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. +*/ + +import React from 'react'; +import OpenRoomsStore from '../../stores/OpenRoomsStore'; +import dis from '../../dispatcher'; +import {_t} from '../../languageHandler'; +import RoomView from './RoomView'; +import classNames from 'classnames'; +import MainSplit from './MainSplit'; +import RightPanel from './RightPanel'; +import RoomHeaderButtons from '../views/right_panel/RoomHeaderButtons'; + +export default class RoomGridView extends React.Component { + constructor(props) { + super(props); + this.state = { + roomStores: OpenRoomsStore.getRoomStores(), + activeRoomStore: OpenRoomsStore.getActiveRoomStore(), + }; + this.onRoomsChanged = this.onRoomsChanged.bind(this); + } + + componentDidUpdate(_, prevState) { + const store = this.state.activeRoomStore; + if (store) { + store.getDispatcher().dispatch({action: 'focus_composer'}); + } + } + + componentDidMount() { + this.componentDidUpdate(); + } + + componentWillMount() { + this._unmounted = false; + this._openRoomsStoreRegistration = OpenRoomsStore.addListener(this.onRoomsChanged); + } + + componentWillUnmount() { + this._unmounted = true; + if (this._openRoomsStoreRegistration) { + this._openRoomsStoreRegistration.remove(); + } + } + + onRoomsChanged() { + if (this._unmounted) return; + this.setState({ + roomStores: OpenRoomsStore.getRoomStores(), + activeRoomStore: OpenRoomsStore.getActiveRoomStore(), + }); + } + + _setActive(i) { + const store = OpenRoomsStore.getRoomStoreAt(i); + if (store !== this.state.activeRoomStore) { + dis.dispatch({ + action: 'group_grid_set_active', + room_id: store.getRoomId(), + }); + } + } + + render() { + let roomStores = this.state.roomStores.slice(0, 6); + const emptyCount = 6 - roomStores.length; + if (emptyCount) { + const emptyTiles = Array.from({length: emptyCount}, () => null); + roomStores = roomStores.concat(emptyTiles); + } + const activeRoomId = this.state.activeRoomStore && this.state.activeRoomStore.getRoomId(); + let rightPanel; + if (activeRoomId) { + rightPanel = ( +
{_t( + "When you log out, you'll lose your secure message history. To prevent " + + "this, set up a recovery method.", + )}
+{_t( + "Alternatively, advanced users can also manually export encryption keys in " + + "Settings before logging out.", {}, + { + a: sub => {sub}, + }, + )}
+{
@@ -229,7 +228,7 @@ export default class MessageComposer extends React.Component {
if (!call) {
return;
}
- dis.dispatch({
+ this.props.roomViewStore.getDispatcher().dispatch({
action: 'hangup',
// hangup the call for this room, which may not be the room in props
// (e.g. conferences which will hangup the 1:1 room instead)
@@ -238,7 +237,7 @@ export default class MessageComposer extends React.Component {
}
onCallClick(ev) {
- dis.dispatch({
+ this.props.roomViewStore.getDispatcher().dispatch({
action: 'place_call',
type: ev.shiftKey ? "screensharing" : "video",
room_id: this.props.room.roomId,
@@ -246,7 +245,7 @@ export default class MessageComposer extends React.Component {
}
onVoiceCallClick(ev) {
- dis.dispatch({
+ this.props.roomViewStore.getDispatcher().dispatch({
action: 'place_call',
type: "voice",
room_id: this.props.room.roomId,
@@ -282,7 +281,7 @@ export default class MessageComposer extends React.Component {
ev.preventDefault();
const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
- dis.dispatch({
+ this.props.roomViewStore.getDispatcher().dispatch({
action: 'view_room',
highlighted: true,
room_id: replacementRoomId,
@@ -421,8 +420,10 @@ export default class MessageComposer extends React.Component {
controls.push(
+
+
{_t("Algorithm: ")}{this.state.backupInfo.algorithm}
{clientBackupStatus}
+ {uploadStatus}