From 7d22bbc00f49356cd5fec3565a19376ef8b0ef05 Mon Sep 17 00:00:00 2001 From: Bryan Kok Date: Sat, 17 Oct 2020 23:52:18 +0800 Subject: [PATCH 001/916] Trim spurious whitespace of nicknames --- src/components/views/settings/ProfileSettings.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/ProfileSettings.js b/src/components/views/settings/ProfileSettings.js index 92851ccaa0..294f80acd1 100644 --- a/src/components/views/settings/ProfileSettings.js +++ b/src/components/views/settings/ProfileSettings.js @@ -77,10 +77,12 @@ export default class ProfileSettings extends React.Component { const client = MatrixClientPeg.get(); const newState = {}; + const displayName = this.state.displayName.trim(); try { if (this.state.originalDisplayName !== this.state.displayName) { - await client.setDisplayName(this.state.displayName); - newState.originalDisplayName = this.state.displayName; + await client.setDisplayName(displayName); + newState.originalDisplayName = displayName; + newState.displayName = displayName; } if (this.state.avatarFile) { From fcbaea640daf3a036d55cb1bda5d7fed552c2d4e Mon Sep 17 00:00:00 2001 From: Bryan Kok Date: Sun, 18 Oct 2020 14:36:50 +0800 Subject: [PATCH 002/916] Trim room names changed through the UI --- src/components/views/room_settings/RoomProfileSettings.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/room_settings/RoomProfileSettings.js b/src/components/views/room_settings/RoomProfileSettings.js index ca09c3093a..b894663c16 100644 --- a/src/components/views/room_settings/RoomProfileSettings.js +++ b/src/components/views/room_settings/RoomProfileSettings.js @@ -95,10 +95,11 @@ export default class RoomProfileSettings extends React.Component { const newState = {}; // TODO: What do we do about errors? - + const displayName = this.state.displayName.trim(); if (this.state.originalDisplayName !== this.state.displayName) { - await client.setRoomName(this.props.roomId, this.state.displayName); - newState.originalDisplayName = this.state.displayName; + await client.setRoomName(this.props.roomId, displayName); + newState.originalDisplayName = displayName; + newState.displayName = displayName; } if (this.state.avatarFile) { From 29a81bbe85ca1ca1c4c54534fe94e9d477aef22c Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Tue, 10 Nov 2020 15:04:01 -0600 Subject: [PATCH 003/916] Warn when you attempt to leave room that you are the only member of Signed-off-by: Aaron Raimist --- src/components/structures/MatrixChat.tsx | 17 ++++++++++++++++- src/i18n/strings/en_EN.json | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 22cd73eff7..43e1798c6e 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1056,8 +1056,21 @@ export default class MatrixChat extends React.PureComponent { private leaveRoomWarnings(roomId: string) { const roomToLeave = MatrixClientPeg.get().getRoom(roomId); // Show a warning if there are additional complications. - const joinRules = roomToLeave.currentState.getStateEvents('m.room.join_rules', ''); const warnings = []; + + const memberCount = roomToLeave.currentState.getJoinedMemberCount(); + if (memberCount === 1) { + warnings.push( + + {' '/* Whitespace, otherwise the sentences get smashed together */ } + { _t("You are the only member of this room. This room will become unjoinable if you leave.") } + + ); + + return warnings; + } + + const joinRules = roomToLeave.currentState.getStateEvents('m.room.join_rules', ''); if (joinRules) { const rule = joinRules.getContent().join_rule; if (rule !== "public") { @@ -1076,6 +1089,7 @@ export default class MatrixChat extends React.PureComponent { const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const roomToLeave = MatrixClientPeg.get().getRoom(roomId); const warnings = this.leaveRoomWarnings(roomId); + const hasWarnings = warnings.length > 0; Modal.createTrackedDialog('Leave room', '', QuestionDialog, { title: _t("Leave room"), @@ -1086,6 +1100,7 @@ export default class MatrixChat extends React.PureComponent { ), button: _t("Leave"), + danger: hasWarnings, onFinished: (shouldLeave) => { if (shouldLeave) { const d = leaveRoomBehaviour(roomId); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 78340447f3..b412db5ca0 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2317,6 +2317,7 @@ "Cannot create rooms in this community": "Cannot create rooms in this community", "You do not have permission to create rooms in this community.": "You do not have permission to create rooms in this community.", "This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.", + "You are the only member of this room. This room will become unjoinable if you leave.": "You are the only member of this room. This room will become unjoinable if you leave.", "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s", "Signed Out": "Signed Out", From 80c4d54ccc56396e15a4979de84d9d18c83a70ad Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 18 Nov 2020 13:54:49 -0600 Subject: [PATCH 004/916] Fix lint Signed-off-by: Aaron Raimist --- src/components/structures/MatrixChat.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 43e1798c6e..17c21a2016 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1060,12 +1060,12 @@ export default class MatrixChat extends React.PureComponent { const memberCount = roomToLeave.currentState.getJoinedMemberCount(); if (memberCount === 1) { - warnings.push( + warnings.push(( {' '/* Whitespace, otherwise the sentences get smashed together */ } { _t("You are the only member of this room. This room will become unjoinable if you leave.") } - ); + )); return warnings; } From 2f988bc97fc1e414bd70149ac5f77301a3ef2833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 26 Nov 2020 13:51:03 +0100 Subject: [PATCH 005/916] Added UI --- .../views/settings/SpellCheckSettings.tsx | 111 ++++++++++++++++++ .../tabs/user/GeneralUserSettingsTab.js | 18 +++ 2 files changed, 129 insertions(+) create mode 100644 src/components/views/settings/SpellCheckSettings.tsx diff --git a/src/components/views/settings/SpellCheckSettings.tsx b/src/components/views/settings/SpellCheckSettings.tsx new file mode 100644 index 0000000000..1bdcd882c9 --- /dev/null +++ b/src/components/views/settings/SpellCheckSettings.tsx @@ -0,0 +1,111 @@ +/* +Copyright 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. +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 LanguageDropdown from "../../../components/views/elements/LanguageDropdown"; +import AccessibleButton from "../../../components/views/elements/AccessibleButton"; +import {_t} from "../../../languageHandler"; + +interface ExistingSpellCheckLanguageIProps { + language: string, + onRemoved(language: string), +}; + +interface SpellCheckLanguagesIProps { + languages: Array, + onLanguagesChange(languages: Array), +}; + +interface SpellCheckLanguagesIState { + newLanguage: string, +} + +export class ExistingSpellCheckLanguage extends React.Component { + _onRemove = (e) => { + e.stopPropagation(); + e.preventDefault(); + + return this.props.onRemoved(this.props.language); + }; + + render() { + return ( +
+ {this.props.language} + + {_t("Remove")} + +
+ ); + } +} + +export default class SpellCheckLanguages extends React.Component { + constructor(props) { + super(props); + this.state = { + newLanguage: "", + } + } + + _onRemoved = (language) => { + const languages = this.props.languages.filter((e) => e !== language); + this.props.onLanguagesChange(languages); + }; + + _onAddClick = (e) => { + e.stopPropagation(); + e.preventDefault(); + + const language = this.state.newLanguage; + + if (!language) return; + if (this.props.languages.includes(language)) return; + + this.props.languages.push(language) + this.props.onLanguagesChange(this.props.languages); + }; + + _onNewLanguageChange = (language: string) => { + if (this.state.newLanguage === language) return; + this.setState({newLanguage: language}); + }; + + render() { + const existingSpellCheckLanguages = this.props.languages.map((e) => { + return ; + }); + + let addButton = ( + + {_t("Add")} + + ); + + return ( +
+ {existingSpellCheckLanguages} +
+ + {addButton} + +
+ ); + }; +} diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 35285351ab..6d04d83047 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -22,6 +22,7 @@ import ProfileSettings from "../../ProfileSettings"; import * as languageHandler from "../../../../../languageHandler"; import SettingsStore from "../../../../../settings/SettingsStore"; import LanguageDropdown from "../../../elements/LanguageDropdown"; +import SpellCheckSettings from "../../SpellCheckSettings" import AccessibleButton from "../../../elements/AccessibleButton"; import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog"; import PropTypes from "prop-types"; @@ -49,6 +50,7 @@ export default class GeneralUserSettingsTab extends React.Component { this.state = { language: languageHandler.getCurrentLanguage(), + spellCheckLanguages: [], haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()), serverSupportsSeparateAddAndBind: null, idServerHasUnsignedTerms: false, @@ -182,6 +184,10 @@ export default class GeneralUserSettingsTab extends React.Component { PlatformPeg.get().reload(); }; + _onSpellCheckLanguagesChange = (languages) => { + this.setState({spellCheckLanguages: languages}) + }; + _onPasswordChangeError = (err) => { // TODO: Figure out a design that doesn't involve replacing the current dialog let errMsg = err.error || ""; @@ -303,6 +309,17 @@ export default class GeneralUserSettingsTab extends React.Component { ); } + _renderSpellCheckSection() { + return ( +
+ {_t("Spell checking")} + +
+ ); + } + _renderDiscoverySection() { const SetIdServer = sdk.getComponent("views.settings.SetIdServer"); @@ -409,6 +426,7 @@ export default class GeneralUserSettingsTab extends React.Component { {this._renderProfileSection()} {this._renderAccountSection()} {this._renderLanguageSection()} + {this._renderSpellCheckSection()} { discoverySection } {this._renderIntegrationManagerSection() /* Has its own title */} { accountManagementSection } From 051368eaab50e4a7d6f2ce554ddff0ed957bb965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 26 Nov 2020 13:53:22 +0100 Subject: [PATCH 006/916] Fix i18n --- src/i18n/strings/en_EN.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 0d50128f32..7fbcc1a350 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1109,6 +1109,7 @@ "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Use an Integration Manager to manage bots, widgets, and sticker packs.", "Manage integrations": "Manage integrations", "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.", + "Add": "Add", "Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).", "Checking for an update...": "Checking for an update...", "No update available.": "No update available.", @@ -1140,6 +1141,7 @@ "Set a new account password...": "Set a new account password...", "Account": "Account", "Language and region": "Language and region", + "Spell checking": "Spell checking", "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.", "Account management": "Account management", "Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!", @@ -1337,7 +1339,6 @@ "Invalid Email Address": "Invalid Email Address", "This doesn't appear to be a valid email address": "This doesn't appear to be a valid email address", "Unable to add email address": "Unable to add email address", - "Add": "Add", "We've sent you an email to verify your address. Please follow the instructions there and then click the button below.": "We've sent you an email to verify your address. Please follow the instructions there and then click the button below.", "Email Address": "Email Address", "Remove %(phone)s?": "Remove %(phone)s?", From 557e650a2c2e2eb24584f21bd4175e69cc7500a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 28 Nov 2020 19:37:49 +0100 Subject: [PATCH 007/916] Added spell-check-languages setting --- src/settings/Settings.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 31e133be72..409cd293d2 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -402,6 +402,10 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, default: "en", }, + "spell-check-languages": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, + default: [], + }, "breadcrumb_rooms": { // not really a setting supportedLevels: [SettingLevel.ACCOUNT], From 43daec03e24820a485f97da0b5cb0311f137e729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 28 Nov 2020 19:38:52 +0100 Subject: [PATCH 008/916] Added setSpellCheckLanguages() method --- src/languageHandler.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/languageHandler.tsx b/src/languageHandler.tsx index b61f57d4b3..9b9e304294 100644 --- a/src/languageHandler.tsx +++ b/src/languageHandler.tsx @@ -346,6 +346,13 @@ export function setLanguage(preferredLangs: string | string[]) { }); } +export function setSpellCheckLanguages(preferredLangs: string[]) { + const plaf = PlatformPeg.get(); + if (plaf) { + plaf.setLanguage(preferredLangs); + } +} + export function getAllLanguagesFromJson() { return getLangsJson().then((langsObject) => { const langs = []; From 5e4f9907cf87e02b41791ce729b38d7474dcbf01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 28 Nov 2020 19:39:09 +0100 Subject: [PATCH 009/916] Added persistance --- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 6d04d83047..585f4fd5b7 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -50,7 +50,7 @@ export default class GeneralUserSettingsTab extends React.Component { this.state = { language: languageHandler.getCurrentLanguage(), - spellCheckLanguages: [], + spellCheckLanguages: SettingsStore.getValue("spell-check-languages", null, /*excludeDefault=*/true), haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()), serverSupportsSeparateAddAndBind: null, idServerHasUnsignedTerms: false, @@ -185,7 +185,9 @@ export default class GeneralUserSettingsTab extends React.Component { }; _onSpellCheckLanguagesChange = (languages) => { + SettingsStore.setValue("spell-check-languages", null, SettingLevel.DEVICE, languages); this.setState({spellCheckLanguages: languages}) + PlatformPeg.get().reload(); }; _onPasswordChangeError = (err) => { From f0bbed0c44270f8411c0ce0f4ee0cf08142a1c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 29 Nov 2020 12:17:07 +0100 Subject: [PATCH 010/916] Allow default value --- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 585f4fd5b7..68a16463b0 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -50,7 +50,7 @@ export default class GeneralUserSettingsTab extends React.Component { this.state = { language: languageHandler.getCurrentLanguage(), - spellCheckLanguages: SettingsStore.getValue("spell-check-languages", null, /*excludeDefault=*/true), + spellCheckLanguages: SettingsStore.getValue("spell-check-languages", null, false), haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()), serverSupportsSeparateAddAndBind: null, idServerHasUnsignedTerms: false, From 8f40cd39fda1ee83ec6b177ba7935f913ed5a45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 29 Nov 2020 12:29:05 +0100 Subject: [PATCH 011/916] Added styling --- res/css/_components.scss | 1 + .../views/settings/_SpellCheckLanguages.scss | 36 +++++++++++++++++++ .../views/settings/SpellCheckSettings.tsx | 9 +++-- .../tabs/user/GeneralUserSettingsTab.js | 7 ++-- 4 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 res/css/views/settings/_SpellCheckLanguages.scss diff --git a/res/css/_components.scss b/res/css/_components.scss index 445ed70ff4..1eb4b91a31 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -207,6 +207,7 @@ @import "./views/settings/_DevicesPanel.scss"; @import "./views/settings/_E2eAdvancedPanel.scss"; @import "./views/settings/_EmailAddresses.scss"; +@import "./views/settings/_SpellCheckLanguages.scss"; @import "./views/settings/_IntegrationManager.scss"; @import "./views/settings/_Notifications.scss"; @import "./views/settings/_PhoneNumbers.scss"; diff --git a/res/css/views/settings/_SpellCheckLanguages.scss b/res/css/views/settings/_SpellCheckLanguages.scss new file mode 100644 index 0000000000..734f669f0e --- /dev/null +++ b/res/css/views/settings/_SpellCheckLanguages.scss @@ -0,0 +1,36 @@ +/* +Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. + +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_ExistingSpellCheckLanguage { + display: flex; + align-items: center; + margin-bottom: 5px; +} + +.mx_ExistingSpellCheckLanguage_language { + flex: 1; + margin-right: 10px; +} + +.mx_GeneralUserSettingsTab_spellCheckLanguageInput { + margin-top: 1em; + margin-bottom: 1em; +} + +.mx_SpellCheckLanguages { + @mixin mx_Settings_fullWidthField; +} \ No newline at end of file diff --git a/src/components/views/settings/SpellCheckSettings.tsx b/src/components/views/settings/SpellCheckSettings.tsx index 1bdcd882c9..befd98112e 100644 --- a/src/components/views/settings/SpellCheckSettings.tsx +++ b/src/components/views/settings/SpellCheckSettings.tsx @@ -43,8 +43,8 @@ export class ExistingSpellCheckLanguage extends React.Component - {this.props.language} +
+ {this.props.language} {_t("Remove")} @@ -96,10 +96,9 @@ export default class SpellCheckLanguages extends React.Component +
{existingSpellCheckLanguages} -
+ diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 68a16463b0..258ff6d318 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -313,11 +313,10 @@ export default class GeneralUserSettingsTab extends React.Component { _renderSpellCheckSection() { return ( -
+
{_t("Spell checking")} - +
); } From 7609f2004e6a004c22e0da189f58cee823bf4468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 29 Nov 2020 13:22:50 +0100 Subject: [PATCH 012/916] Added newline to end --- res/css/views/settings/_SpellCheckLanguages.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/settings/_SpellCheckLanguages.scss b/res/css/views/settings/_SpellCheckLanguages.scss index 734f669f0e..ddfa0bf9e0 100644 --- a/res/css/views/settings/_SpellCheckLanguages.scss +++ b/res/css/views/settings/_SpellCheckLanguages.scss @@ -33,4 +33,4 @@ limitations under the License. .mx_SpellCheckLanguages { @mixin mx_Settings_fullWidthField; -} \ No newline at end of file +} From ead00dcdede9e6a24904599baba39bc91de0681a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 29 Nov 2020 14:46:09 +0100 Subject: [PATCH 013/916] Set spell-check languages without reloading --- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 258ff6d318..ad7e04d677 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -187,7 +187,11 @@ export default class GeneralUserSettingsTab extends React.Component { _onSpellCheckLanguagesChange = (languages) => { SettingsStore.setValue("spell-check-languages", null, SettingLevel.DEVICE, languages); this.setState({spellCheckLanguages: languages}) - PlatformPeg.get().reload(); + + const plaf = PlatformPeg.get(); + if (plaf) { + plaf.setLanguage(languages); + } }; _onPasswordChangeError = (err) => { From 38080c5b2bccafb7dae5c9737e69ad7f295f1d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 29 Nov 2020 20:46:47 +0100 Subject: [PATCH 014/916] Added getAvailableSpellCheckLanguages() methods --- src/BasePlatform.ts | 4 ++++ src/languageHandler.tsx | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index 0a1f06f0b3..9ac35092a7 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -237,6 +237,10 @@ export default abstract class BasePlatform { setLanguage(preferredLangs: string[]) {} + getAvailableSpellCheckLanguages(): Promise | null { + return null; + } + protected getSSOCallbackUrl(fragmentAfterLogin: string): URL { const url = new URL(window.location.href); url.hash = fragmentAfterLogin || ""; diff --git a/src/languageHandler.tsx b/src/languageHandler.tsx index 9b9e304294..b827e83ded 100644 --- a/src/languageHandler.tsx +++ b/src/languageHandler.tsx @@ -353,6 +353,11 @@ export function setSpellCheckLanguages(preferredLangs: string[]) { } } +export async function getAvailableSpellCheckLanguages(): Promise { + const plaf = PlatformPeg.get(); + return plaf.getAvailableSpellCheckLanguages(); +} + export function getAllLanguagesFromJson() { return getLangsJson().then((langsObject) => { const langs = []; From 5d9f5ba979d3fc89bf6eec5ad2f319bea7168aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 30 Nov 2020 08:35:51 +0100 Subject: [PATCH 015/916] Fix indentation --- src/components/views/elements/LanguageDropdown.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js index e37109caff..03ec456af5 100644 --- a/src/components/views/elements/LanguageDropdown.js +++ b/src/components/views/elements/LanguageDropdown.js @@ -100,10 +100,10 @@ export default class LanguageDropdown extends React.Component { let language = SettingsStore.getValue("language", null, /*excludeDefault:*/true); let value = null; if (language) { - value = this.props.value || language; + value = this.props.value || language; } else { - language = navigator.language || navigator.userLanguage; - value = this.props.value || language; + language = navigator.language || navigator.userLanguage; + value = this.props.value || language; } return Date: Tue, 1 Dec 2020 16:59:02 +0100 Subject: [PATCH 016/916] Change label --- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index ad7e04d677..8d06ea3b36 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -318,7 +318,7 @@ export default class GeneralUserSettingsTab extends React.Component { _renderSpellCheckSection() { return (
- {_t("Spell checking")} + {_t("Spell check dictionaries")}
From cf61d50df40614a45c36c0d9886c3583ca69513e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 1 Dec 2020 16:59:21 +0100 Subject: [PATCH 017/916] Added SpellCheckLanguagesDropdown --- .../elements/SpellCheckLanguagesDropdown.tsx | 125 ++++++++++++++++++ .../views/settings/SpellCheckSettings.tsx | 4 +- 2 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 src/components/views/elements/SpellCheckLanguagesDropdown.tsx diff --git a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx new file mode 100644 index 0000000000..db158fa3dd --- /dev/null +++ b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx @@ -0,0 +1,125 @@ +/* +Copyright 2017 Marcel Radzio (MTRNord) +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. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; + +import Dropdown from "../../views/elements/Dropdown" +import PlatformPeg from "../../../PlatformPeg"; +import * as sdk from '../../../index'; +import * as languageHandler from '../../../languageHandler'; +import SettingsStore from "../../../settings/SettingsStore"; +import { _t } from "../../../languageHandler"; + +function languageMatchesSearchQuery(query, language) { + if (language.label.toUpperCase().includes(query.toUpperCase())) return true; + if (language.value.toUpperCase() === query.toUpperCase()) return true; + return false; +} + +interface SpellCheckLanguagesDropdownIProps { + className: string, + value: string, + onOptionChange(language: string), +}; + +interface SpellCheckLanguagesDropdownIState { + searchQuery: string, + languages: any, +} + +export default class SpellCheckLanguagesDropdown extends React.Component { + constructor(props) { + super(props); + this._onSearchChange = this._onSearchChange.bind(this); + + this.state = { + searchQuery: '', + languages: null, + }; + } + + componentDidMount() { + languageHandler.getAvailableSpellCheckLanguages().then((languages) => { + languages.sort(function(a, b) { + if (a < b) return -1; + if (a > b) return 1; + return 0; + }); + var langs = []; + languages.forEach((language) => { + langs.push({ + label: language, + value: language, + }) + }) + this.setState({languages: langs}); + }).catch((e) => { + this.setState({languages: ['en']}); + }); + } + + _onSearchChange(search) { + this.setState({ + searchQuery: search, + }); + } + + render() { + if (this.state.languages === null) { + const Spinner = sdk.getComponent('elements.Spinner'); + return ; + } + + let displayedLanguages; + if (this.state.searchQuery) { + displayedLanguages = this.state.languages.filter((lang) => { + return languageMatchesSearchQuery(this.state.searchQuery, lang); + }); + } else { + displayedLanguages = this.state.languages; + } + + const options = displayedLanguages.map((language) => { + return
+ { language.label } +
; + }); + + // default value here too, otherwise we need to handle null / undefined; + // values between mounting and the initial value propgating + let language = SettingsStore.getValue("language", null, /*excludeDefault:*/true); + let value = null; + if (language) { + value = this.props.value || language; + } else { + language = navigator.language || navigator.userLanguage; + value = this.props.value || language; + } + + return + { options } + ; + } +} diff --git a/src/components/views/settings/SpellCheckSettings.tsx b/src/components/views/settings/SpellCheckSettings.tsx index befd98112e..37476d5f34 100644 --- a/src/components/views/settings/SpellCheckSettings.tsx +++ b/src/components/views/settings/SpellCheckSettings.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React from 'react'; -import LanguageDropdown from "../../../components/views/elements/LanguageDropdown"; +import SpellCheckLanguagesDropdown from "../../../components/views/elements/SpellCheckLanguagesDropdown"; import AccessibleButton from "../../../components/views/elements/AccessibleButton"; import {_t} from "../../../languageHandler"; @@ -99,7 +99,7 @@ export default class SpellCheckLanguages extends React.Component {existingSpellCheckLanguages} - {addButton} From a6d6af1a937fb6bc6cec2e320fbff453bef3c680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 1 Dec 2020 17:19:45 +0100 Subject: [PATCH 018/916] Added defaults --- src/settings/Settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 409cd293d2..c83dbab897 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -404,7 +404,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { }, "spell-check-languages": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, - default: [], + default: ["en"], }, "breadcrumb_rooms": { // not really a setting From e9203d75715dbd6a677849dbb83a3d4706b2e6e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 1 Dec 2020 17:21:23 +0100 Subject: [PATCH 019/916] Removed unnecessary imports --- src/components/views/elements/SpellCheckLanguagesDropdown.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx index db158fa3dd..5e0fe3132c 100644 --- a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx +++ b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx @@ -16,10 +16,8 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import Dropdown from "../../views/elements/Dropdown" -import PlatformPeg from "../../../PlatformPeg"; import * as sdk from '../../../index'; import * as languageHandler from '../../../languageHandler'; import SettingsStore from "../../../settings/SettingsStore"; From 8287f197f40869941d402e45da87c88d19514545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 1 Dec 2020 19:49:31 +0100 Subject: [PATCH 020/916] Fix i18n --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 282c1ce686..9ccd0e1e75 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1149,7 +1149,7 @@ "Set a new account password...": "Set a new account password...", "Account": "Account", "Language and region": "Language and region", - "Spell checking": "Spell checking", + "Spell check dictionaries": "Spell check dictionaries", "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.", "Account management": "Account management", "Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!", From 44a363f188fb95927fff942b4c6b5a3914dbe31d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 1 Dec 2020 20:16:48 +0100 Subject: [PATCH 021/916] Fix default value --- src/settings/Settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index c83dbab897..3540767a99 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -404,7 +404,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { }, "spell-check-languages": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, - default: ["en"], + default: ["en-US"], }, "breadcrumb_rooms": { // not really a setting From 3c2bb6e4f6d19e337d902613adbddf42fcba2f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 1 Dec 2020 20:17:24 +0100 Subject: [PATCH 022/916] Cleanup --- src/BasePlatform.ts | 2 ++ .../views/settings/tabs/user/GeneralUserSettingsTab.js | 5 +---- src/languageHandler.tsx | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index 9ac35092a7..2af2ea51c5 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -237,6 +237,8 @@ export default abstract class BasePlatform { setLanguage(preferredLangs: string[]) {} + setSpellCheckLanguages(preferredLangs: string[]) {} + getAvailableSpellCheckLanguages(): Promise | null { return null; } diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 8d06ea3b36..6ed887d749 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -188,10 +188,7 @@ export default class GeneralUserSettingsTab extends React.Component { SettingsStore.setValue("spell-check-languages", null, SettingLevel.DEVICE, languages); this.setState({spellCheckLanguages: languages}) - const plaf = PlatformPeg.get(); - if (plaf) { - plaf.setLanguage(languages); - } + languageHandler.setSpellCheckLanguages(languages); }; _onPasswordChangeError = (err) => { diff --git a/src/languageHandler.tsx b/src/languageHandler.tsx index b827e83ded..38d3c8347a 100644 --- a/src/languageHandler.tsx +++ b/src/languageHandler.tsx @@ -349,7 +349,7 @@ export function setLanguage(preferredLangs: string | string[]) { export function setSpellCheckLanguages(preferredLangs: string[]) { const plaf = PlatformPeg.get(); if (plaf) { - plaf.setLanguage(preferredLangs); + plaf.setSpellCheckLanguages(preferredLangs); } } From db5bc0cb7ade92fc056283af639e3c782d384e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 1 Dec 2020 20:36:25 +0100 Subject: [PATCH 023/916] Fix formatting --- .../elements/SpellCheckLanguagesDropdown.tsx | 7 ++++--- .../views/settings/SpellCheckSettings.tsx | 17 +++++++++-------- .../tabs/user/GeneralUserSettingsTab.js | 6 +++--- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx index 5e0fe3132c..53c3f310b7 100644 --- a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx +++ b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx @@ -33,14 +33,15 @@ interface SpellCheckLanguagesDropdownIProps { className: string, value: string, onOptionChange(language: string), -}; +} interface SpellCheckLanguagesDropdownIState { searchQuery: string, languages: any, } -export default class SpellCheckLanguagesDropdown extends React.Component { +export default class SpellCheckLanguagesDropdown extends React.Component { constructor(props) { super(props); this._onSearchChange = this._onSearchChange.bind(this); @@ -58,7 +59,7 @@ export default class SpellCheckLanguagesDropdown extends React.Component b) return 1; return 0; }); - var langs = []; + const langs = []; languages.forEach((language) => { langs.push({ label: language, diff --git a/src/components/views/settings/SpellCheckSettings.tsx b/src/components/views/settings/SpellCheckSettings.tsx index 37476d5f34..bfe0774570 100644 --- a/src/components/views/settings/SpellCheckSettings.tsx +++ b/src/components/views/settings/SpellCheckSettings.tsx @@ -22,12 +22,12 @@ import {_t} from "../../../languageHandler"; interface ExistingSpellCheckLanguageIProps { language: string, onRemoved(language: string), -}; +} interface SpellCheckLanguagesIProps { languages: Array, onLanguagesChange(languages: Array), -}; +} interface SpellCheckLanguagesIState { newLanguage: string, @@ -71,7 +71,7 @@ export default class SpellCheckLanguages extends React.Component; }); - let addButton = ( + const addButton = ( {_t("Add")} @@ -99,12 +99,13 @@ export default class SpellCheckLanguages extends React.Component {existingSpellCheckLanguages} - + {addButton}
); - }; + } } diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 6ed887d749..95a8abbb24 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -22,7 +22,7 @@ import ProfileSettings from "../../ProfileSettings"; import * as languageHandler from "../../../../../languageHandler"; import SettingsStore from "../../../../../settings/SettingsStore"; import LanguageDropdown from "../../../elements/LanguageDropdown"; -import SpellCheckSettings from "../../SpellCheckSettings" +import SpellCheckSettings from "../../SpellCheckSettings"; import AccessibleButton from "../../../elements/AccessibleButton"; import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog"; import PropTypes from "prop-types"; @@ -186,8 +186,8 @@ export default class GeneralUserSettingsTab extends React.Component { _onSpellCheckLanguagesChange = (languages) => { SettingsStore.setValue("spell-check-languages", null, SettingLevel.DEVICE, languages); - this.setState({spellCheckLanguages: languages}) - + this.setState({spellCheckLanguages: languages}); + languageHandler.setSpellCheckLanguages(languages); }; From bab541a652e402c1aede8caa00b22b13a2adb0c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 2 Dec 2020 20:14:58 +0100 Subject: [PATCH 024/916] Hide spell-check settings if not using Electron --- src/BasePlatform.ts | 8 ++++++++ .../views/settings/tabs/user/GeneralUserSettingsTab.js | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index 2af2ea51c5..54d15675cb 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -128,6 +128,14 @@ export default abstract class BasePlatform { hideUpdateToast(); } + /** + * Return true if platform supports multi-language + * spell-checking, otherwise false. + */ + supportsMultiLanguageSpellCheck(): boolean { + return false; + } + /** * Returns true if the platform supports displaying * notifications, otherwise false. diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 95a8abbb24..4d1210dc40 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -400,6 +400,9 @@ export default class GeneralUserSettingsTab extends React.Component { } render() { + const plaf = PlatformPeg.get(); + const supportsMultiLanguageSpellCheck = plaf.supportsMultiLanguageSpellCheck() ? true : false; + const discoWarning = this.state.requiredPolicyInfo.hasTerms ? Date: Thu, 3 Dec 2020 11:50:08 +0100 Subject: [PATCH 025/916] Simplifie --- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 4d1210dc40..febbcc8e36 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -401,7 +401,7 @@ export default class GeneralUserSettingsTab extends React.Component { render() { const plaf = PlatformPeg.get(); - const supportsMultiLanguageSpellCheck = plaf.supportsMultiLanguageSpellCheck() ? true : false; + const supportsMultiLanguageSpellCheck = plaf.supportsMultiLanguageSpellCheck(); const discoWarning = this.state.requiredPolicyInfo.hasTerms ? Date: Thu, 3 Dec 2020 11:50:20 +0100 Subject: [PATCH 026/916] Added in if statement --- src/languageHandler.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/languageHandler.tsx b/src/languageHandler.tsx index 38d3c8347a..985719fce7 100644 --- a/src/languageHandler.tsx +++ b/src/languageHandler.tsx @@ -355,7 +355,9 @@ export function setSpellCheckLanguages(preferredLangs: string[]) { export async function getAvailableSpellCheckLanguages(): Promise { const plaf = PlatformPeg.get(); - return plaf.getAvailableSpellCheckLanguages(); + if (plaf) { + return plaf.getAvailableSpellCheckLanguages(); + } } export function getAllLanguagesFromJson() { From 89bc4435945bfb207355cf5e5e290925f7d7f7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 16 Dec 2020 16:02:27 +0100 Subject: [PATCH 027/916] Fix file drop UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 2 +- res/css/structures/_RoomView.scss | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index ad1656efbb..3de68b000d 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -22,7 +22,7 @@ limitations under the License. } .mx_MainSplit > .mx_RightPanel_ResizeWrapper { - padding: 5px; + padding: 0 5px 5px 5px; // margin left to not allow the handle to not encroach on the space for the scrollbar margin-left: 8px; height: calc(100vh - 51px); // height of .mx_RoomHeader.light-panel diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 572c7166d2..0a70b027ae 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -23,24 +23,21 @@ limitations under the License. .mx_RoomView_fileDropTarget { min-width: 0px; width: 100%; + height: 100%; + + margin-left: 6.25px; + font-size: $font-18px; text-align: center; pointer-events: none; - padding-left: 12px; - padding-right: 12px; - margin-left: -12px; - border-top-left-radius: 10px; border-top-right-radius: 10px; background-color: $droptarget-bg-color; - border: 2px #e1dddd solid; - border-bottom: none; + position: absolute; - top: 52px; - bottom: 0px; z-index: 3000; } From 41e2ffdf0df43104ef171b690b344a6e22b286f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 16 Dec 2020 19:51:49 +0100 Subject: [PATCH 028/916] Added background MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 0a70b027ae..9292a400bc 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -39,13 +39,19 @@ limitations under the License. position: absolute; z-index: 3000; + + display: flex; + justify-content: center; + align-items: center; } .mx_RoomView_fileDropTargetLabel { - top: 50%; - width: 100%; - margin-top: -50px; position: absolute; + + border-radius: 10px; + padding: 10px; + + background-color: $menu-bg-color; } .mx_RoomView_auxPanel { From da97d18332c5740499913506b0e059e5b4c7616c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 16 Dec 2020 21:33:05 +0100 Subject: [PATCH 029/916] Added a comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 9292a400bc..dd63be3a11 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -25,6 +25,7 @@ limitations under the License. width: 100%; height: 100%; + // This is an ugly fix for centering this element margin-left: 6.25px; font-size: $font-18px; From dcb30b72b0ed1adc6fb075ee9cc26ca0338177bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 17 Dec 2020 13:25:22 +0100 Subject: [PATCH 030/916] Fix left panel resizer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 32 ++++++++++++++++---------- res/css/structures/_RoomView.scss | 12 ++++++---- src/components/structures/RoomView.tsx | 28 +++++++++++----------- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 3de68b000d..6875ef12e0 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -22,22 +22,30 @@ limitations under the License. } .mx_MainSplit > .mx_RightPanel_ResizeWrapper { - padding: 0 5px 5px 5px; - // margin left to not allow the handle to not encroach on the space for the scrollbar - margin-left: 8px; + padding: 0 5px 5px 0px; height: calc(100vh - 51px); // height of .mx_RoomHeader.light-panel + + .mx_RightPanel_ResizeHandle { + width: 9px; + } &:hover .mx_RightPanel_ResizeHandle { - // Need to use important to override element style attributes - // set by re-resizable - top: 50% !important; - transform: translate(0, -50%); + &::before { + position: absolute; + left: 6px; + top: 50%; + transform: translate(0, -50%); - height: 64px !important; // to match width of the ones on roomlist - width: 4px !important; - border-radius: 4px !important; + height: 64px; + width: 4px; + border-radius: 4px; - background-color: $primary-fg-color; - opacity: 0.8; + content: ' '; + + background-color: $primary-fg-color; + opacity: 0.8; + + margin-left: -10px; + } } } diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index dd63be3a11..0a12a86c33 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -25,9 +25,6 @@ limitations under the License. width: 100%; height: 100%; - // This is an ugly fix for centering this element - margin-left: 6.25px; - font-size: $font-18px; text-align: center; @@ -120,16 +117,23 @@ limitations under the License. height: 50px; } -.mx_RoomView_body { +.mx_RoomView_container { position: relative; //for .mx_RoomView_auxPanel_fullHeight display: flex; flex-direction: column; +} + +.mx_RoomView_body { + display: flex; + flex-direction: column; flex: 1; min-width: 0; .mx_RoomView_messagePanel, .mx_RoomView_messagePanelSpinner, .mx_RoomView_messagePanelSearchSpinner { order: 2; } + + margin-right: 10px; } .mx_RoomView_body .mx_RoomView_timeline { diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 0ee847fbc9..3d62c06e4b 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2003,22 +2003,24 @@ export default class RoomView extends React.Component { appsShown={this.state.showApps} /> -
+
{auxPanel} -
- {topUnreadMessagesBar} - {jumpToBottom} - {messagePanel} - {searchResultsPanel} -
-
-
-
- {statusBar} +
+
+ {topUnreadMessagesBar} + {jumpToBottom} + {messagePanel} + {searchResultsPanel}
+
+
+
+ {statusBar} +
+
+ {previewBar} + {messageComposer}
- {previewBar} - {messageComposer}
From e70dee08d0ea7b303a51fb807929376b2dad79dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 17 Dec 2020 19:50:59 +0100 Subject: [PATCH 031/916] Fix flickering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/RoomView.tsx | 42 ++++++++++++++++++++------ 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 3d62c06e4b..67f9663597 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -187,6 +187,7 @@ export interface IState { rejecting?: boolean; rejectError?: Error; hasPinnedWidgets?: boolean; + dragCounter: number; } export default class RoomView extends React.Component { @@ -237,6 +238,7 @@ export default class RoomView extends React.Component { canReply: false, useIRCLayout: SettingsStore.getValue("useIRCLayout"), matrixClientIsReady: this.context && this.context.isInitialSyncComplete(), + dragCounter: 0, }; this.dispatcherRef = dis.register(this.onAction); @@ -525,8 +527,8 @@ export default class RoomView extends React.Component { if (!roomView.ondrop) { roomView.addEventListener('drop', this.onDrop); roomView.addEventListener('dragover', this.onDragOver); - roomView.addEventListener('dragleave', this.onDragLeaveOrEnd); - roomView.addEventListener('dragend', this.onDragLeaveOrEnd); + roomView.addEventListener('dragenter', this.onDragEnter); + roomView.addEventListener('dragleave', this.onDragLeave); } } @@ -1108,6 +1110,31 @@ export default class RoomView extends React.Component { this.updateTopUnreadMessagesBar(); }; + private onDragEnter = ev => { + ev.stopPropagation(); + ev.preventDefault(); + + this.setState({ + dragCounter: this.state.dragCounter + 1, + draggingFile: true, + }); + }; + + private onDragLeave = ev => { + ev.stopPropagation(); + ev.preventDefault(); + + this.setState({ + dragCounter: this.state.dragCounter - 1, + }); + + if (this.state.dragCounter == 0) { + this.setState({ + draggingFile: false, + }); + } + }; + private onDragOver = ev => { ev.stopPropagation(); ev.preventDefault(); @@ -1115,7 +1142,6 @@ export default class RoomView extends React.Component { ev.dataTransfer.dropEffect = 'none'; if (ev.dataTransfer.types.includes("Files") || ev.dataTransfer.types.includes("application/x-moz-file")) { - this.setState({ draggingFile: true }); ev.dataTransfer.dropEffect = 'copy'; } }; @@ -1126,14 +1152,12 @@ export default class RoomView extends React.Component { ContentMessages.sharedInstance().sendContentListToRoom( ev.dataTransfer.files, this.state.room.roomId, this.context, ); - this.setState({ draggingFile: false }); dis.fire(Action.FocusComposer); - }; - private onDragLeaveOrEnd = ev => { - ev.stopPropagation(); - ev.preventDefault(); - this.setState({ draggingFile: false }); + this.setState({ + draggingFile: false, + dragCounter: this.state.dragCounter - 1, + }); }; private injectSticker(url, info, text) { From 044e02b06ad46b417d3aa8fc33f24c1374fdcb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 17 Dec 2020 20:16:53 +0100 Subject: [PATCH 032/916] Remove spaces in empty line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 6875ef12e0..f05f24d0d7 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -24,7 +24,7 @@ limitations under the License. .mx_MainSplit > .mx_RightPanel_ResizeWrapper { padding: 0 5px 5px 0px; height: calc(100vh - 51px); // height of .mx_RoomHeader.light-panel - + .mx_RightPanel_ResizeHandle { width: 9px; } From 365d252d3f0eb64755f502318c95f855a4404f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 17 Dec 2020 20:25:12 +0100 Subject: [PATCH 033/916] Fix removing event listeners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/RoomView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 67f9663597..d910940a73 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -572,8 +572,8 @@ export default class RoomView extends React.Component { const roomView = this.roomView.current; roomView.removeEventListener('drop', this.onDrop); roomView.removeEventListener('dragover', this.onDragOver); - roomView.removeEventListener('dragleave', this.onDragLeaveOrEnd); - roomView.removeEventListener('dragend', this.onDragLeaveOrEnd); + roomView.removeEventListener('dragenter', this.onDragEnter); + roomView.removeEventListener('dragleave', this.onDragLeave); } dis.unregister(this.dispatcherRef); if (this.context) { From 5d7e45e6cf85e14f4143923f7e29642f97965fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 17 Dec 2020 20:29:33 +0100 Subject: [PATCH 034/916] Added dragCounter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/contexts/RoomContext.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/contexts/RoomContext.ts b/src/contexts/RoomContext.ts index 082dcc4e6b..1b9097e337 100644 --- a/src/contexts/RoomContext.ts +++ b/src/contexts/RoomContext.ts @@ -42,6 +42,7 @@ const RoomContext = createContext({ canReply: false, useIRCLayout: false, matrixClientIsReady: false, + dragCounter: 0, }); RoomContext.displayName = "RoomContext"; export default RoomContext; From 8aabe1f33077ef95f4414c32b3b625b4297369ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 09:26:09 +0100 Subject: [PATCH 035/916] Reorganized elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 114 +++++---------------- src/components/views/elements/ImageView.js | 60 +++++------ 2 files changed, 54 insertions(+), 120 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 0a4ed2a194..77fa597d66 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -22,45 +22,46 @@ limitations under the License. display: flex; width: 100%; height: 100%; - align-items: center; -} - -.mx_ImageView_lhs { - order: 1; - flex: 1 1 10%; - min-width: 60px; - // background-color: #080; - // height: 20px; } .mx_ImageView_content { - order: 2; - /* min-width hack needed for FF */ - min-width: 0px; - height: 90%; - flex: 15 15 0; + width: 100%; + height: 100%; + + display: flex; + flex-direction: column; +} + +.mx_ImageView_imageBox { + overflow: auto; + margin: 0 50px 50px 50px; + flex: 1; display: flex; - align-items: center; - justify-content: center; } .mx_ImageView_content img { - max-width: 100%; + flex: 1; + //max-width: 100%; /* XXX: max-height interacts badly with flex on Chrome and doesn't relayout properly until you refresh */ - max-height: 100%; + //max-height: 100%; /* object-fit hack needed for Chrome due to Chrome not re-laying-out until you refresh */ object-fit: contain; /* background-image: url('$(res)/img/trans.png'); */ pointer-events: all; } -.mx_ImageView_labelWrapper { - position: absolute; - top: 0px; - right: 0px; - height: 100%; - overflow: auto; - pointer-events: all; +.mx_ImageView_panel { + display: flex; + justify-content: space-between; + padding: 50px; +} + +.mx_ImageView_toolbar { + display: flex; +} + +.mx_ImageView_button { + padding: 5px; } .mx_ImageView_label { @@ -68,39 +69,11 @@ limitations under the License. display: flex; justify-content: center; flex-direction: column; - padding-left: 30px; - padding-right: 30px; min-height: 100%; max-width: 240px; color: $lightbox-fg-color; } -.mx_ImageView_cancel { - position: absolute; - // hack for mx_Dialog having a top padding of 40px - top: 40px; - right: 0px; - padding-top: 35px; - padding-right: 35px; - cursor: pointer; -} - -.mx_ImageView_rotateClockwise { - position: absolute; - top: 40px; - right: 70px; - padding-top: 35px; - cursor: pointer; -} - -.mx_ImageView_rotateCounterClockwise { - position: absolute; - top: 40px; - right: 105px; - padding-top: 35px; - cursor: pointer; -} - .mx_ImageView_name { font-size: $font-18px; margin-bottom: 6px; @@ -112,41 +85,6 @@ limitations under the License. opacity: 0.5; } -.mx_ImageView_download { - display: table; - margin-top: 24px; - margin-bottom: 6px; - border-radius: 5px; - background-color: $lightbox-bg-color; - font-size: $font-14px; - padding: 9px; - border: 1px solid $lightbox-border-color; -} - .mx_ImageView_size { font-size: $font-11px; } - -.mx_ImageView_link { - color: $lightbox-fg-color !important; - text-decoration: none !important; -} - -.mx_ImageView_button { - font-size: $font-15px; - opacity: 0.5; - margin-top: 18px; - cursor: pointer; -} - -.mx_ImageView_shim { - height: 30px; -} - -.mx_ImageView_rhs { - order: 3; - flex: 1 1 10%; - min-width: 300px; - // background-color: #800; - // height: 20px; -} diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index e39075cedc..a66d2e2e6f 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -153,7 +153,7 @@ export default class ImageView extends React.Component { let mayRedact = false; const showEventMeta = !!this.props.mxEvent; - let eventMeta; + let metadata; if (showEventMeta) { // Figure out the sender, defaulting to mxid let sender = this.props.mxEvent.getSender(); @@ -165,7 +165,7 @@ export default class ImageView extends React.Component { if (member) sender = member.name; } - eventMeta = (
+ metadata = (
{ _t('Uploaded on %(date)s by %(user)s', { date: formatDate(new Date(this.props.mxEvent.getTs())), user: sender, @@ -173,11 +173,13 @@ export default class ImageView extends React.Component {
); } - let eventRedact; + let redactButton; if (mayRedact) { - eventRedact = (
- { _t('Remove') } -
); + redactButton = ( + + { + + ); } const rotationDegrees = this.state.rotationDegrees; @@ -192,40 +194,34 @@ export default class ImageView extends React.Component { }} className="mx_ImageView" > -
-
- -
+
- - { - - - { - - - { - -
-
{ this.getName() }
- { eventMeta } - -
- { _t('Download this file') }
- { sizeRes } -
+ { metadata } + { sizeRes } +
+
+ + { + + + { + + + { - { eventRedact } -
-
+ { redactButton } + + { +
-
-
+
+ +
); From 2040815f661be9c832a629022f01764ae99c8c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 10:20:15 +0100 Subject: [PATCH 036/916] Implement zooming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 45 +++++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index a66d2e2e6f..0c235d87be 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -46,7 +46,10 @@ export default class ImageView extends React.Component { constructor(props) { super(props); - this.state = { rotationDegrees: 0 }; + this.state = { + rotationDegrees: 0, + zoom: 100, + }; } onKeyDown = (ev) => { @@ -57,6 +60,16 @@ export default class ImageView extends React.Component { } }; + onWheel = (ev) => { + if (ev.ctrlKey) { + ev.stopPropagation(); + ev.preventDefault(); + this.setState({ + zoom: this.state.zoom - ev.deltaY, + }); + } + } + onRedactClick = () => { const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog"); Modal.createTrackedDialog('Confirm Redact Dialog', 'Image View', ConfirmRedactDialog, { @@ -98,6 +111,18 @@ export default class ImageView extends React.Component { this.setState({ rotationDegrees }); }; + zoomIn = () => { + this.setState({ + zoom: this.state.zoom + 10, + }); + }; + + zoomOut = () => { + this.setState({ + zoom: this.state.zoom - 10, + }); + } + render() { /* // In theory max-width: 80%, max-height: 80% on the CSS should work @@ -130,12 +155,13 @@ export default class ImageView extends React.Component { let style = {}; let res; + style = { + width: this.state.zoom + "%", + height: this.state.zoom + "%", + }; + if (this.props.width && this.props.height) { - style = { - width: this.props.width, - height: this.props.height, - }; - res = style.width + "x" + style.height + "px"; + res = this.props.width + "x" + this.props.height + "px"; } let size; @@ -190,6 +216,7 @@ export default class ImageView extends React.Component { returnFocus={true} lockProps={{ onKeyDown: this.onKeyDown, + onWheel: this.onWheel, role: "dialog", }} className="mx_ImageView" @@ -204,6 +231,12 @@ export default class ImageView extends React.Component { { sizeRes }
+ + { + + + { + { From 3c306bc54bb81aea6fa18d3f7204c083a053a0ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 12:54:55 +0100 Subject: [PATCH 037/916] Added icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/download-white.svg | 95 +++++++++++++++++++++ res/img/feather-customised/trash.custom.svg | 87 +++++++++++++++++-- res/img/minus-white.svg | 64 ++++++++++++++ res/img/plus-white.svg | 73 ++++++++++++++++ res/img/trash-red.svg | 89 +++++++++++++++++++ src/components/views/elements/ImageView.js | 8 +- 6 files changed, 406 insertions(+), 10 deletions(-) create mode 100644 res/img/download-white.svg create mode 100644 res/img/minus-white.svg create mode 100644 res/img/plus-white.svg create mode 100644 res/img/trash-red.svg diff --git a/res/img/download-white.svg b/res/img/download-white.svg new file mode 100644 index 0000000000..5c800b350e --- /dev/null +++ b/res/img/download-white.svg @@ -0,0 +1,95 @@ + + + + + + image/svg+xml + + Fill 75 + + + + + + Fill 75 + Created with Sketch. + + + + + + + + + + + + + diff --git a/res/img/feather-customised/trash.custom.svg b/res/img/feather-customised/trash.custom.svg index dc1985ddb2..589bb0a4e5 100644 --- a/res/img/feather-customised/trash.custom.svg +++ b/res/img/feather-customised/trash.custom.svg @@ -1,7 +1,82 @@ - - - - - - + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/res/img/minus-white.svg b/res/img/minus-white.svg new file mode 100644 index 0000000000..2921f34980 --- /dev/null +++ b/res/img/minus-white.svg @@ -0,0 +1,64 @@ + + + + + + image/svg+xml + + Fill 75 + + + + + + Fill 75 + Created with Sketch. + + + diff --git a/res/img/plus-white.svg b/res/img/plus-white.svg new file mode 100644 index 0000000000..7759ace50a --- /dev/null +++ b/res/img/plus-white.svg @@ -0,0 +1,73 @@ + + + + + + image/svg+xml + + Fill 75 + + + + + + Fill 75 + Created with Sketch. + + + + diff --git a/res/img/trash-red.svg b/res/img/trash-red.svg new file mode 100644 index 0000000000..0b1d201d2e --- /dev/null +++ b/res/img/trash-red.svg @@ -0,0 +1,89 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 0c235d87be..719a17d3cb 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -203,7 +203,7 @@ export default class ImageView extends React.Component { if (mayRedact) { redactButton = ( - { + { ); } @@ -232,10 +232,10 @@ export default class ImageView extends React.Component {
- { + { - { + { { @@ -244,7 +244,7 @@ export default class ImageView extends React.Component { { - { + { { redactButton } From b9f480a825f8f8279d807aa2d10973a58fef78b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 13:13:34 +0100 Subject: [PATCH 038/916] Remove flex property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 77fa597d66..5f002c7d7a 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -35,7 +35,6 @@ limitations under the License. .mx_ImageView_imageBox { overflow: auto; margin: 0 50px 50px 50px; - flex: 1; display: flex; } From 2c5f3f31b1bd339c6d40337ddc3bd059fe095edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 13:30:56 +0100 Subject: [PATCH 039/916] Fixed Chromium issues - made listner non-passive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 719a17d3cb..6dfd1f78ad 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -52,6 +52,14 @@ export default class ImageView extends React.Component { }; } + componentDidMount() { + this.focusLock.addEventListener('wheel', this.onWheel, { passive: false }); + } + + componentWillUnmount() { + this.focusLock.removeEventListener('wheel', this.onWheel); + } + onKeyDown = (ev) => { if (ev.key === Key.ESCAPE) { ev.stopPropagation(); @@ -216,10 +224,10 @@ export default class ImageView extends React.Component { returnFocus={true} lockProps={{ onKeyDown: this.onKeyDown, - onWheel: this.onWheel, role: "dialog", }} className="mx_ImageView" + ref={ref => this.focusLock = ref} >
From 633221f012961024d2e706f7b7ee0c1fb3cb88f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 13:50:21 +0100 Subject: [PATCH 040/916] Center image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 5f002c7d7a..e6ed3684bb 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -40,13 +40,9 @@ limitations under the License. .mx_ImageView_content img { flex: 1; - //max-width: 100%; - /* XXX: max-height interacts badly with flex on Chrome and doesn't relayout properly until you refresh */ - //max-height: 100%; - /* object-fit hack needed for Chrome due to Chrome not re-laying-out until you refresh */ object-fit: contain; - /* background-image: url('$(res)/img/trans.png'); */ pointer-events: all; + margin: auto; } .mx_ImageView_panel { From ae25ff82169048be25dcb37c8ec680b22dfb805d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 19 Dec 2020 15:54:26 +0100 Subject: [PATCH 041/916] Switched to scale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 5 +++-- src/components/views/elements/ImageView.js | 9 ++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index e6ed3684bb..c87cfd1ece 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -36,13 +36,14 @@ limitations under the License. overflow: auto; margin: 0 50px 50px 50px; display: flex; + height: 100%; } .mx_ImageView_content img { - flex: 1; object-fit: contain; pointer-events: all; - margin: auto; + //margin: auto; + //margin: 0 auto 0 auto; } .mx_ImageView_panel { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 6dfd1f78ad..a42d957daa 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -160,14 +160,8 @@ export default class ImageView extends React.Component { height: displayHeight }; */ - let style = {}; let res; - style = { - width: this.state.zoom + "%", - height: this.state.zoom + "%", - }; - if (this.props.width && this.props.height) { res = this.props.width + "x" + this.props.height + "px"; } @@ -217,7 +211,8 @@ export default class ImageView extends React.Component { } const rotationDegrees = this.state.rotationDegrees; - const effectiveStyle = {transform: `rotate(${rotationDegrees}deg)`, ...style}; + const zoom = this.state.zoom/100; + const effectiveStyle = {transform: `rotate(${rotationDegrees}deg) scale(${zoom})`}; return ( Date: Sun, 20 Dec 2020 09:51:57 +0100 Subject: [PATCH 042/916] Added a comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index a42d957daa..bc1d8b4d53 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -53,6 +53,8 @@ export default class ImageView extends React.Component { } componentDidMount() { + // We have to use addEventListener() because the listener + // needs to be passive in order to work with Chromium this.focusLock.addEventListener('wheel', this.onWheel, { passive: false }); } From be9b68a4dd3d453548b5e1562019998bd28170de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 10:07:03 +0100 Subject: [PATCH 043/916] Use height and width properties for scaling again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 11 ++++++----- src/components/views/elements/ImageView.js | 7 +++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index c87cfd1ece..1fcf1bf543 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -33,17 +33,18 @@ limitations under the License. } .mx_ImageView_imageBox { - overflow: auto; - margin: 0 50px 50px 50px; + overflow: scroll; display: flex; - height: 100%; + flex-grow: 1; +} + +.mainImage { + margin: auto; } .mx_ImageView_content img { object-fit: contain; pointer-events: all; - //margin: auto; - //margin: 0 auto 0 auto; } .mx_ImageView_panel { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index bc1d8b4d53..2feff97fef 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -163,6 +163,10 @@ export default class ImageView extends React.Component { }; */ let res; + const style = { + height: this.state.zoom + "%", + width: this.state.zoom + "%", + }; if (this.props.width && this.props.height) { res = this.props.width + "x" + this.props.height + "px"; @@ -213,8 +217,7 @@ export default class ImageView extends React.Component { } const rotationDegrees = this.state.rotationDegrees; - const zoom = this.state.zoom/100; - const effectiveStyle = {transform: `rotate(${rotationDegrees}deg) scale(${zoom})`}; + const effectiveStyle = {transform: `rotate(${rotationDegrees}deg)`, ...style}; return ( Date: Sun, 20 Dec 2020 12:16:05 +0100 Subject: [PATCH 044/916] Added some padding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 1fcf1bf543..ec651fcc6a 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -35,6 +35,7 @@ limitations under the License. .mx_ImageView_imageBox { overflow: scroll; display: flex; + padding: 0 50px 50px 50px; flex-grow: 1; } From f9884b1cc75cd7b2cbdb1d15cfa9b9ea364f424c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 17:40:16 +0100 Subject: [PATCH 045/916] Implement translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 6 +- src/components/views/elements/ImageView.js | 144 +++++++++++++-------- 2 files changed, 97 insertions(+), 53 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index ec651fcc6a..10553a1c54 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -33,7 +33,7 @@ limitations under the License. } .mx_ImageView_imageBox { - overflow: scroll; + overflow: hidden; display: flex; padding: 0 50px 50px 50px; flex-grow: 1; @@ -41,6 +41,10 @@ limitations under the License. .mainImage { margin: auto; + + &:hover { + cursor: grab; + } } .mx_ImageView_content img { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 2feff97fef..8cc55e40bf 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -47,11 +47,19 @@ export default class ImageView extends React.Component { constructor(props) { super(props); this.state = { - rotationDegrees: 0, + rotation: 0, zoom: 100, + translationX: 0, + translationY: 0, + moving: false, }; } + initX = 0; + initY = 0; + lastX = 0; + lastY = 0; + componentDidMount() { // We have to use addEventListener() because the listener // needs to be passive in order to work with Chromium @@ -74,8 +82,18 @@ export default class ImageView extends React.Component { if (ev.ctrlKey) { ev.stopPropagation(); ev.preventDefault(); + const newZoom =this.state.zoom - ev.deltaY; + + if (newZoom <= 100) { + this.setState({ + zoom: 100, + translationX: 0, + translationY: 0, + }); + return; + } this.setState({ - zoom: this.state.zoom - ev.deltaY, + zoom: newZoom, }); } } @@ -109,65 +127,72 @@ export default class ImageView extends React.Component { return name; } - rotateCounterClockwise = () => { - const cur = this.state.rotationDegrees; + onRotateCounterClockwiseClick = () => { + const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; - this.setState({ rotationDegrees }); + this.setState({ rotation: rotationDegrees }); }; - rotateClockwise = () => { - const cur = this.state.rotationDegrees; + onRotateClockwiseClick = () => { + const cur = this.state.rotation; const rotationDegrees = (cur + 90) % 360; - this.setState({ rotationDegrees }); + this.setState({ rotation: rotationDegrees }); }; - zoomIn = () => { + onZoomInClick = () => { this.setState({ zoom: this.state.zoom + 10, }); }; - zoomOut = () => { + onZoomOutClick = () => { + if (this.state.zoom <= 100) { + this.setState({ + zoom: 100, + translationX: 0, + translationY: 0, + }); + return; + } this.setState({ zoom: this.state.zoom - 10, }); } + onStartMoving = ev => { + ev.stopPropagation(); + ev.preventDefault(); + + if (this.state.zoom <= 100) return false; + + this.setState({moving: true}); + this.initX = ev.pageX - this.lastX; + this.initY = ev.pageY - this.lastY; + } + + onMoving = ev => { + ev.stopPropagation(); + ev.preventDefault(); + + if (!this.state.moving) return false; + + this.lastX = ev.pageX - this.initX; + this.lastY = ev.pageY - this.initY; + this.setState({ + translationX: this.lastX, + translationY: this.lastY, + }); + } + + onEndMoving = ev => { + this.setState({moving: false}); + } + render() { -/* - // In theory max-width: 80%, max-height: 80% on the CSS should work - // but in practice, it doesn't, so do it manually: + let mayRedact = false; + const showEventMeta = !!this.props.mxEvent; - var width = this.props.width || 500; - var height = this.props.height || 500; - - var maxWidth = document.documentElement.clientWidth * 0.8; - var maxHeight = document.documentElement.clientHeight * 0.8; - - var widthFrac = width / maxWidth; - var heightFrac = height / maxHeight; - - var displayWidth; - var displayHeight; - if (widthFrac > heightFrac) { - displayWidth = Math.min(width, maxWidth); - displayHeight = (displayWidth / width) * height; - } else { - displayHeight = Math.min(height, maxHeight); - displayWidth = (displayHeight / height) * width; - } - - var style = { - width: displayWidth, - height: displayHeight - }; -*/ let res; - const style = { - height: this.state.zoom + "%", - width: this.state.zoom + "%", - }; - if (this.props.width && this.props.height) { res = this.props.width + "x" + this.props.height + "px"; } @@ -184,9 +209,6 @@ export default class ImageView extends React.Component { sizeRes = size || res; } - let mayRedact = false; - const showEventMeta = !!this.props.mxEvent; - let metadata; if (showEventMeta) { // Figure out the sender, defaulting to mxid @@ -216,8 +238,16 @@ export default class ImageView extends React.Component { ); } - const rotationDegrees = this.state.rotationDegrees; - const effectiveStyle = {transform: `rotate(${rotationDegrees}deg)`, ...style}; + const rotationDegrees = this.state.rotation + "deg"; + const zoomPercentage = this.state.zoom/100; + const translatePixelsX = this.state.translationX + "px"; + const translatePixelsY = this.state.translationY + "px"; + const style = { + transform: `rotate(${rotationDegrees}) + scale(${zoomPercentage}) + translateX(${translatePixelsX}) + translateY(${translatePixelsY})`, + }; return ( { sizeRes }
- +
From 7dd7aeffedc612827712a0b1ff91a3561f56f016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 18:19:11 +0100 Subject: [PATCH 046/916] Remove imageBox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 10 +++------ src/components/views/elements/ImageView.js | 24 ++++++++++------------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 10553a1c54..a2ca9c7927 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -26,21 +26,15 @@ limitations under the License. .mx_ImageView_content { width: 100%; - height: 100%; display: flex; flex-direction: column; -} -.mx_ImageView_imageBox { overflow: hidden; - display: flex; - padding: 0 50px 50px 50px; - flex-grow: 1; } .mainImage { - margin: auto; + //margin: auto; &:hover { cursor: grab; @@ -56,6 +50,8 @@ limitations under the License. display: flex; justify-content: space-between; padding: 50px; + position: absolute; + width: 100%; } .mx_ImageView_toolbar { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 8cc55e40bf..9d747463f9 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -290,19 +290,17 @@ export default class ImageView extends React.Component {
-
- -
+
); From 6758734593ad30e9a766e63ccb23921b43c00c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 19:48:24 +0100 Subject: [PATCH 047/916] Remove panel element MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 39 +++++++++------- src/components/views/elements/ImageView.js | 54 +++++++++++----------- 2 files changed, 48 insertions(+), 45 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index a2ca9c7927..0153d372fc 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -28,50 +28,55 @@ limitations under the License. width: 100%; display: flex; - flex-direction: column; + justify-content: center; + align-items: center; overflow: hidden; } .mainImage { - //margin: auto; + object-fit: contain; + pointer-events: all; + + max-width: 100vw; + max-height: 90vh; + min-width: 100px; + min-height: 100px; &:hover { cursor: grab; } } -.mx_ImageView_content img { - object-fit: contain; +.mx_ImageView_panel { + position: absolute; + z-index: 1000; + align-self: flex-start; pointer-events: all; } -.mx_ImageView_panel { - display: flex; - justify-content: space-between; - padding: 50px; - position: absolute; - width: 100%; -} - .mx_ImageView_toolbar { + right: 0; + padding: 50px 50px 0 0; display: flex; -} - -.mx_ImageView_button { - padding: 5px; + } .mx_ImageView_label { + left: 0; + padding: 50px 0 0 50px; text-align: left; display: flex; justify-content: center; flex-direction: column; - min-height: 100%; max-width: 240px; color: $lightbox-fg-color; } +.mx_ImageView_button { + padding: 5px; +} + .mx_ImageView_name { font-size: $font-18px; margin-bottom: 6px; diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 9d747463f9..86c6cb8ad5 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -260,35 +260,33 @@ export default class ImageView extends React.Component { ref={ref => this.focusLock = ref} >
-
-
-
- { this.getName() } -
- { metadata } - { sizeRes } -
-
- - { - - - { - - - { - - - { - - - { - - { redactButton } - - { - +
+
+ { this.getName() }
+ { metadata } + { sizeRes } +
+
+ + { + + + { + + + { + + + { + + + { + + { redactButton } + + { +
Date: Sun, 20 Dec 2020 20:00:11 +0100 Subject: [PATCH 048/916] Fixed translation issue while the image is rotated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 86c6cb8ad5..0db907003c 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -242,11 +242,15 @@ export default class ImageView extends React.Component { const zoomPercentage = this.state.zoom/100; const translatePixelsX = this.state.translationX + "px"; const translatePixelsY = this.state.translationY + "px"; + /* The order of the values is important! + * First, we translate and only then we rotate, otherwise + * we would apply the translation to an already rotated + * image causing it translate in the wrong direction. */ const style = { - transform: `rotate(${rotationDegrees}) + transform: `translateX(${translatePixelsX}) + translateY(${translatePixelsY}) scale(${zoomPercentage}) - translateX(${translatePixelsX}) - translateY(${translatePixelsY})`, + rotate(${rotationDegrees})`, }; return ( From 776b0e8198e1179ee5f1b7ee9edb6e07d6c14ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:05:27 +0100 Subject: [PATCH 049/916] Change comment styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 0db907003c..b6fa9ef35b 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -61,8 +61,8 @@ export default class ImageView extends React.Component { lastY = 0; componentDidMount() { - // We have to use addEventListener() because the listener - // needs to be passive in order to work with Chromium + /* We have to use addEventListener() because the listener + * needs to be passive in order to work with Chromium */ this.focusLock.addEventListener('wheel', this.onWheel, { passive: false }); } From f0abd52130b6f70df4bef144d26afdda05819bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:09:01 +0100 Subject: [PATCH 050/916] Remove the need to press ctrl while zooming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index b6fa9ef35b..d40708af4c 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -79,23 +79,21 @@ export default class ImageView extends React.Component { }; onWheel = (ev) => { - if (ev.ctrlKey) { - ev.stopPropagation(); - ev.preventDefault(); - const newZoom =this.state.zoom - ev.deltaY; + ev.stopPropagation(); + ev.preventDefault(); + const newZoom =this.state.zoom - ev.deltaY; - if (newZoom <= 100) { - this.setState({ - zoom: 100, - translationX: 0, - translationY: 0, - }); - return; - } + if (newZoom <= 100) { this.setState({ - zoom: newZoom, + zoom: 100, + translationX: 0, + translationY: 0, }); + return; } + this.setState({ + zoom: newZoom, + }); } onRedactClick = () => { From cbfa6c5f94bc839ce158ee994a2efda81d024508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:15:25 +0100 Subject: [PATCH 051/916] Fix some sizing issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 0153d372fc..2c3d881394 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -39,7 +39,7 @@ limitations under the License. pointer-events: all; max-width: 100vw; - max-height: 90vh; + max-height: 80vh; min-width: 100px; min-height: 100px; From 61c5e7e8f17356c849fda70793cd6e5c86f4342c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:15:39 +0100 Subject: [PATCH 052/916] Reorder label items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index d40708af4c..b343ec37c2 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -266,8 +266,8 @@ export default class ImageView extends React.Component {
{ this.getName() }
- { metadata } { sizeRes } + { metadata }
From 0dff150bb271985133b551c0b99639fc68461cfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:18:21 +0100 Subject: [PATCH 053/916] Fix some more sizing issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 2c3d881394..1b38021267 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -39,7 +39,7 @@ limitations under the License. pointer-events: all; max-width: 100vw; - max-height: 80vh; + max-height: 70vh; min-width: 100px; min-height: 100px; From 096fb33397e29e65cf0ec5dea77eb99b8b8c690e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:19:32 +0100 Subject: [PATCH 054/916] Always allow moving MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index b343ec37c2..12112db48a 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -161,8 +161,6 @@ export default class ImageView extends React.Component { ev.stopPropagation(); ev.preventDefault(); - if (this.state.zoom <= 100) return false; - this.setState({moving: true}); this.initX = ev.pageX - this.lastX; this.initY = ev.pageY - this.lastY; From 3d62138cbd67fa7beefa9770abf954a35c8286f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:28:19 +0100 Subject: [PATCH 055/916] Set max zoom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 12112db48a..82f3386e8d 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -91,6 +91,11 @@ export default class ImageView extends React.Component { }); return; } + if (newZoom >= 300) { + this.setState({zoom: 300}); + return; + } + this.setState({ zoom: newZoom, }); @@ -138,6 +143,11 @@ export default class ImageView extends React.Component { }; onZoomInClick = () => { + if (this.state.zoom >= 300) { + this.setState({zoom: 300}); + return; + } + this.setState({ zoom: this.state.zoom + 10, }); From f771b7ac98764135691f64fbddefded6cdb06621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:37:31 +0100 Subject: [PATCH 056/916] Added zoom button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/zoom-white.svg | 59 ++++++++++++++++++++++ src/components/views/elements/ImageView.js | 33 +++++++++--- 2 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 res/img/zoom-white.svg diff --git a/res/img/zoom-white.svg b/res/img/zoom-white.svg new file mode 100644 index 0000000000..19379cb881 --- /dev/null +++ b/res/img/zoom-white.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 82f3386e8d..8552b2e381 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -59,6 +59,8 @@ export default class ImageView extends React.Component { initY = 0; lastX = 0; lastY = 0; + minZoom = 100; + maxZoom = 300; componentDidMount() { /* We have to use addEventListener() because the listener @@ -83,16 +85,16 @@ export default class ImageView extends React.Component { ev.preventDefault(); const newZoom =this.state.zoom - ev.deltaY; - if (newZoom <= 100) { + if (newZoom <= this.minZoom) { this.setState({ - zoom: 100, + zoom: this.minZoom, translationX: 0, translationY: 0, }); return; } - if (newZoom >= 300) { - this.setState({zoom: 300}); + if (newZoom >= this.maxZoom) { + this.setState({zoom: this.maxZoom}); return; } @@ -143,8 +145,8 @@ export default class ImageView extends React.Component { }; onZoomInClick = () => { - if (this.state.zoom >= 300) { - this.setState({zoom: 300}); + if (this.state.zoom >= this.maxZoom) { + this.setState({zoom: this.maxZoom}); return; } @@ -154,9 +156,9 @@ export default class ImageView extends React.Component { }; onZoomOutClick = () => { - if (this.state.zoom <= 100) { + if (this.state.zoom <= this.minZoom) { this.setState({ - zoom: 100, + zoom: this.minZoom, translationX: 0, translationY: 0, }); @@ -167,6 +169,18 @@ export default class ImageView extends React.Component { }); } + onZoomClick = () => { + if (this.state.zoom <= this.minZoom) { + this.setState({zoom: this.maxZoom}); + } else { + this.setState({ + zoom: this.minZoom, + translationX: 0, + translationY: 0, + }); + } + } + onStartMoving = ev => { ev.stopPropagation(); ev.preventDefault(); @@ -278,6 +292,9 @@ export default class ImageView extends React.Component { { metadata }
+ + { + { From 6315c8ecefae446a11eb9b031e35b524a1bfa9d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:45:33 +0100 Subject: [PATCH 057/916] Fix formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 1b38021267..43333a25e6 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -59,7 +59,6 @@ limitations under the License. right: 0; padding: 50px 50px 0 0; display: flex; - } .mx_ImageView_label { From 00bc97b63fde4d1605b8820422d98fa73680e889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 20 Dec 2020 20:45:47 +0100 Subject: [PATCH 058/916] i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/i18n/strings/en_EN.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f8ef44763d..cd4285753f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1833,11 +1833,14 @@ "expand": "expand", "You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)", "Uploaded on %(date)s by %(user)s": "Uploaded on %(date)s by %(user)s", + "Zoom": "Zoom", + "Zoom in": "Zoom in", + "Zoom out": "Zoom out", "Rotate Left": "Rotate Left", "Rotate counter-clockwise": "Rotate counter-clockwise", "Rotate Right": "Rotate Right", "Rotate clockwise": "Rotate clockwise", - "Download this file": "Download this file", + "Download": "Download", "Information": "Information", "Language Dropdown": "Language Dropdown", "%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s", @@ -2593,7 +2596,6 @@ "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.": "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.", "Keep a copy of it somewhere secure, like a password manager or even a safe.": "Keep a copy of it somewhere secure, like a password manager or even a safe.", "Your recovery key": "Your recovery key", - "Download": "Download", "Your recovery key has been copied to your clipboard, paste it to:": "Your recovery key has been copied to your clipboard, paste it to:", "Your recovery key is in your Downloads folder.": "Your recovery key is in your Downloads folder.", "Print it and store it somewhere safe": "Print it and store it somewhere safe", From 78b3f50bfd4bbe3de053d7b7d994ad779e3002a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20M=C3=A4der?= Date: Sun, 20 Dec 2020 23:14:56 +0100 Subject: [PATCH 059/916] Use LaTeX delimiters by default, add /tex command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since parsing for $'s as maths delimiters is tricky, switch the default to \(...\) for inline and \[...\] for display maths as it is used in LaTeX. Add /tex command to explicitly parse in TeX mode, which uses $...$ for inline and $$...$$ for display maths. Signed-off-by: Sven Mäder --- src/SlashCommands.tsx | 18 +++++++ src/editor/deserialize.ts | 8 +-- src/editor/serialize.ts | 98 ++++++++++++++++++++++++++++--------- src/i18n/strings/en_EN.json | 1 + 4 files changed, 99 insertions(+), 26 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 79c21c4af5..e9bde933ec 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -48,6 +48,7 @@ import SettingsStore from "./settings/SettingsStore"; import {UIFeature} from "./settings/UIFeature"; import {CHAT_EFFECTS} from "./effects" import CallHandler from "./CallHandler"; +import {markdownSerializeIfNeeded} from './editor/serialize'; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -223,6 +224,23 @@ export const Commands = [ }, category: CommandCategories.messages, }), + new Command({ + command: 'tex', + args: '', + description: _td('Sends a message in TeX mode, using $ and $$ delimiters for maths'), + runFn: function(roomId, args) { + if (SettingsStore.getValue("feature_latex_maths")) { + if (args) { + let html = markdownSerializeIfNeeded(args, {forceHTML: false}, {forceTEX: true}); + return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, html)); + } + return reject(this.getUsage()); + } else { + return reject("Render LaTeX maths in messages needs to be enabled in Labs"); + } + }, + category: CommandCategories.messages, + }), new Command({ command: 'ddg', args: '', diff --git a/src/editor/deserialize.ts b/src/editor/deserialize.ts index 6336b4c46b..a1ee079af5 100644 --- a/src/editor/deserialize.ts +++ b/src/editor/deserialize.ts @@ -136,11 +136,11 @@ function parseElement(n: HTMLElement, partCreator: PartCreator, lastNode: HTMLEl // math nodes are translated back into delimited latex strings if (n.hasAttribute("data-mx-maths")) { const delimLeft = (n.nodeName == "SPAN") ? - (SdkConfig.get()['latex_maths_delims'] || {})['inline_left'] || "$" : - (SdkConfig.get()['latex_maths_delims'] || {})['display_left'] || "$$"; + (SdkConfig.get()['latex_maths_delims'] || {})['inline_left'] || "\\(" : + (SdkConfig.get()['latex_maths_delims'] || {})['display_left'] || "\\["; const delimRight = (n.nodeName == "SPAN") ? - (SdkConfig.get()['latex_maths_delims'] || {})['inline_right'] || "$" : - (SdkConfig.get()['latex_maths_delims'] || {})['display_right'] || "$$"; + (SdkConfig.get()['latex_maths_delims'] || {})['inline_right'] || "\\)" : + (SdkConfig.get()['latex_maths_delims'] || {})['display_right'] || "\\]"; const tex = n.getAttribute("data-mx-maths"); return partCreator.plain(delimLeft + tex + delimRight); } else if (!checkDescendInto(n)) { diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index c1f4da306b..ca798a324e 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -41,24 +41,57 @@ export function mdSerialize(model: EditorModel) { }, ""); } -export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { - let md = mdSerialize(model); +export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, {forceTEX = false} = {}) { + // copy of raw input to remove unwanted math later + const orig = md; if (SettingsStore.getValue("feature_latex_maths")) { - const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || - "\\$\\$(([^$]|\\\\\\$)*)\\$\\$"; - const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || - "\\$(([^$]|\\\\\\$)*)\\$"; + if (forceTEX) { + // detect math with tex delimiters, inline: $...$, display $$...$$ + // preferably use negative lookbehinds, not supported in all major browsers: + // const displayPattern = "^(?\n\n
\n\n`; - }); + // conditions for display math detection ($$...$$): + // - left delimiter ($$) is not escaped by a backslash + // - pattern starts at the beginning of a line + // - left delimiter is not followed by a space or tab character + // - pattern ends at the end of a line + const displayPattern = "^(?!\\\\)\\$\\$(?![ \\t])(([^$]|\\\\\\$)+?)\\$\\$$"; - md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1) { - const p1e = AllHtmlEntities.encode(p1); - return ``; - }); + // conditions for inline math detection ($...$): + // - left and right delimiters ($) are not escaped by backslashes + // - pattern starts at the beginning of a line or follows a whitespace character + // - left delimiter is not followed by a whitespace character + // - right delimiter is not preseeded by a whitespace character + const inlinePattern = "(^|\\s)(?!\\\\)\\$(?!\\s)(([^$]|\\\\\\$)*[^\\\\\\s\\$](?:\\\\\\$)?)\\$"; + + md = md.replace(RegExp(displayPattern, "gm"), function(m, p1) { + const p1e = AllHtmlEntities.encode(p1); + return `
\n\n
\n\n`; + }); + + md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}`; + }); + } else { + // detect math with latex delimiters, inline: \(...\), display \[...\] + const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || + "^\\\\\\[(.*?)\\\\\\]$"; + const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || + "(^|\\s)\\\\\\((.*?)\\\\\\)"; + + md = md.replace(RegExp(displayPattern, "gms"), function(m, p1) { + const p1e = AllHtmlEntities.encode(p1); + return `
\n\n
\n\n`; + }); + + md = md.replace(RegExp(inlinePattern, "gms"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}`; + }); + } // make sure div tags always start on a new line, otherwise it will confuse // the markdown parser @@ -69,15 +102,30 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = if (!parser.isPlainText() || forceHTML) { // feed Markdown output to HTML parser const phtml = cheerio.load(parser.toHTML(), - { _useHtmlParser2: true, decodeEntities: false }) + { _useHtmlParser2: true, decodeEntities: false }); - // add fallback output for latex math, which should not be interpreted as markdown - phtml('div, span').each(function(i, e) { - const tex = phtml(e).attr('data-mx-maths') - if (tex) { - phtml(e).html(`${tex}`) - } - }); + if (SettingsStore.getValue("feature_latex_maths")) { + // original Markdown without LaTeX replacements + const parserOrig = new Markdown(orig); + const phtmlOrig = cheerio.load(parserOrig.toHTML(), + { _useHtmlParser2: true, decodeEntities: false }); + + // since maths delimiters are handled before Markdown, + // code blocks could contain mangled content. + // replace code blocks with original content + phtml('code').contents('div, span').each(function(i) { + const origData = phtmlOrig('code').contents('div, span')[i].data; + phtml('code').contents('div, span')[i].data = origData; + }); + + // add fallback output for latex math, which should not be interpreted as markdown + phtml('div, span').each(function(i, e) { + const tex = phtml(e).attr('data-mx-maths') + if (tex) { + phtml(e).html(`${tex}`) + } + }); + } return phtml.html(); } // ensure removal of escape backslashes in non-Markdown messages @@ -86,6 +134,12 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = } } +export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { + let md = mdSerialize(model); + + return markdownSerializeIfNeeded(md, {forceHTML: forceHTML}); +} + export function textSerialize(model: EditorModel) { return model.parts.reduce((text, part) => { switch (part.type) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 2fb70ecdb1..bf6c61414a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -416,6 +416,7 @@ "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message", "Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown", "Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown", + "Sends a message in TeX mode, using $ and $$ delimiters for maths": "Sends a message in TeX mode, using $ and $$ delimiters for maths", "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", "/ddg is not a command": "/ddg is not a command", "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", From 997e08f21e32d61f164cfefb4c397fc48d19de71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 21 Dec 2020 10:07:46 +0100 Subject: [PATCH 060/916] Reorganize buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 8552b2e381..4e38b4b4eb 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -292,15 +292,16 @@ export default class ImageView extends React.Component { { metadata }
+ { redactButton } { - - { - { + + { + { @@ -310,7 +311,6 @@ export default class ImageView extends React.Component { { - { redactButton } { From ad5844fcc0b9e585e0f0f5766a33a9e7f716d43e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 21 Dec 2020 20:33:03 +0100 Subject: [PATCH 061/916] Fix i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 32c3a61f8a..a3345caac5 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1839,8 +1839,8 @@ "You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)", "Uploaded on %(date)s by %(user)s": "Uploaded on %(date)s by %(user)s", "Zoom": "Zoom", - "Zoom in": "Zoom in", "Zoom out": "Zoom out", + "Zoom in": "Zoom in", "Rotate Left": "Rotate Left", "Rotate counter-clockwise": "Rotate counter-clockwise", "Rotate Right": "Rotate Right", From fb57123e25966912277ac65927304b088b9e7db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20M=C3=A4der?= Date: Tue, 22 Dec 2020 12:18:38 +0100 Subject: [PATCH 062/916] Improve inline latex regex matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sven Mäder --- src/SlashCommands.tsx | 2 +- src/editor/serialize.ts | 29 +++++++++++++++++++---------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index e9bde933ec..bf190fc450 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -231,7 +231,7 @@ export const Commands = [ runFn: function(roomId, args) { if (SettingsStore.getValue("feature_latex_maths")) { if (args) { - let html = markdownSerializeIfNeeded(args, {forceHTML: false}, {forceTEX: true}); + const html = markdownSerializeIfNeeded(args, {forceHTML: false}, {forceTEX: true}); return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, html)); } return reject(this.getUsage()); diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index ca798a324e..4ef722e334 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -52,18 +52,18 @@ export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, // const displayPattern = "^(? Date: Tue, 22 Dec 2020 13:31:58 +0100 Subject: [PATCH 063/916] Fix linting error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sven Mäder --- src/editor/serialize.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 4ef722e334..3aafa70fe8 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -144,7 +144,7 @@ export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, } export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { - let md = mdSerialize(model); + const md = mdSerialize(model); return markdownSerializeIfNeeded(md, {forceHTML: forceHTML}); } From 5de92b68d954ba3f997f2d5713d954ee05303b2e Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 27 Jan 2021 11:39:57 +0000 Subject: [PATCH 064/916] Show a specific error for hs_disabled --- src/components/structures/LoggedInView.tsx | 2 +- src/components/structures/RoomStatusBar.js | 4 ++++ src/components/structures/auth/Login.tsx | 3 +++ src/components/structures/auth/Registration.tsx | 1 + src/toasts/ServerLimitToast.tsx | 1 + src/utils/ErrorUtils.js | 1 + 6 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 70ec2b7033..508b7f05e7 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -94,7 +94,7 @@ interface IProps { interface IUsageLimit { // eslint-disable-next-line camelcase - limit_type: "monthly_active_user" | string; + limit_type: "monthly_active_user" | "hs_disabled" | string; // eslint-disable-next-line camelcase admin_contact?: string; } diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index c1c4ad6292..aa4bceba74 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -195,6 +195,10 @@ export default class RoomStatusBar extends React.Component { "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. " + "Please contact your service administrator to continue using the service.", ), + 'hs_disabled': _td( + "Your message wasn't sent because this homeserver has been blocked by it's administrator. " + + "Please contact your service administrator to continue using the service.", + ), '': _td( "Your message wasn't sent because this homeserver has exceeded a resource limit. " + "Please contact your service administrator to continue using the service.", diff --git a/src/components/structures/auth/Login.tsx b/src/components/structures/auth/Login.tsx index 606aeb44ab..a9fd363763 100644 --- a/src/components/structures/auth/Login.tsx +++ b/src/components/structures/auth/Login.tsx @@ -218,6 +218,9 @@ export default class LoginComponent extends React.PureComponent 'monthly_active_user': _td( "This homeserver has hit its Monthly Active User limit.", ), + 'hs_blocked': _td( + "This homeserver has been blocked by it's administrator.", + ), '': _td( "This homeserver has exceeded one of its resource limits.", ), diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index 095f3d3433..f9d338902c 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -276,6 +276,7 @@ export default class Registration extends React.Component { response.data.admin_contact, { 'monthly_active_user': _td("This homeserver has hit its Monthly Active User limit."), + 'hs_blocked': _td("This homeserver has been blocked by it's administrator."), '': _td("This homeserver has exceeded one of its resource limits."), }, ); diff --git a/src/toasts/ServerLimitToast.tsx b/src/toasts/ServerLimitToast.tsx index d35140be3d..9dbe8c05f1 100644 --- a/src/toasts/ServerLimitToast.tsx +++ b/src/toasts/ServerLimitToast.tsx @@ -26,6 +26,7 @@ const TOAST_KEY = "serverlimit"; export const showToast = (limitType: string, adminContact?: string, syncError?: boolean) => { const errorText = messageForResourceLimitError(limitType, adminContact, { 'monthly_active_user': _td("Your homeserver has exceeded its user limit."), + 'hs_blocked': _td("This homeserver has been blocked by it's administrator."), '': _td("Your homeserver has exceeded one of its resource limits."), }); const contactText = messageForResourceLimitError(limitType, adminContact, { diff --git a/src/utils/ErrorUtils.js b/src/utils/ErrorUtils.js index f0a4d7c49e..2c6acd5503 100644 --- a/src/utils/ErrorUtils.js +++ b/src/utils/ErrorUtils.js @@ -62,6 +62,7 @@ export function messageForSyncError(err) { err.data.admin_contact, { 'monthly_active_user': _td("This homeserver has hit its Monthly Active User limit."), + 'hs_blocked': _td("This homeserver has been blocked by its administrator."), '': _td("This homeserver has exceeded one of its resource limits."), }, ); From 27724a93d28d7945e49f94b5fa1158095bd84d8d Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 27 Jan 2021 11:42:36 +0000 Subject: [PATCH 065/916] new strings --- src/i18n/strings/en_EN.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8d047ea3f1..e55ab581ca 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -650,6 +650,7 @@ "Unexpected error resolving identity server configuration": "Unexpected error resolving identity server configuration", "The message you are trying to send is too large.": "The message you are trying to send is too large.", "This homeserver has hit its Monthly Active User limit.": "This homeserver has hit its Monthly Active User limit.", + "This homeserver has been blocked by its administrator.": "This homeserver has been blocked by its administrator.", "This homeserver has exceeded one of its resource limits.": "This homeserver has exceeded one of its resource limits.", "Please contact your service administrator to continue using the service.": "Please contact your service administrator to continue using the service.", "Unable to connect to Homeserver. Retrying...": "Unable to connect to Homeserver. Retrying...", @@ -727,6 +728,7 @@ "Enable desktop notifications": "Enable desktop notifications", "Enable": "Enable", "Your homeserver has exceeded its user limit.": "Your homeserver has exceeded its user limit.", + "This homeserver has been blocked by it's administrator.": "This homeserver has been blocked by it's administrator.", "Your homeserver has exceeded one of its resource limits.": "Your homeserver has exceeded one of its resource limits.", "Contact your server admin.": "Contact your server admin.", "Warning": "Warning", @@ -2471,6 +2473,7 @@ "Filter rooms and people": "Filter rooms and people", "You can't send any messages until you review and agree to our terms and conditions.": "You can't send any messages until you review and agree to our terms and conditions.", "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.": "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.", + "Your message wasn't sent because this homeserver has been blocked by it's administrator. Please contact your service administrator to continue using the service.": "Your message wasn't sent because this homeserver has been blocked by it's administrator. Please contact your service administrator to continue using the service.", "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.": "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.", "%(count)s of your messages have not been sent.|other": "Some of your messages have not been sent.", "%(count)s of your messages have not been sent.|one": "Your message was not sent.", From c4f0987487c58ed88dacc62ff155dfa140729fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20M=C3=A4der?= Date: Fri, 29 Jan 2021 00:11:06 +0100 Subject: [PATCH 066/916] Use TeX and LaTeX delimiters by default for serialize --- src/SlashCommands.tsx | 2 +- src/editor/serialize.ts | 71 +++++++++++++++++++++---------------- src/i18n/strings/en_EN.json | 2 +- 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index bf190fc450..387b9991db 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -227,7 +227,7 @@ export const Commands = [ new Command({ command: 'tex', args: '', - description: _td('Sends a message in TeX mode, using $ and $$ delimiters for maths'), + description: _td('Sends a message in TeX mode, without restrictions'), runFn: function(roomId, args) { if (SettingsStore.getValue("feature_latex_maths")) { if (args) { diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 3aafa70fe8..61d7878de5 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -47,28 +47,12 @@ export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, if (SettingsStore.getValue("feature_latex_maths")) { if (forceTEX) { - // detect math with tex delimiters, inline: $...$, display $$...$$ - // preferably use negative lookbehinds, not supported in all major browsers: - // const displayPattern = "^(?\n\n
\n\n`; + md = md.replace(RegExp(displayPattern, "gm"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}
\n\n
\n\n`; }); md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1, p2) { @@ -76,24 +60,51 @@ export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, return `${p1}`; }); } else { + // detect math with tex delimiters, inline: $...$, display $$...$$ + // preferably use negative lookbehinds, not supported in all major browsers: + // const displayPattern = "^(?\n\n
\n\n`; + }); + + md = md.replace(RegExp(inlinePatternDollar, "gm"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}`; + }); + // detect math with latex delimiters, inline: \(...\), display \[...\] // conditions for display math detection \[...\]: - // - pattern starts at beginning of line - // - pattern ends at end of line + // - pattern starts at beginning of line or is not prefixed with backslash + // - pattern is not empty const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || - "^\\\\\\[(.*?)\\\\\\]$"; + "(^|[^\\\\])\\\\\\[(?!\\\\\\])(.*?)\\\\\\]"; // conditions for inline math detection \(...\): // - pattern starts at beginning of line or is not prefixed with backslash - // (this allows escaping and requires that multiple consecutive - // patterns are separated by at least one character) + // - pattern is not empty const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || - "(^|[^\\\\])\\\\\\((.*?)\\\\\\)"; + "(^|[^\\\\])\\\\\\((?!\\\\\\))(.*?)\\\\\\)"; - md = md.replace(RegExp(displayPattern, "gms"), function(m, p1) { - const p1e = AllHtmlEntities.encode(p1); - return `
\n\n
\n\n`; + md = md.replace(RegExp(displayPattern, "gms"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}
\n\n
\n\n`; }); md = md.replace(RegExp(inlinePattern, "gms"), function(m, p1, p2) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bf6c61414a..bcae343550 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -416,7 +416,7 @@ "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message", "Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown", "Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown", - "Sends a message in TeX mode, using $ and $$ delimiters for maths": "Sends a message in TeX mode, using $ and $$ delimiters for maths", + "Sends a message in TeX mode, without restrictions": "Sends a message in TeX mode, without restrictions", "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", "/ddg is not a command": "/ddg is not a command", "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", From bcc069771061b7559313ded1f453f91eaf0f283f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20M=C3=A4der?= Date: Fri, 29 Jan 2021 13:05:49 +0100 Subject: [PATCH 067/916] Remove /tex command --- src/SlashCommands.tsx | 18 ------ src/editor/serialize.ts | 108 +++++++++++++++--------------------- src/i18n/strings/en_EN.json | 1 - 3 files changed, 44 insertions(+), 83 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 387b9991db..79c21c4af5 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -48,7 +48,6 @@ import SettingsStore from "./settings/SettingsStore"; import {UIFeature} from "./settings/UIFeature"; import {CHAT_EFFECTS} from "./effects" import CallHandler from "./CallHandler"; -import {markdownSerializeIfNeeded} from './editor/serialize'; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -224,23 +223,6 @@ export const Commands = [ }, category: CommandCategories.messages, }), - new Command({ - command: 'tex', - args: '', - description: _td('Sends a message in TeX mode, without restrictions'), - runFn: function(roomId, args) { - if (SettingsStore.getValue("feature_latex_maths")) { - if (args) { - const html = markdownSerializeIfNeeded(args, {forceHTML: false}, {forceTEX: true}); - return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, html)); - } - return reject(this.getUsage()); - } else { - return reject("Render LaTeX maths in messages needs to be enabled in Labs"); - } - }, - category: CommandCategories.messages, - }), new Command({ command: 'ddg', args: '', diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 61d7878de5..6b1d97a0ef 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -41,77 +41,63 @@ export function mdSerialize(model: EditorModel) { }, ""); } -export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, {forceTEX = false} = {}) { +export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { + let md = mdSerialize(model); // copy of raw input to remove unwanted math later const orig = md; if (SettingsStore.getValue("feature_latex_maths")) { - if (forceTEX) { - const displayPattern = "(^|[^\\\\$])\\$\\$(([^$]|\\\\\\$)+?)\\$\\$"; - const inlinePattern = "(^|[^\\\\$])\\$(([^$]|\\\\\\$)*([^\\\\\\$]|\\\\\\$))\\$"; + // detect math with tex delimiters, inline: $...$, display $$...$$ + // preferably use negative lookbehinds, not supported in all major browsers: + // const displayPattern = "^(?\n\n
\n\n`; - }); + // conditions for display math detection $$...$$: + // - pattern starts at beginning of line or is not prefixed with backslash or dollar + // - left delimiter ($$) is not escaped by backslash + const displayPatternDollar = "(^|[^\\\\$])\\$\\$(([^$]|\\\\\\$)+?)\\$\\$"; - md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1, p2) { - const p2e = AllHtmlEntities.encode(p2); - return `${p1}`; - }); - } else { - // detect math with tex delimiters, inline: $...$, display $$...$$ - // preferably use negative lookbehinds, not supported in all major browsers: - // const displayPattern = "^(?\n\n
\n\n`; + }); - // conditions for inline math detection $...$: - // - pattern starts at beginning of line, follows whitespace character or punctuation - // - pattern is on a single line - // - left and right delimiters ($) are not escaped by backslashes - // - left delimiter is not followed by whitespace character - // - right delimiter is not prefixed with whitespace character - const inlinePatternDollar = "(^|\\s|[.,!?:;])(?!\\\\)\\$(?!\\s)(([^$\\n]|\\\\\\$)*([^\\\\\\s\\$]|\\\\\\$)(?:\\\\\\$)?)\\$"; + md = md.replace(RegExp(inlinePatternDollar, "gm"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}`; + }); - md = md.replace(RegExp(displayPatternDollar, "gm"), function(m, p1, p2) { - const p2e = AllHtmlEntities.encode(p2); - return `${p1}
\n\n
\n\n`; - }); + // detect math with latex delimiters, inline: \(...\), display \[...\] - md = md.replace(RegExp(inlinePatternDollar, "gm"), function(m, p1, p2) { - const p2e = AllHtmlEntities.encode(p2); - return `${p1}`; - }); + // conditions for display math detection \[...\]: + // - pattern starts at beginning of line or is not prefixed with backslash + // - pattern is not empty + const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || + "(^|[^\\\\])\\\\\\[(?!\\\\\\])(.*?)\\\\\\]"; - // detect math with latex delimiters, inline: \(...\), display \[...\] + // conditions for inline math detection \(...\): + // - pattern starts at beginning of line or is not prefixed with backslash + // - pattern is not empty + const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || + "(^|[^\\\\])\\\\\\((?!\\\\\\))(.*?)\\\\\\)"; - // conditions for display math detection \[...\]: - // - pattern starts at beginning of line or is not prefixed with backslash - // - pattern is not empty - const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] || - "(^|[^\\\\])\\\\\\[(?!\\\\\\])(.*?)\\\\\\]"; + md = md.replace(RegExp(displayPattern, "gms"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}
\n\n
\n\n`; + }); - // conditions for inline math detection \(...\): - // - pattern starts at beginning of line or is not prefixed with backslash - // - pattern is not empty - const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] || - "(^|[^\\\\])\\\\\\((?!\\\\\\))(.*?)\\\\\\)"; - - md = md.replace(RegExp(displayPattern, "gms"), function(m, p1, p2) { - const p2e = AllHtmlEntities.encode(p2); - return `${p1}
\n\n
\n\n`; - }); - - md = md.replace(RegExp(inlinePattern, "gms"), function(m, p1, p2) { - const p2e = AllHtmlEntities.encode(p2); - return `${p1}`; - }); - } + md = md.replace(RegExp(inlinePattern, "gms"), function(m, p1, p2) { + const p2e = AllHtmlEntities.encode(p2); + return `${p1}`; + }); // make sure div tags always start on a new line, otherwise it will confuse // the markdown parser @@ -154,12 +140,6 @@ export function markdownSerializeIfNeeded(md: string, {forceHTML = false} = {}, } } -export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) { - const md = mdSerialize(model); - - return markdownSerializeIfNeeded(md, {forceHTML: forceHTML}); -} - export function textSerialize(model: EditorModel) { return model.parts.reduce((text, part) => { switch (part.type) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bcae343550..2fb70ecdb1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -416,7 +416,6 @@ "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message", "Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown", "Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown", - "Sends a message in TeX mode, without restrictions": "Sends a message in TeX mode, without restrictions", "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", "/ddg is not a command": "/ddg is not a command", "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", From ac1f9b4247f7eefacc969bfcbdbf30984c8e15e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20M=C3=A4der?= Date: Fri, 29 Jan 2021 15:49:20 +0100 Subject: [PATCH 068/916] Add config keys for alternative patterns --- src/editor/serialize.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 6b1d97a0ef..6655c64347 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -55,7 +55,9 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = // conditions for display math detection $$...$$: // - pattern starts at beginning of line or is not prefixed with backslash or dollar // - left delimiter ($$) is not escaped by backslash - const displayPatternDollar = "(^|[^\\\\$])\\$\\$(([^$]|\\\\\\$)+?)\\$\\$"; + const displayPatternAlternative = (SdkConfig.get()['latex_maths_delims'] || + {})['display_pattern_alternative'] || + "(^|[^\\\\$])\\$\\$(([^$]|\\\\\\$)+?)\\$\\$"; // conditions for inline math detection $...$: // - pattern starts at beginning of line, follows whitespace character or punctuation @@ -63,14 +65,16 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = // - left and right delimiters ($) are not escaped by backslashes // - left delimiter is not followed by whitespace character // - right delimiter is not prefixed with whitespace character - const inlinePatternDollar = "(^|\\s|[.,!?:;])(?!\\\\)\\$(?!\\s)(([^$\\n]|\\\\\\$)*([^\\\\\\s\\$]|\\\\\\$)(?:\\\\\\$)?)\\$"; + const inlinePatternAlternative = (SdkConfig.get()['latex_maths_delims'] || + {})['inline_pattern_alternative'] || + "(^|\\s|[.,!?:;])(?!\\\\)\\$(?!\\s)(([^$\\n]|\\\\\\$)*([^\\\\\\s\\$]|\\\\\\$)(?:\\\\\\$)?)\\$"; - md = md.replace(RegExp(displayPatternDollar, "gm"), function(m, p1, p2) { + md = md.replace(RegExp(displayPatternAlternative, "gm"), function(m, p1, p2) { const p2e = AllHtmlEntities.encode(p2); return `${p1}
\n\n
\n\n`; }); - md = md.replace(RegExp(inlinePatternDollar, "gm"), function(m, p1, p2) { + md = md.replace(RegExp(inlinePatternAlternative, "gm"), function(m, p1, p2) { const p2e = AllHtmlEntities.encode(p2); return `${p1}`; }); From cc38bcf333bc9fdd7d8ebc7d0b4d06330ba7e359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 6 Feb 2021 15:09:21 +0100 Subject: [PATCH 069/916] Display room name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/Pill.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index daa4cb70e2..f1527c48b1 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -226,7 +226,7 @@ class Pill extends React.Component { case Pill.TYPE_ROOM_MENTION: { const room = this.state.room; if (room) { - linkText = resource; + linkText = room.name; if (this.props.shouldShowPillAvatar) { avatar =
); } - } else if (this.state.view === Views.WELCOME) { + } else if (this.state.view === Views.WELCOME && !shouldUseLoginForWelcome(SdkConfig.get())) { const Welcome = sdk.getComponent('auth.Welcome'); view = ; } else if (this.state.view === Views.REGISTER && SettingsStore.getValue(UIFeature.Registration)) { @@ -2020,7 +2021,8 @@ export default class MatrixChat extends React.PureComponent { {...this.getServerProperties()} /> ); - } else if (this.state.view === Views.LOGIN) { + } else if (this.state.view === Views.LOGIN + || (this.state.view === Views.WELCOME && shouldUseLoginForWelcome(SdkConfig.get()))) { const showPasswordReset = SettingsStore.getValue(UIFeature.PasswordReset); const Login = sdk.getComponent('structures.auth.Login'); view = ( diff --git a/src/utils/pages.js b/src/utils/pages.ts similarity index 68% rename from src/utils/pages.js rename to src/utils/pages.ts index d63ca3f2c7..bae76be29d 100644 --- a/src/utils/pages.js +++ b/src/utils/pages.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019, 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -export function getHomePageUrl(appConfig) { +import { ConfigOptions } from "../SdkConfig"; + +export function getHomePageUrl(appConfig: ConfigOptions): string | null { const pagesConfig = appConfig.embeddedPages; - let pageUrl = null; - if (pagesConfig) { - pageUrl = pagesConfig.homeUrl; - } + let pageUrl = pagesConfig?.homeUrl; + if (!pageUrl) { // This is a deprecated config option for the home page // (despite the name, given we also now have a welcome @@ -29,3 +29,8 @@ export function getHomePageUrl(appConfig) { return pageUrl; } + +export function shouldUseLoginForWelcome(appConfig: ConfigOptions): boolean { + const pagesConfig = appConfig.embeddedPages; + return pagesConfig?.loginForWelcome === true; +} From 54c38844d254546a35afbe8d81be4f6380a54262 Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Wed, 17 Feb 2021 22:00:48 +1300 Subject: [PATCH 086/916] Use key bindings in BasicMessageComposer --- src/KeyBindingsManager.ts | 164 +++++++++++++++- .../views/rooms/BasicMessageComposer.tsx | 175 +++++++++--------- 2 files changed, 245 insertions(+), 94 deletions(-) diff --git a/src/KeyBindingsManager.ts b/src/KeyBindingsManager.ts index e8f4126fbd..ef5084c16c 100644 --- a/src/KeyBindingsManager.ts +++ b/src/KeyBindingsManager.ts @@ -4,6 +4,8 @@ import SettingsStore from './settings/SettingsStore'; export enum KeyBindingContext { /** Key bindings for the chat message composer component */ MessageComposer = 'MessageComposer', + /** Key bindings for text editing autocompletion */ + AutoComplete = 'AutoComplete', } export enum KeyAction { @@ -21,9 +23,34 @@ export enum KeyAction { EditPrevMessage = 'EditPrevMessage', /** Start editing the user's next sent message */ EditNextMessage = 'EditNextMessage', - - /** Cancel editing a message or cancel replying to a message */ + /** Cancel editing a message or cancel replying to a message*/ CancelEditing = 'CancelEditing', + + /** Set bold format the current selection */ + FormatBold = 'FormatBold', + /** Set italics format the current selection */ + FormatItalics = 'FormatItalics', + /** Format the current selection as quote */ + FormatQuote = 'FormatQuote', + /** Undo the last editing */ + EditUndo = 'EditUndo', + /** Redo editing */ + EditRedo = 'EditRedo', + /** Insert new line */ + NewLine = 'NewLine', + MoveCursorToStart = 'MoveCursorToStart', + MoveCursorToEnd = 'MoveCursorToEnd', + + // Autocomplete + + /** Apply the current autocomplete selection */ + AutocompleteApply = 'AutocompleteApply', + /** Cancel autocompletion */ + AutocompleteCancel = 'AutocompleteCancel', + /** Move to the previous autocomplete selection */ + AutocompletePrevSelection = 'AutocompletePrevSelection', + /** Move to the next autocomplete selection */ + AutocompleteNextSelection = 'AutocompleteNextSelection', } /** @@ -84,7 +111,69 @@ const messageComposerBindings = (): KeyBinding[] => { key: Key.ESCAPE, }, }, + { + action: KeyAction.FormatBold, + keyCombo: { + key: Key.B, + ctrlOrCmd: true, + }, + }, + { + action: KeyAction.FormatItalics, + keyCombo: { + key: Key.I, + ctrlOrCmd: true, + }, + }, + { + action: KeyAction.FormatQuote, + keyCombo: { + key: Key.GREATER_THAN, + ctrlOrCmd: true, + shiftKey: true, + }, + }, + { + action: KeyAction.EditUndo, + keyCombo: { + key: Key.Z, + ctrlOrCmd: true, + }, + }, + // Note: the following two bindings also work with just HOME and END, add them here? + { + action: KeyAction.MoveCursorToStart, + keyCombo: { + key: Key.HOME, + ctrlOrCmd: true, + }, + }, + { + action: KeyAction.MoveCursorToEnd, + keyCombo: { + key: Key.END, + ctrlOrCmd: true, + }, + }, ]; + if (isMac) { + bindings.push({ + action: KeyAction.EditRedo, + keyCombo: { + key: Key.Z, + ctrlOrCmd: true, + shiftKey: true, + }, + }); + } else { + bindings.push({ + action: KeyAction.EditRedo, + keyCombo: { + key: Key.Y, + ctrlOrCmd: true, + }, + }); + } if (SettingsStore.getValue('MessageComposerInput.ctrlEnterToSend')) { bindings.push({ action: KeyAction.Send, @@ -93,6 +182,12 @@ const messageComposerBindings = (): KeyBinding[] => { ctrlOrCmd: true, }, }); + bindings.push({ + action: KeyAction.NewLine, + keyCombo: { + key: Key.ENTER, + }, + }); } else { bindings.push({ action: KeyAction.Send, @@ -100,17 +195,75 @@ const messageComposerBindings = (): KeyBinding[] => { key: Key.ENTER, }, }); + bindings.push({ + action: KeyAction.NewLine, + keyCombo: { + key: Key.ENTER, + shiftKey: true, + }, + }); + if (isMac) { + bindings.push({ + action: KeyAction.NewLine, + keyCombo: { + key: Key.ENTER, + altKey: true, + }, + }); + } } - return bindings; } +const autocompleteBindings = (): KeyBinding[] => { + return [ + { + action: KeyAction.AutocompleteApply, + keyCombo: { + key: Key.TAB, + }, + }, + { + action: KeyAction.AutocompleteApply, + keyCombo: { + key: Key.TAB, + ctrlKey: true, + }, + }, + { + action: KeyAction.AutocompleteApply, + keyCombo: { + key: Key.TAB, + shiftKey: true, + }, + }, + { + action: KeyAction.AutocompleteCancel, + keyCombo: { + key: Key.ESCAPE, + }, + }, + { + action: KeyAction.AutocompletePrevSelection, + keyCombo: { + key: Key.ARROW_UP, + }, + }, + { + action: KeyAction.AutocompleteNextSelection, + keyCombo: { + key: Key.ARROW_DOWN, + }, + }, + ] +} + /** * Helper method to check if a KeyboardEvent matches a KeyCombo * * Note, this method is only exported for testing. */ -export function isKeyComboMatch(ev: KeyboardEvent, combo: KeyCombo, onMac: boolean): boolean { +export function isKeyComboMatch(ev: KeyboardEvent | React.KeyboardEvent, combo: KeyCombo, onMac: boolean): boolean { if (combo.key !== undefined && ev.key !== combo.key) { return false; } @@ -160,12 +313,13 @@ export class KeyBindingsManager { */ contextBindings: Record = { [KeyBindingContext.MessageComposer]: messageComposerBindings, + [KeyBindingContext.AutoComplete]: autocompleteBindings, }; /** * Finds a matching KeyAction for a given KeyboardEvent */ - getAction(context: KeyBindingContext, ev: KeyboardEvent): KeyAction { + getAction(context: KeyBindingContext, ev: KeyboardEvent | React.KeyboardEvent): KeyAction { const bindings = this.contextBindings[context]?.(); if (!bindings) { return KeyAction.None; diff --git a/src/components/views/rooms/BasicMessageComposer.tsx b/src/components/views/rooms/BasicMessageComposer.tsx index 017ce77166..d0119ddc05 100644 --- a/src/components/views/rooms/BasicMessageComposer.tsx +++ b/src/components/views/rooms/BasicMessageComposer.tsx @@ -46,6 +46,7 @@ import {IDiff} from "../../../editor/diff"; import AutocompleteWrapperModel from "../../../editor/autocomplete"; import DocumentPosition from "../../../editor/position"; import {ICompletion} from "../../../autocomplete/Autocompleter"; +import { getKeyBindingsManager, KeyBindingContext, KeyAction } from '../../../KeyBindingsManager'; // matches emoticons which follow the start of a line or whitespace const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s$'); @@ -419,98 +420,94 @@ export default class BasicMessageEditor extends React.Component private onKeyDown = (event: React.KeyboardEvent) => { const model = this.props.model; - const modKey = IS_MAC ? event.metaKey : event.ctrlKey; let handled = false; - // format bold - if (modKey && event.key === Key.B) { - this.onFormatAction(Formatting.Bold); - handled = true; - // format italics - } else if (modKey && event.key === Key.I) { - this.onFormatAction(Formatting.Italics); - handled = true; - // format quote - } else if (modKey && event.key === Key.GREATER_THAN) { - this.onFormatAction(Formatting.Quote); - handled = true; - // redo - } else if ((!IS_MAC && modKey && event.key === Key.Y) || - (IS_MAC && modKey && event.shiftKey && event.key === Key.Z)) { - if (this.historyManager.canRedo()) { - const {parts, caret} = this.historyManager.redo(); - // pass matching inputType so historyManager doesn't push echo - // when invoked from rerender callback. - model.reset(parts, caret, "historyRedo"); - } - handled = true; - // undo - } else if (modKey && event.key === Key.Z) { - if (this.historyManager.canUndo()) { - const {parts, caret} = this.historyManager.undo(this.props.model); - // pass matching inputType so historyManager doesn't push echo - // when invoked from rerender callback. - model.reset(parts, caret, "historyUndo"); - } - handled = true; - // insert newline on Shift+Enter - } else if (event.key === Key.ENTER && (event.shiftKey || (IS_MAC && event.altKey))) { - this.insertText("\n"); - handled = true; - // move selection to start of composer - } else if (modKey && event.key === Key.HOME && !event.shiftKey) { - setSelection(this.editorRef.current, model, { - index: 0, - offset: 0, - }); - handled = true; - // move selection to end of composer - } else if (modKey && event.key === Key.END && !event.shiftKey) { - setSelection(this.editorRef.current, model, { - index: model.parts.length - 1, - offset: model.parts[model.parts.length - 1].text.length, - }); - handled = true; - // autocomplete or enter to send below shouldn't have any modifier keys pressed. - } else { - const metaOrAltPressed = event.metaKey || event.altKey; - const modifierPressed = metaOrAltPressed || event.shiftKey; - if (model.autoComplete && model.autoComplete.hasCompletions()) { - const autoComplete = model.autoComplete; - switch (event.key) { - case Key.ARROW_UP: - if (!modifierPressed) { - autoComplete.onUpArrow(event); - handled = true; - } - break; - case Key.ARROW_DOWN: - if (!modifierPressed) { - autoComplete.onDownArrow(event); - handled = true; - } - break; - case Key.TAB: - if (!metaOrAltPressed) { - autoComplete.onTab(event); - handled = true; - } - break; - case Key.ESCAPE: - if (!modifierPressed) { - autoComplete.onEscape(event); - handled = true; - } - break; - default: - return; // don't preventDefault on anything else - } - } else if (event.key === Key.TAB) { - this.tabCompleteName(event); + const action = getKeyBindingsManager().getAction(KeyBindingContext.MessageComposer, event); + switch (action) { + case KeyAction.FormatBold: + this.onFormatAction(Formatting.Bold); handled = true; - } else if (event.key === Key.BACKSPACE || event.key === Key.DELETE) { - this.formatBarRef.current.hide(); - } + break; + case KeyAction.FormatItalics: + this.onFormatAction(Formatting.Italics); + handled = true; + break; + case KeyAction.FormatQuote: + this.onFormatAction(Formatting.Quote); + handled = true; + break; + case KeyAction.EditRedo: + if (this.historyManager.canRedo()) { + const {parts, caret} = this.historyManager.redo(); + // pass matching inputType so historyManager doesn't push echo + // when invoked from rerender callback. + model.reset(parts, caret, "historyRedo"); + } + handled = true; + break; + case KeyAction.EditUndo: + if (this.historyManager.canUndo()) { + const {parts, caret} = this.historyManager.undo(this.props.model); + // pass matching inputType so historyManager doesn't push echo + // when invoked from rerender callback. + model.reset(parts, caret, "historyUndo"); + } + handled = true; + break; + case KeyAction.NewLine: + this.insertText("\n"); + handled = true; + break; + case KeyAction.MoveCursorToStart: + setSelection(this.editorRef.current, model, { + index: 0, + offset: 0, + }); + handled = true; + break; + case KeyAction.MoveCursorToEnd: + setSelection(this.editorRef.current, model, { + index: model.parts.length - 1, + offset: model.parts[model.parts.length - 1].text.length, + }); + handled = true; + break; } + if (handled) { + event.preventDefault(); + event.stopPropagation(); + return; + } + + const autocompleteAction = getKeyBindingsManager().getAction(KeyBindingContext.AutoComplete, event); + if (model.autoComplete && model.autoComplete.hasCompletions()) { + const autoComplete = model.autoComplete; + switch (autocompleteAction) { + case KeyAction.AutocompletePrevSelection: + autoComplete.onUpArrow(event); + handled = true; + break; + case KeyAction.AutocompleteNextSelection: + autoComplete.onDownArrow(event); + handled = true; + break; + case KeyAction.AutocompleteApply: + autoComplete.onTab(event); + handled = true; + break; + case KeyAction.AutocompleteCancel: + autoComplete.onEscape(event); + handled = true; + break; + default: + return; // don't preventDefault on anything else + } + } else if (autocompleteAction === KeyAction.AutocompleteApply) { + this.tabCompleteName(event); + handled = true; + } else if (event.key === Key.BACKSPACE || event.key === Key.DELETE) { + this.formatBarRef.current.hide(); + } + if (handled) { event.preventDefault(); event.stopPropagation(); From 5de99c7708c58b2808d288160ce8fb69f4f5027c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 18 Feb 2021 19:40:24 +0100 Subject: [PATCH 087/916] Fix licenses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/settings/_SpellCheckLanguages.scss | 3 +-- src/components/views/elements/SpellCheckLanguagesDropdown.tsx | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/res/css/views/settings/_SpellCheckLanguages.scss b/res/css/views/settings/_SpellCheckLanguages.scss index ddfa0bf9e0..bb322c983f 100644 --- a/res/css/views/settings/_SpellCheckLanguages.scss +++ b/res/css/views/settings/_SpellCheckLanguages.scss @@ -1,6 +1,5 @@ /* -Copyright 2019 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2021 Šimon Brandner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx index 53c3f310b7..029d162573 100644 --- a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx +++ b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx @@ -1,6 +1,5 @@ /* -Copyright 2017 Marcel Radzio (MTRNord) -Copyright 2017 Vector Creations Ltd. +Copyright 2021 Šimon Brandner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From ed02503462d93e43659bddd3280a19a0b31e26f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 18 Feb 2021 19:41:19 +0100 Subject: [PATCH 088/916] Fix one more license MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/settings/SpellCheckSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/SpellCheckSettings.tsx b/src/components/views/settings/SpellCheckSettings.tsx index bfe0774570..d08f263b5f 100644 --- a/src/components/views/settings/SpellCheckSettings.tsx +++ b/src/components/views/settings/SpellCheckSettings.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2021 Šimon Brandner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 2ebc1252cbbfc9731dc412947287ef5e4c9ce460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 18 Feb 2021 19:54:54 +0100 Subject: [PATCH 089/916] Removed unnecessary functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../elements/SpellCheckLanguagesDropdown.tsx | 37 ++++++++++--------- .../tabs/user/GeneralUserSettingsTab.js | 6 ++- src/languageHandler.tsx | 14 ------- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx index 029d162573..c647f6e410 100644 --- a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx +++ b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx @@ -18,7 +18,7 @@ import React from 'react'; import Dropdown from "../../views/elements/Dropdown" import * as sdk from '../../../index'; -import * as languageHandler from '../../../languageHandler'; +import PlatformPeg from "../../../PlatformPeg"; import SettingsStore from "../../../settings/SettingsStore"; import { _t } from "../../../languageHandler"; @@ -52,23 +52,26 @@ export default class SpellCheckLanguagesDropdown extends React.Component { - languages.sort(function(a, b) { - if (a < b) return -1; - if (a > b) return 1; - return 0; - }); - const langs = []; - languages.forEach((language) => { - langs.push({ - label: language, - value: language, + const plaf = PlatformPeg.get(); + if (plaf) { + plaf.getAvailableSpellCheckLanguages().then((languages) => { + languages.sort(function(a, b) { + if (a < b) return -1; + if (a > b) return 1; + return 0; + }); + const langs = []; + languages.forEach((language) => { + langs.push({ + label: language, + value: language, + }) }) - }) - this.setState({languages: langs}); - }).catch((e) => { - this.setState({languages: ['en']}); - }); + this.setState({languages: langs}); + }).catch((e) => { + this.setState({languages: ['en']}); + }); + } } _onSearchChange(search) { diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index febbcc8e36..e87dca88c8 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -188,7 +188,10 @@ export default class GeneralUserSettingsTab extends React.Component { SettingsStore.setValue("spell-check-languages", null, SettingLevel.DEVICE, languages); this.setState({spellCheckLanguages: languages}); - languageHandler.setSpellCheckLanguages(languages); + const plaf = PlatformPeg.get(); + if (plaf) { + plaf.setSpellCheckLanguages(languages); + } }; _onPasswordChangeError = (err) => { @@ -402,6 +405,7 @@ export default class GeneralUserSettingsTab extends React.Component { render() { const plaf = PlatformPeg.get(); const supportsMultiLanguageSpellCheck = plaf.supportsMultiLanguageSpellCheck(); + console.log("LOG", supportsMultiLanguageSpellCheck); const discoWarning = this.state.requiredPolicyInfo.hasTerms ? { - const plaf = PlatformPeg.get(); - if (plaf) { - return plaf.getAvailableSpellCheckLanguages(); - } -} - export function getAllLanguagesFromJson() { return getLangsJson().then((langsObject) => { const langs = []; From 305d64cda88aebef8e2a0e799606224baeac4dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 18 Feb 2021 20:09:39 +0100 Subject: [PATCH 090/916] Removed log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index e87dca88c8..41597604e9 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -405,7 +405,6 @@ export default class GeneralUserSettingsTab extends React.Component { render() { const plaf = PlatformPeg.get(); const supportsMultiLanguageSpellCheck = plaf.supportsMultiLanguageSpellCheck(); - console.log("LOG", supportsMultiLanguageSpellCheck); const discoWarning = this.state.requiredPolicyInfo.hasTerms ? Date: Thu, 18 Feb 2021 20:12:48 +0100 Subject: [PATCH 091/916] Use getSpellCheckLanguages() instead of a setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/BasePlatform.ts | 4 ++++ .../settings/tabs/user/GeneralUserSettingsTab.js | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index fe655371a5..9d7077097b 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -250,6 +250,10 @@ export default abstract class BasePlatform { setSpellCheckLanguages(preferredLangs: string[]) {} + getSpellCheckLanguages(): Promise | null { + return null; + } + getAvailableSpellCheckLanguages(): Promise | null { return null; } diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 41597604e9..3936864215 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -50,7 +50,7 @@ export default class GeneralUserSettingsTab extends React.Component { this.state = { language: languageHandler.getCurrentLanguage(), - spellCheckLanguages: SettingsStore.getValue("spell-check-languages", null, false), + spellCheckLanguages: [], haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()), serverSupportsSeparateAddAndBind: null, idServerHasUnsignedTerms: false, @@ -87,6 +87,15 @@ export default class GeneralUserSettingsTab extends React.Component { this._getThreepidState(); } + async componentDidMount() { + const plaf = PlatformPeg.get(); + if (plaf) { + this.setState({ + spellCheckLanguages: await plaf.getSpellCheckLanguages(), + }); + } + } + componentWillUnmount() { dis.unregister(this.dispatcherRef); } From 5a6e393fa2c6344a00efc14ab4eb17fc6a258a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 18 Feb 2021 20:13:55 +0100 Subject: [PATCH 092/916] Removed spell-check-languages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 1 - src/settings/Settings.ts | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 3936864215..b17ab18c39 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -194,7 +194,6 @@ export default class GeneralUserSettingsTab extends React.Component { }; _onSpellCheckLanguagesChange = (languages) => { - SettingsStore.setValue("spell-check-languages", null, SettingLevel.DEVICE, languages); this.setState({spellCheckLanguages: languages}); const plaf = PlatformPeg.get(); diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index b486f0fbf8..ca5e2f1d04 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -424,10 +424,6 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, default: "en", }, - "spell-check-languages": { - supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, - default: ["en-US"], - }, "breadcrumb_rooms": { // not really a setting supportedLevels: [SettingLevel.ACCOUNT], From e5d68142c6e35811a37d2ea709167ead99247a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 23 Feb 2021 20:47:42 +0100 Subject: [PATCH 093/916] Remove zoom icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/zoom-white.svg | 59 ---------------------- src/components/views/elements/ImageView.js | 3 -- 2 files changed, 62 deletions(-) delete mode 100644 res/img/zoom-white.svg diff --git a/res/img/zoom-white.svg b/res/img/zoom-white.svg deleted file mode 100644 index 19379cb881..0000000000 --- a/res/img/zoom-white.svg +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 4e38b4b4eb..be849d1dde 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -293,9 +293,6 @@ export default class ImageView extends React.Component {
{ redactButton } - - { - { From 35663c35d27e6fc6211ad501375c49a1b8c469d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 23 Feb 2021 20:49:31 +0100 Subject: [PATCH 094/916] Reorder the icons according to the design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index be849d1dde..7c40604a88 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -293,18 +293,18 @@ export default class ImageView extends React.Component {
{ redactButton } + + { + + + { + { { - - { - - - { - { From 6cf19e889777a1732182274ae0408dc8a746d7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 23 Feb 2021 21:04:21 +0100 Subject: [PATCH 095/916] Update icons according to the design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/cancel-white.svg | 10 --- res/img/download-white.svg | 95 ---------------------- res/img/image-view/close.svg | 4 + res/img/image-view/download.svg | 3 + res/img/image-view/rotate-ccw.svg | 3 + res/img/image-view/rotate-cw.svg | 3 + res/img/image-view/zoom-in.svg | 3 + res/img/image-view/zoom-out.svg | 3 + res/img/minus-white.svg | 64 --------------- res/img/plus-white.svg | 73 ----------------- res/img/rotate-ccw.svg | 1 - res/img/rotate-cw.svg | 1 - src/components/views/elements/ImageView.js | 12 +-- 13 files changed, 25 insertions(+), 250 deletions(-) delete mode 100644 res/img/cancel-white.svg delete mode 100644 res/img/download-white.svg create mode 100644 res/img/image-view/close.svg create mode 100644 res/img/image-view/download.svg create mode 100644 res/img/image-view/rotate-ccw.svg create mode 100644 res/img/image-view/rotate-cw.svg create mode 100644 res/img/image-view/zoom-in.svg create mode 100644 res/img/image-view/zoom-out.svg delete mode 100644 res/img/minus-white.svg delete mode 100644 res/img/plus-white.svg delete mode 100644 res/img/rotate-ccw.svg delete mode 100644 res/img/rotate-cw.svg diff --git a/res/img/cancel-white.svg b/res/img/cancel-white.svg deleted file mode 100644 index 65e14c2fbc..0000000000 --- a/res/img/cancel-white.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - Slice 1 - Created with Sketch. - - - - - \ No newline at end of file diff --git a/res/img/download-white.svg b/res/img/download-white.svg deleted file mode 100644 index 5c800b350e..0000000000 --- a/res/img/download-white.svg +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - image/svg+xml - - Fill 75 - - - - - - Fill 75 - Created with Sketch. - - - - - - - - - - - - - diff --git a/res/img/image-view/close.svg b/res/img/image-view/close.svg new file mode 100644 index 0000000000..f849425264 --- /dev/null +++ b/res/img/image-view/close.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/img/image-view/download.svg b/res/img/image-view/download.svg new file mode 100644 index 0000000000..c51deed876 --- /dev/null +++ b/res/img/image-view/download.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/image-view/rotate-ccw.svg b/res/img/image-view/rotate-ccw.svg new file mode 100644 index 0000000000..85ea3198de --- /dev/null +++ b/res/img/image-view/rotate-ccw.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/image-view/rotate-cw.svg b/res/img/image-view/rotate-cw.svg new file mode 100644 index 0000000000..e337f3420e --- /dev/null +++ b/res/img/image-view/rotate-cw.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/image-view/zoom-in.svg b/res/img/image-view/zoom-in.svg new file mode 100644 index 0000000000..c0816d489e --- /dev/null +++ b/res/img/image-view/zoom-in.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/image-view/zoom-out.svg b/res/img/image-view/zoom-out.svg new file mode 100644 index 0000000000..0539e8c81a --- /dev/null +++ b/res/img/image-view/zoom-out.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/minus-white.svg b/res/img/minus-white.svg deleted file mode 100644 index 2921f34980..0000000000 --- a/res/img/minus-white.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - image/svg+xml - - Fill 75 - - - - - - Fill 75 - Created with Sketch. - - - diff --git a/res/img/plus-white.svg b/res/img/plus-white.svg deleted file mode 100644 index 7759ace50a..0000000000 --- a/res/img/plus-white.svg +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - image/svg+xml - - Fill 75 - - - - - - Fill 75 - Created with Sketch. - - - - diff --git a/res/img/rotate-ccw.svg b/res/img/rotate-ccw.svg deleted file mode 100644 index 3924eca040..0000000000 --- a/res/img/rotate-ccw.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/res/img/rotate-cw.svg b/res/img/rotate-cw.svg deleted file mode 100644 index 91021c96d8..0000000000 --- a/res/img/rotate-cw.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 7c40604a88..10e5b083da 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -294,22 +294,22 @@ export default class ImageView extends React.Component {
{ redactButton } - { + { - { + { - { + { - { + { - { + { - { + {
Date: Wed, 24 Feb 2021 07:47:59 +0100 Subject: [PATCH 096/916] Reorganize elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 48 +++++++++++----------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 10e5b083da..d37f6ee618 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -284,33 +284,35 @@ export default class ImageView extends React.Component { ref={ref => this.focusLock = ref} >
-
+
+
{ this.getName() }
- { sizeRes } + { sizeRes } { metadata } -
-
- { redactButton } - - { - - - { - - - { - - - { - - - { - - - { - +
+
+ { redactButton } + + { + + + { + + + { + + + { + + + { + + + { + +
Date: Wed, 24 Feb 2021 07:50:10 +0100 Subject: [PATCH 097/916] Remove name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index d37f6ee618..3007fe566b 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -124,14 +124,6 @@ export default class ImageView extends React.Component { }); }; - getName() { - let name = this.props.name; - if (name && this.props.link) { - name = { name }; - } - return name; - } - onRotateCounterClockwiseClick = () => { const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; @@ -286,11 +278,8 @@ export default class ImageView extends React.Component {
-
- { this.getName() } -
- { sizeRes } - { metadata } + { sizeRes } + { metadata }
{ redactButton } From 497131874b2c16e3d532558557bec59057ade65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 07:53:33 +0100 Subject: [PATCH 098/916] Remove size info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 3007fe566b..e3dbe2a411 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -20,7 +20,6 @@ import PropTypes from 'prop-types'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {formatDate} from '../../../DateUtils'; import { _t } from '../../../languageHandler'; -import filesize from "filesize"; import AccessibleButton from "./AccessibleButton"; import Modal from "../../../Modal"; import * as sdk from "../../../index"; @@ -204,23 +203,6 @@ export default class ImageView extends React.Component { let mayRedact = false; const showEventMeta = !!this.props.mxEvent; - let res; - if (this.props.width && this.props.height) { - res = this.props.width + "x" + this.props.height + "px"; - } - - let size; - if (this.props.fileSize) { - size = filesize(this.props.fileSize); - } - - let sizeRes; - if (size && res) { - sizeRes = size + ", " + res; - } else { - sizeRes = size || res; - } - let metadata; if (showEventMeta) { // Figure out the sender, defaulting to mxid @@ -278,7 +260,6 @@ export default class ImageView extends React.Component {
- { sizeRes } { metadata }
From 768d26818965d10646e3605c70cabaf7e596f3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 07:54:36 +0100 Subject: [PATCH 099/916] Fix css MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 43333a25e6..ee02a1dce8 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -53,6 +53,9 @@ limitations under the License. z-index: 1000; align-self: flex-start; pointer-events: all; + display: flex; + justify-content: space-between; + width: 100%; } .mx_ImageView_toolbar { @@ -76,17 +79,7 @@ limitations under the License. padding: 5px; } -.mx_ImageView_name { - font-size: $font-18px; - margin-bottom: 6px; - word-wrap: break-word; -} - .mx_ImageView_metadata { font-size: $font-15px; opacity: 0.5; } - -.mx_ImageView_size { - font-size: $font-11px; -} From ab79deb88fa828ef1a13a1fecb9e0648b38b68f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 08:11:53 +0100 Subject: [PATCH 100/916] Update the looks a bit more MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 14 ++++++-- src/components/views/elements/ImageView.js | 38 ++++++++++++---------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index ee02a1dce8..8afd2670a7 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -49,24 +49,32 @@ limitations under the License. } .mx_ImageView_panel { + width: 100%; + height: 68px; position: absolute; z-index: 1000; align-self: flex-start; pointer-events: all; display: flex; justify-content: space-between; - width: 100%; + align-items: center; } .mx_ImageView_toolbar { right: 0; - padding: 50px 50px 0 0; + padding-right: 18px; display: flex; + align-items: center; +} + +.mx_ImageView_toolbar_buttons { + display: flex; + align-items: center; } .mx_ImageView_label { left: 0; - padding: 50px 0 0 50px; + padding-left: 18px; text-align: left; display: flex; justify-content: center; diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index e3dbe2a411..fd9d84b900 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -227,7 +227,7 @@ export default class ImageView extends React.Component { if (mayRedact) { redactButton = ( - { + { ); } @@ -263,24 +263,26 @@ export default class ImageView extends React.Component { { metadata }
- { redactButton } - - { - - - { - - - { - - - { - - - { - +
+ { redactButton } + + { + + + { + + + { + + + { + + + { + +
- { + {
From 899ce1f605bcebbe46d44c35a6e70bae0155eac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 11:15:59 +0100 Subject: [PATCH 101/916] Partially fix overflow issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 14 ++++++++----- src/components/views/elements/ImageView.js | 24 ++++++++++++---------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 8afd2670a7..b8fb9b81c2 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -27,14 +27,19 @@ limitations under the License. .mx_ImageView_content { width: 100%; + display: flex; + flex-direction: column; +} + +.mx_ImageView_image_wrapper { display: flex; justify-content: center; align-items: center; - + height: 100%; overflow: hidden; } -.mainImage { +.mx_ImageView_image { object-fit: contain; pointer-events: all; @@ -43,6 +48,8 @@ limitations under the License. min-width: 100px; min-height: 100px; + border-radius: 8px; + &:hover { cursor: grab; } @@ -51,7 +58,6 @@ limitations under the License. .mx_ImageView_panel { width: 100%; height: 68px; - position: absolute; z-index: 1000; align-self: flex-start; pointer-events: all; @@ -62,7 +68,6 @@ limitations under the License. .mx_ImageView_toolbar { right: 0; - padding-right: 18px; display: flex; align-items: center; } @@ -74,7 +79,6 @@ limitations under the License. .mx_ImageView_label { left: 0; - padding-left: 18px; text-align: left; display: flex; justify-content: center; diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index fd9d84b900..e35c8a3e15 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -286,17 +286,19 @@ export default class ImageView extends React.Component {
- +
+ +
); From 6aac8f1735b8d3bb0bfae0bfc91f8a514178edfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 11:20:28 +0100 Subject: [PATCH 102/916] Change padding a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index b8fb9b81c2..943758b80e 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -88,7 +88,7 @@ limitations under the License. } .mx_ImageView_button { - padding: 5px; + padding-left: 28px; } .mx_ImageView_metadata { From 13d766218aeb0f866f45ffe8e7e0b9e91bf93fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 11:59:43 +0100 Subject: [PATCH 103/916] Remove redact button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index e35c8a3e15..e1babd8bf1 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -200,7 +200,6 @@ export default class ImageView extends React.Component { } render() { - let mayRedact = false; const showEventMeta = !!this.props.mxEvent; let metadata; @@ -210,7 +209,6 @@ export default class ImageView extends React.Component { const cli = MatrixClientPeg.get(); const room = cli.getRoom(this.props.mxEvent.getRoomId()); if (room) { - mayRedact = room.currentState.maySendRedactionForEvent(this.props.mxEvent, cli.credentials.userId); const member = room.getMember(sender); if (member) sender = member.name; } @@ -223,15 +221,6 @@ export default class ImageView extends React.Component {
); } - let redactButton; - if (mayRedact) { - redactButton = ( - - { - - ); - } - const rotationDegrees = this.state.rotation + "deg"; const zoomPercentage = this.state.zoom/100; const translatePixelsX = this.state.translationX + "px"; @@ -264,14 +253,7 @@ export default class ImageView extends React.Component {
- { redactButton } - - { - - - { - - + { From 8454a2d44098ed5c3c7f509c673b85e323cc90a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 12:52:08 +0100 Subject: [PATCH 104/916] Remove padding on lightboxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/_common.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/_common.scss b/res/css/_common.scss index 6e9d252659..9296b67375 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -315,6 +315,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { max-width: 100%; max-height: 100%; pointer-events: none; + padding: 0; } .mx_Dialog_header { From 3e408b3fcd6c3c8a201f3cb9affbd9bc651ef7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 12:59:40 +0100 Subject: [PATCH 105/916] Remove unused code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index e1babd8bf1..0e11eda7d0 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -160,18 +160,6 @@ export default class ImageView extends React.Component { }); } - onZoomClick = () => { - if (this.state.zoom <= this.minZoom) { - this.setState({zoom: this.maxZoom}); - } else { - this.setState({ - zoom: this.minZoom, - translationX: 0, - translationY: 0, - }); - } - } - onStartMoving = ev => { ev.stopPropagation(); ev.preventDefault(); From 7cd8f1135bafc69e2e3aab117e19a9379a6936c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 13:02:25 +0100 Subject: [PATCH 106/916] Quit on empty panel click MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 25 +++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 0e11eda7d0..288d3495d3 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -123,19 +123,25 @@ export default class ImageView extends React.Component { }); }; - onRotateCounterClockwiseClick = () => { + onRotateCounterClockwiseClick = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; this.setState({ rotation: rotationDegrees }); }; - onRotateClockwiseClick = () => { + onRotateClockwiseClick = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); const cur = this.state.rotation; const rotationDegrees = (cur + 90) % 360; this.setState({ rotation: rotationDegrees }); }; - onZoomInClick = () => { + onZoomInClick = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); if (this.state.zoom >= this.maxZoom) { this.setState({zoom: this.maxZoom}); return; @@ -146,7 +152,9 @@ export default class ImageView extends React.Component { }); }; - onZoomOutClick = () => { + onZoomOutClick = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); if (this.state.zoom <= this.minZoom) { this.setState({ zoom: this.minZoom, @@ -160,6 +168,10 @@ export default class ImageView extends React.Component { }); } + onPanelClick = (ev) => { + this.props.onFinished(); + } + onStartMoving = ev => { ev.stopPropagation(); ev.preventDefault(); @@ -235,10 +247,7 @@ export default class ImageView extends React.Component { ref={ref => this.focusLock = ref} >
-
-
- { metadata } -
+
From fafb8d43a37b039dcc8e98a0b2d192e2db05e22d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 14:16:58 +0100 Subject: [PATCH 107/916] Fix padding according to the design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 943758b80e..17e2167494 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -64,6 +64,7 @@ limitations under the License. display: flex; justify-content: space-between; align-items: center; + padding: 0 16px 0 32px; } .mx_ImageView_toolbar { From a6bb203a4b2e165b9c5a505213ecfab6b4dc6c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 14:43:33 +0100 Subject: [PATCH 108/916] Redo icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 57 ++++++++++++++++++-- src/components/views/elements/ImageView.js | 63 ++++++++++++++++------ 2 files changed, 99 insertions(+), 21 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 17e2167494..ddc51cf583 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -64,10 +64,10 @@ limitations under the License. display: flex; justify-content: space-between; align-items: center; - padding: 0 16px 0 32px; } .mx_ImageView_toolbar { + padding-right: 16px; right: 0; display: flex; align-items: center; @@ -78,18 +78,65 @@ limitations under the License. align-items: center; } -.mx_ImageView_label { +.mx_ImageView_info_wrapper { + padding-left: 32px; left: 0; text-align: left; display: flex; justify-content: center; - flex-direction: column; - max-width: 240px; + flex-direction: row; color: $lightbox-fg-color; + align-items: center; +} + +.mx_ImageView_info { + padding-left: 12px; } .mx_ImageView_button { - padding-left: 28px; + padding-left: 24px; + display: block; + + &::before { + content: ''; + height: 22px; + width: 22px; + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; + display: block; + background-color: $icon-button-color; + } +} + +.mx_ImageView_button_rotateCW::before { + mask-image: url('$(res)/img/image-view/rotate-cw.svg'); +} + +.mx_ImageView_button_rotateCCW::before { + mask-image: url('$(res)/img/image-view/rotate-ccw.svg'); +} + +.mx_ImageView_button_zoomOut::before { + mask-image: url('$(res)/img/image-view/zoom-out.svg'); +} + +.mx_ImageView_button_zoomIn::before { + mask-image: url('$(res)/img/image-view/zoom-in.svg'); +} + +.mx_ImageView_button_download::before { + mask-image: url('$(res)/img/image-view/download.svg'); +} + +.mx_ImageView_button_close { + padding-left: 32px; + &::before { + width: 32px; + height: 32px; + mask-image: url('$(res)/img/image-view/close.svg'); + background-color: none; + } } .mx_ImageView_metadata { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 288d3495d3..28a087f77d 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -20,11 +20,12 @@ import PropTypes from 'prop-types'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {formatDate} from '../../../DateUtils'; import { _t } from '../../../languageHandler'; -import AccessibleButton from "./AccessibleButton"; +import AccessibleTooltipButton from "./AccessibleTooltipButton"; import Modal from "../../../Modal"; import * as sdk from "../../../index"; import {Key} from "../../../Keyboard"; import FocusLock from "react-focus-lock"; +import MemberAvatar from "../avatars/MemberAvatar"; export default class ImageView extends React.Component { static propTypes = { @@ -214,10 +215,7 @@ export default class ImageView extends React.Component { } metadata = (
- { _t('Uploaded on %(date)s by %(user)s', { - date: formatDate(new Date(this.props.mxEvent.getTs())), - user: sender, - }) } + { formatDate(new Date(this.props.mxEvent.getTs())) }
); } @@ -236,6 +234,8 @@ export default class ImageView extends React.Component { rotate(${rotationDegrees})`, }; + const event = this.props.mxEvent; + return (
+
+ +
+ { event.sender ? event.sender.name : event.getSender() } + { metadata } +
+
- - { - - - { - - - { + + + + + + + + + + +
- - { -
From b068a4c05504c4b5104a97c99f23f899e7dfb301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 16:28:12 +0100 Subject: [PATCH 109/916] Make download into a button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 28a087f77d..8251af1a5d 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -169,6 +169,15 @@ export default class ImageView extends React.Component { }); } + onDownloadClick = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + const a = document.createElement("a"); + a.href = this.props.src; + a.download = this.props.name; + a.click(); + } + onPanelClick = (ev) => { this.props.onFinished(); } @@ -281,13 +290,11 @@ export default class ImageView extends React.Component { title={_t("Zoom in")} onClick={ this.onZoomInClick }> - - + onClick={ this.onDownloadClick }> + Date: Wed, 24 Feb 2021 18:10:50 +0100 Subject: [PATCH 110/916] Add more icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/image-view/more.svg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 res/img/image-view/more.svg diff --git a/res/img/image-view/more.svg b/res/img/image-view/more.svg new file mode 100644 index 0000000000..4f5fa6f9b9 --- /dev/null +++ b/res/img/image-view/more.svg @@ -0,0 +1,3 @@ + + + From 83e1a7a70781c026c62c03dd8f2652125e44da9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 18:13:12 +0100 Subject: [PATCH 111/916] Add more button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 4 ++++ src/components/views/elements/ImageView.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index ddc51cf583..bb99454152 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -129,6 +129,10 @@ limitations under the License. mask-image: url('$(res)/img/image-view/download.svg'); } +.mx_ImageView_button_more::before { + mask-image: url('$(res)/img/image-view/more.svg'); +} + .mx_ImageView_button_close { padding-left: 32px; &::before { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 8251af1a5d..cd104cce37 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -295,6 +295,10 @@ export default class ImageView extends React.Component { title={_t("Download")} onClick={ this.onDownloadClick }> + Date: Wed, 24 Feb 2021 18:24:44 +0100 Subject: [PATCH 112/916] Don't show info if no event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 36 +++++++++++++++------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index cd104cce37..c00b62ecc8 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -245,6 +245,30 @@ export default class ImageView extends React.Component { const event = this.props.mxEvent; + let info; + if (event) { + info = ( +
+ +
+ { event.sender ? event.sender.name : event.getSender() } + { metadata } +
+
+ ); + } else { + // If there is no event - we're viewing an avatar, we set + // an empty div here, since the panel uses space-between + // and we want the same placement of elements + info = ( +
+ ); + } + return (
-
- -
- { event.sender ? event.sender.name : event.getSender() } - { metadata } -
-
+ {info}
Date: Wed, 24 Feb 2021 17:27:56 +0000 Subject: [PATCH 113/916] Upgrade matrix-js-sdk to 9.8.0-rc.1 --- package.json | 2 +- yarn.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index d4f931d811..7b4e577406 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "katex": "^0.12.0", "linkifyjs": "^2.1.9", "lodash": "^4.17.20", - "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", + "matrix-js-sdk": "9.8.0-rc.1", "matrix-widget-api": "^0.1.0-beta.13", "minimist": "^1.2.5", "pako": "^2.0.3", diff --git a/yarn.lock b/yarn.lock index 01450908cc..bef26e27b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5572,9 +5572,10 @@ mathml-tag-names@^2.1.3: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": - version "9.7.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/c82bc35202f93efa2cb9b27b140f83df37c64ab2" +matrix-js-sdk@9.8.0-rc.1: + version "9.8.0-rc.1" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-9.8.0-rc.1.tgz#229122583bec5971f22a423a4a40d749e07602d9" + integrity sha512-Tmo5cdyBBgYcMZMaAavEvtdCsEwr5sYE0RLd6etLOSTxmGRSYpqKvvKQqGsYrogmZYNbx9nNZYYYV2aJkCKcQg== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From 364f24851355fe0b1a716cd5973bbc19cf575d7a Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 24 Feb 2021 17:32:53 +0000 Subject: [PATCH 114/916] Prepare changelog for v3.15.0-rc.1 --- CHANGELOG.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c87f1c62e6..e727adabfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,65 @@ +Changes in [3.15.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.15.0-rc.1) (2021-02-24) +=============================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.14.0...v3.15.0-rc.1) + + * Upgrade to JS SDK 9.8.0-rc.1 + * Translations update from Weblate + [\#5683](https://github.com/matrix-org/matrix-react-sdk/pull/5683) + * Fix object diffing when objects have different keys + [\#5681](https://github.com/matrix-org/matrix-react-sdk/pull/5681) + * Add if it's missing + [\#5673](https://github.com/matrix-org/matrix-react-sdk/pull/5673) + * Add email only if the verification is complete + [\#5629](https://github.com/matrix-org/matrix-react-sdk/pull/5629) + * Fix portrait videocalls + [\#5676](https://github.com/matrix-org/matrix-react-sdk/pull/5676) + * Tweak code block icon positions + [\#5643](https://github.com/matrix-org/matrix-react-sdk/pull/5643) + * Revert "Improve URL preview formatting and image upload thumbnail size" + [\#5677](https://github.com/matrix-org/matrix-react-sdk/pull/5677) + * Fix context menu leaving visible area + [\#5644](https://github.com/matrix-org/matrix-react-sdk/pull/5644) + * Jitsi conferences names, take 3 + [\#5675](https://github.com/matrix-org/matrix-react-sdk/pull/5675) + * Update isUserOnDarkTheme to take use_system_theme in account + [\#5670](https://github.com/matrix-org/matrix-react-sdk/pull/5670) + * Discard some dead code + [\#5665](https://github.com/matrix-org/matrix-react-sdk/pull/5665) + * Add developer tool to explore and edit settings + [\#5664](https://github.com/matrix-org/matrix-react-sdk/pull/5664) + * Use and create new room helpers + [\#5663](https://github.com/matrix-org/matrix-react-sdk/pull/5663) + * Clear message previews when the maximum limit is reached for history + [\#5661](https://github.com/matrix-org/matrix-react-sdk/pull/5661) + * VoIP virtual rooms, mk II + [\#5639](https://github.com/matrix-org/matrix-react-sdk/pull/5639) + * Disable chat effects when reduced motion preferred + [\#5660](https://github.com/matrix-org/matrix-react-sdk/pull/5660) + * Improve URL preview formatting and image upload thumbnail size + [\#5637](https://github.com/matrix-org/matrix-react-sdk/pull/5637) + * Fix border radius when the panel is collapsed + [\#5641](https://github.com/matrix-org/matrix-react-sdk/pull/5641) + * Use a more generic layout setting - useIRCLayout → layout + [\#5571](https://github.com/matrix-org/matrix-react-sdk/pull/5571) + * Remove redundant lockOrigin parameter from usercontent + [\#5657](https://github.com/matrix-org/matrix-react-sdk/pull/5657) + * Set ICE candidate pool size option + [\#5655](https://github.com/matrix-org/matrix-react-sdk/pull/5655) + * Prepare to encrypt when a call arrives + [\#5654](https://github.com/matrix-org/matrix-react-sdk/pull/5654) + * Use config for host signup branding + [\#5650](https://github.com/matrix-org/matrix-react-sdk/pull/5650) + * Use randomly generated conference names for Jitsi + [\#5649](https://github.com/matrix-org/matrix-react-sdk/pull/5649) + * Modified regex to account for an immediate new line after slash commands + [\#5647](https://github.com/matrix-org/matrix-react-sdk/pull/5647) + * Fix codeblock scrollbar color for non-Firefox + [\#5642](https://github.com/matrix-org/matrix-react-sdk/pull/5642) + * Fix codeblock scrollbar colors + [\#5630](https://github.com/matrix-org/matrix-react-sdk/pull/5630) + * Added loading and disabled the button while searching for server + [\#5634](https://github.com/matrix-org/matrix-react-sdk/pull/5634) + Changes in [3.14.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.14.0) (2021-02-16) ===================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.14.0-rc.1...v3.14.0) From 8860c8bfe7b48cbfb230f28e81d48ea7f474e3f4 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 24 Feb 2021 17:32:54 +0000 Subject: [PATCH 115/916] v3.15.0-rc.1 --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 7b4e577406..f49b99831f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.14.0", + "version": "3.15.0-rc.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -27,7 +27,7 @@ "matrix-gen-i18n": "scripts/gen-i18n.js", "matrix-prune-i18n": "scripts/prune-i18n.js" }, - "main": "./src/index.js", + "main": "./lib/index.js", "matrix_src_main": "./src/index.js", "matrix_lib_main": "./lib/index.js", "matrix_lib_typings": "./lib/index.d.ts", @@ -189,5 +189,6 @@ "transformIgnorePatterns": [ "/node_modules/(?!matrix-js-sdk).+$" ] - } + }, + "typings": "./lib/index.d.ts" } From bd5efc7ceb0d1d56ef4b87e8a77ec12b1cc2c11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 19:11:08 +0100 Subject: [PATCH 116/916] Pass permallinkCreator to ImageView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 1 + src/components/views/messages/MImageBody.js | 3 +++ src/components/views/messages/MessageEvent.js | 3 +++ src/components/views/rooms/EventTile.js | 1 + 4 files changed, 8 insertions(+) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index c00b62ecc8..ede4cc9623 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -42,6 +42,7 @@ export default class ImageView extends React.Component { // properties above, which let us use lightboxes to display images which aren't associated // with events. mxEvent: PropTypes.object, + permalinkCreator: PropTypes.object, }; constructor(props) { diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index 8456a5bd09..616f2b1cc8 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -38,6 +38,8 @@ export default class MImageBody extends React.Component { /* the maximum image height to use */ maxImageHeight: PropTypes.number, + + permalinkCreator: PropTypes.object, }; static contextType = MatrixClientContext; @@ -103,6 +105,7 @@ export default class MImageBody extends React.Component { src: httpUrl, name: content.body && content.body.length > 0 ? content.body : _t('Attachment'), mxEvent: this.props.mxEvent, + permalinkCreator: this.props.permalinkCreator, }; if (content.info) { diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js index f93813fe79..daee6558c9 100644 --- a/src/components/views/messages/MessageEvent.js +++ b/src/components/views/messages/MessageEvent.js @@ -44,6 +44,8 @@ export default class MessageEvent extends React.Component { /* the maximum image height to use, if the event is an image */ maxImageHeight: PropTypes.number, + + permalinkCreator: PropTypes.object, }; constructor(props) { @@ -120,6 +122,7 @@ export default class MessageEvent extends React.Component { editState={this.props.editState} onHeightChanged={this.props.onHeightChanged} onMessageAllowed={this.onTileUpdate} + permalinkCreator={this.props.permalinkCreator} />; } } diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index c856919f5a..fb0fa7d6d2 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -968,6 +968,7 @@ export default class EventTile extends React.Component { highlights={this.props.highlights} highlightLink={this.props.highlightLink} showUrlPreview={this.props.showUrlPreview} + permalinkCreator={this.props.permalinkCreator} onHeightChanged={this.props.onHeightChanged} /> { keyRequestInfo } { reactionsRow } From 9312becee56ee0dd5b704e0913b9fc8fc2c1119a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 19:17:33 +0100 Subject: [PATCH 117/916] Add context menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 48 +++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index ede4cc9623..5f77b1ccfa 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, { createRef } from 'react'; import PropTypes from 'prop-types'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {formatDate} from '../../../DateUtils'; @@ -26,6 +26,9 @@ import * as sdk from "../../../index"; import {Key} from "../../../Keyboard"; import FocusLock from "react-focus-lock"; import MemberAvatar from "../avatars/MemberAvatar"; +import {ContextMenuTooltipButton} from "../../../accessibility/context_menu/ContextMenuTooltipButton"; +import MessageContextMenu from "../context_menus/MessageContextMenu"; +import {aboveLeftOf, ContextMenu} from '../../structures/ContextMenu'; export default class ImageView extends React.Component { static propTypes = { @@ -53,9 +56,11 @@ export default class ImageView extends React.Component { translationX: 0, translationY: 0, moving: false, + contextMenuDisplay: false, }; } + contextMenuButton = createRef(); initX = 0; initY = 0; lastX = 0; @@ -179,6 +184,20 @@ export default class ImageView extends React.Component { a.click(); } + onOpenContextMenu = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + this.setState({ + contextMenuDisplay: true, + }); + } + + onCloseContextMenu = () => { + this.setState({ + contextMenuDisplay: false, + }); + } + onPanelClick = (ev) => { this.props.onFinished(); } @@ -210,6 +229,30 @@ export default class ImageView extends React.Component { this.setState({moving: false}); } + renderContextMenu() { + let contextMenu = null; + if (this.state.contextMenuDisplay) { + contextMenu = ( + + + + ); + } + + return ( + + { contextMenu } + + ); + } + render() { const showEventMeta = !!this.props.mxEvent; @@ -313,12 +356,15 @@ export default class ImageView extends React.Component { + {this.renderContextMenu()}
From 81698a2714ff2637d522831c109f4e7a0750fbb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 19:31:19 +0100 Subject: [PATCH 118/916] Fix pointer-events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index bb99454152..cd5f9a247b 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -58,9 +58,7 @@ limitations under the License. .mx_ImageView_panel { width: 100%; height: 68px; - z-index: 1000; align-self: flex-start; - pointer-events: all; display: flex; justify-content: space-between; align-items: center; @@ -76,6 +74,7 @@ limitations under the License. .mx_ImageView_toolbar_buttons { display: flex; align-items: center; + pointer-events: all; } .mx_ImageView_info_wrapper { @@ -91,6 +90,7 @@ limitations under the License. .mx_ImageView_info { padding-left: 12px; + pointer-events: all; } .mx_ImageView_button { From 2021e4e345c7cfc554421509e88253dcddedcf07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 19:33:17 +0100 Subject: [PATCH 119/916] Remove ugly workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 5f77b1ccfa..dc785aabb2 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -131,24 +131,18 @@ export default class ImageView extends React.Component { }; onRotateCounterClockwiseClick = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; this.setState({ rotation: rotationDegrees }); }; onRotateClockwiseClick = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); const cur = this.state.rotation; const rotationDegrees = (cur + 90) % 360; this.setState({ rotation: rotationDegrees }); }; onZoomInClick = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); if (this.state.zoom >= this.maxZoom) { this.setState({zoom: this.maxZoom}); return; @@ -160,8 +154,6 @@ export default class ImageView extends React.Component { }; onZoomOutClick = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); if (this.state.zoom <= this.minZoom) { this.setState({ zoom: this.minZoom, @@ -176,8 +168,6 @@ export default class ImageView extends React.Component { } onDownloadClick = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); const a = document.createElement("a"); a.href = this.props.src; a.download = this.props.name; @@ -185,8 +175,6 @@ export default class ImageView extends React.Component { } onOpenContextMenu = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); this.setState({ contextMenuDisplay: true, }); @@ -198,10 +186,6 @@ export default class ImageView extends React.Component { }); } - onPanelClick = (ev) => { - this.props.onFinished(); - } - onStartMoving = ev => { ev.stopPropagation(); ev.preventDefault(); @@ -324,7 +308,7 @@ export default class ImageView extends React.Component { ref={ref => this.focusLock = ref} >
-
+
{info}
From 7293181552b3bad2b24ec9f9dae80597be82ab04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 19:33:22 +0100 Subject: [PATCH 120/916] i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/i18n/strings/en_EN.json | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 74617e17af..e94e1bbae6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1861,14 +1861,10 @@ "collapse": "collapse", "expand": "expand", "You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)", - "Uploaded on %(date)s by %(user)s": "Uploaded on %(date)s by %(user)s", - "Zoom": "Zoom", + "Rotate Right": "Rotate Right", + "Rotate Left": "Rotate Left", "Zoom out": "Zoom out", "Zoom in": "Zoom in", - "Rotate Left": "Rotate Left", - "Rotate counter-clockwise": "Rotate counter-clockwise", - "Rotate Right": "Rotate Right", - "Rotate clockwise": "Rotate clockwise", "Download": "Download", "Information": "Information", "Language Dropdown": "Language Dropdown", From 983895289c30a9626816ba0ef971cbc01d4eefbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 20:04:25 +0100 Subject: [PATCH 121/916] Update info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 7 +--- src/components/views/elements/ImageView.js | 47 +++++++++------------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index cd5f9a247b..11ef9ec692 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -91,6 +91,8 @@ limitations under the License. .mx_ImageView_info { padding-left: 12px; pointer-events: all; + display: flex; + flex-direction: column; } .mx_ImageView_button { @@ -142,8 +144,3 @@ limitations under the License. background-color: none; } } - -.mx_ImageView_metadata { - font-size: $font-15px; - opacity: 0.5; -} diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index dc785aabb2..1a93b5c3f7 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -18,7 +18,6 @@ limitations under the License. import React, { createRef } from 'react'; import PropTypes from 'prop-types'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; -import {formatDate} from '../../../DateUtils'; import { _t } from '../../../languageHandler'; import AccessibleTooltipButton from "./AccessibleTooltipButton"; import Modal from "../../../Modal"; @@ -29,6 +28,8 @@ import MemberAvatar from "../avatars/MemberAvatar"; import {ContextMenuTooltipButton} from "../../../accessibility/context_menu/ContextMenuTooltipButton"; import MessageContextMenu from "../context_menus/MessageContextMenu"; import {aboveLeftOf, ContextMenu} from '../../structures/ContextMenu'; +import MessageTimestamp from "../messages/MessageTimestamp"; +import SenderProfile from '../messages/SenderProfile'; export default class ImageView extends React.Component { static propTypes = { @@ -240,22 +241,6 @@ export default class ImageView extends React.Component { render() { const showEventMeta = !!this.props.mxEvent; - let metadata; - if (showEventMeta) { - // Figure out the sender, defaulting to mxid - let sender = this.props.mxEvent.getSender(); - const cli = MatrixClientPeg.get(); - const room = cli.getRoom(this.props.mxEvent.getRoomId()); - if (room) { - const member = room.getMember(sender); - if (member) sender = member.name; - } - - metadata = (
- { formatDate(new Date(this.props.mxEvent.getTs())) } -
); - } - const rotationDegrees = this.state.rotation + "deg"; const zoomPercentage = this.state.zoom/100; const translatePixelsX = this.state.translationX + "px"; @@ -271,20 +256,28 @@ export default class ImageView extends React.Component { rotate(${rotationDegrees})`, }; - const event = this.props.mxEvent; - let info; - if (event) { + if (showEventMeta) { + const mxEvent = this.props.mxEvent; + + const senderName = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); + const messageTimestamp = ( + + ); + const avatar = ( + + ); + info = (
- + {avatar}
- { event.sender ? event.sender.name : event.getSender() } - { metadata } + {senderName} + {messageTimestamp}
); From 6008a6f9fa09060f2c6851e61a5d416d7a28469d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 24 Feb 2021 20:07:41 +0100 Subject: [PATCH 122/916] Use showTwelveHourTimestamps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 1a93b5c3f7..cbe5b0592f 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -29,7 +29,7 @@ import {ContextMenuTooltipButton} from "../../../accessibility/context_menu/Cont import MessageContextMenu from "../context_menus/MessageContextMenu"; import {aboveLeftOf, ContextMenu} from '../../structures/ContextMenu'; import MessageTimestamp from "../messages/MessageTimestamp"; -import SenderProfile from '../messages/SenderProfile'; +import SettingsStore from "../../../settings/SettingsStore"; export default class ImageView extends React.Component { static propTypes = { @@ -259,10 +259,11 @@ export default class ImageView extends React.Component { let info; if (showEventMeta) { const mxEvent = this.props.mxEvent; + const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps"); const senderName = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); const messageTimestamp = ( - + ); const avatar = ( Date: Wed, 24 Feb 2021 20:14:12 +0100 Subject: [PATCH 123/916] Make permalink clickable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 27 +++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index cbe5b0592f..073b9dddbd 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -30,6 +30,8 @@ import MessageContextMenu from "../context_menus/MessageContextMenu"; import {aboveLeftOf, ContextMenu} from '../../structures/ContextMenu'; import MessageTimestamp from "../messages/MessageTimestamp"; import SettingsStore from "../../../settings/SettingsStore"; +import {formatTime} from "../../../DateUtils"; +import dis from '../../../dispatcher/dispatcher'; export default class ImageView extends React.Component { static propTypes = { @@ -187,6 +189,18 @@ export default class ImageView extends React.Component { }); } + onPermalinkClicked = e => { + // This allows the permalink to be opened in a new tab/window or copied as + // matrix.to, but also for it to enable routing within Element when clicked. + e.preventDefault(); + dis.dispatch({ + action: 'view_room', + event_id: this.props.mxEvent.getId(), + highlighted: true, + room_id: this.props.mxEvent.getRoomId(), + }); + }; + onStartMoving = ev => { ev.stopPropagation(); ev.preventDefault(); @@ -260,10 +274,21 @@ export default class ImageView extends React.Component { if (showEventMeta) { const mxEvent = this.props.mxEvent; const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps"); + let permalink = "#"; + if (this.props.permalinkCreator) { + permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId()); + } const senderName = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); const messageTimestamp = ( - + + + + ); const avatar = ( Date: Wed, 24 Feb 2021 20:41:20 +0100 Subject: [PATCH 124/916] Remove rounded border --- res/css/views/elements/_ImageView.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 11ef9ec692..f904d31330 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -48,8 +48,6 @@ limitations under the License. min-width: 100px; min-height: 100px; - border-radius: 8px; - &:hover { cursor: grab; } From d58c17ff3bfb75c61329979b7ea8d3ccd69bfe70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 07:50:53 +0100 Subject: [PATCH 125/916] Show grabbing cursor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 4 ---- src/components/views/elements/ImageView.js | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index f904d31330..d864ad9adf 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -47,10 +47,6 @@ limitations under the License. max-height: 70vh; min-width: 100px; min-height: 100px; - - &:hover { - cursor: grab; - } } .mx_ImageView_panel { diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 073b9dddbd..51e700c481 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -264,6 +264,7 @@ export default class ImageView extends React.Component { * we would apply the translation to an already rotated * image causing it translate in the wrong direction. */ const style = { + cursor: this.state.moving ? "grabbing" : "grab", transform: `translateX(${translatePixelsX}) translateY(${translatePixelsY}) scale(${zoomPercentage}) From fe8e90f92063d8efd6f1b880e6f70e994cb84f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 07:51:38 +0100 Subject: [PATCH 126/916] Change comment styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 51e700c481..d91b7c8aba 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -72,8 +72,8 @@ export default class ImageView extends React.Component { maxZoom = 300; componentDidMount() { - /* We have to use addEventListener() because the listener - * needs to be passive in order to work with Chromium */ + // We have to use addEventListener() because the listener + // needs to be passive in order to work with Chromium this.focusLock.addEventListener('wheel', this.onWheel, { passive: false }); } @@ -259,10 +259,10 @@ export default class ImageView extends React.Component { const zoomPercentage = this.state.zoom/100; const translatePixelsX = this.state.translationX + "px"; const translatePixelsY = this.state.translationY + "px"; - /* The order of the values is important! - * First, we translate and only then we rotate, otherwise - * we would apply the translation to an already rotated - * image causing it translate in the wrong direction. */ + // The order of the values is important! + // First, we translate and only then we rotate, otherwise + // we would apply the translation to an already rotated + // image causing it translate in the wrong direction. const style = { cursor: this.state.moving ? "grabbing" : "grab", transform: `translateX(${translatePixelsX}) From 5a9e1a14822e5f6ecb5e0fed186e9f021921286c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 08:10:54 +0100 Subject: [PATCH 127/916] Fix close icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 7 ++++--- res/img/image-view/close.svg | 5 ++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index d864ad9adf..9b77f70262 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -90,7 +90,7 @@ limitations under the License. } .mx_ImageView_button { - padding-left: 24px; + margin-left: 24px; display: block; &::before { @@ -130,11 +130,12 @@ limitations under the License. } .mx_ImageView_button_close { - padding-left: 32px; + border-radius: 100%; + background: #21262C; &::before { width: 32px; height: 32px; mask-image: url('$(res)/img/image-view/close.svg'); - background-color: none; + mask-size: 40%; } } diff --git a/res/img/image-view/close.svg b/res/img/image-view/close.svg index f849425264..d603b7f5cc 100644 --- a/res/img/image-view/close.svg +++ b/res/img/image-view/close.svg @@ -1,4 +1,3 @@ - - - + + From 83de84972ed143750f0f633f962398818643207b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 08:13:27 +0100 Subject: [PATCH 128/916] Close onPermalinkClicked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index d91b7c8aba..f7826d45fe 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -199,6 +199,7 @@ export default class ImageView extends React.Component { highlighted: true, room_id: this.props.mxEvent.getRoomId(), }); + this.props.onFinished(); }; onStartMoving = ev => { From b18622efe492860153bf60a92ea56ce49dbd4480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 08:20:34 +0100 Subject: [PATCH 129/916] Show full date MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 6 +++--- src/components/views/messages/MessageTimestamp.js | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index f7826d45fe..5acd36bde0 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -30,7 +30,7 @@ import MessageContextMenu from "../context_menus/MessageContextMenu"; import {aboveLeftOf, ContextMenu} from '../../structures/ContextMenu'; import MessageTimestamp from "../messages/MessageTimestamp"; import SettingsStore from "../../../settings/SettingsStore"; -import {formatTime} from "../../../DateUtils"; +import {formatFullDate} from "../../../DateUtils"; import dis from '../../../dispatcher/dispatcher'; export default class ImageView extends React.Component { @@ -286,9 +286,9 @@ export default class ImageView extends React.Component { - + ); diff --git a/src/components/views/messages/MessageTimestamp.js b/src/components/views/messages/MessageTimestamp.js index 199a6f47ce..26b8096c4f 100644 --- a/src/components/views/messages/MessageTimestamp.js +++ b/src/components/views/messages/MessageTimestamp.js @@ -23,13 +23,18 @@ export default class MessageTimestamp extends React.Component { static propTypes = { ts: PropTypes.number.isRequired, showTwelveHour: PropTypes.bool, + showFullDate: PropTypes.bool, }; render() { const date = new Date(this.props.ts); return ( - { formatTime(date, this.props.showTwelveHour) } + { + this.props.showFullDate ? + formatFullDate(date, this.props.showTwelveHour) : + formatTime(date, this.props.showTwelveHour) + } ); } From 273753a42a9a122b1f6044c2840ef4830896781d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 08:25:34 +0100 Subject: [PATCH 130/916] Fix hex formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 9b77f70262..45968ddaa5 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -131,7 +131,7 @@ limitations under the License. .mx_ImageView_button_close { border-radius: 100%; - background: #21262C; + background: #21262c; &::before { width: 32px; height: 32px; From a4130cb7f35ae8325b2b68b457246e23db02158a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 10:23:27 +0100 Subject: [PATCH 131/916] Revert trash icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/feather-customised/trash.custom.svg | 89 ++------------------- 1 file changed, 7 insertions(+), 82 deletions(-) diff --git a/res/img/feather-customised/trash.custom.svg b/res/img/feather-customised/trash.custom.svg index 589bb0a4e5..70eeaf35cd 100644 --- a/res/img/feather-customised/trash.custom.svg +++ b/res/img/feather-customised/trash.custom.svg @@ -1,82 +1,7 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - + + + + + + + \ No newline at end of file From 7fc375805628a6b7a11777b70f8c06011e132b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 10:23:59 +0100 Subject: [PATCH 132/916] Add newline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/feather-customised/trash.custom.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/img/feather-customised/trash.custom.svg b/res/img/feather-customised/trash.custom.svg index 70eeaf35cd..dc1985ddb2 100644 --- a/res/img/feather-customised/trash.custom.svg +++ b/res/img/feather-customised/trash.custom.svg @@ -4,4 +4,4 @@ - \ No newline at end of file + From 984b4372db4e6c56638dc0f6d419acb5b4e8a324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 10:25:24 +0100 Subject: [PATCH 133/916] Remove trash red icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/trash-red.svg | 89 ------------------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 res/img/trash-red.svg diff --git a/res/img/trash-red.svg b/res/img/trash-red.svg deleted file mode 100644 index 0b1d201d2e..0000000000 --- a/res/img/trash-red.svg +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - From 4f3fe3d236640f744eec9f966b0a0260d698f469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:09:52 +0100 Subject: [PATCH 134/916] Add comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/messages/MImageBody.js | 1 + src/components/views/messages/MessageEvent.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index 616f2b1cc8..b1d5995121 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -39,6 +39,7 @@ export default class MImageBody extends React.Component { /* the maximum image height to use */ maxImageHeight: PropTypes.number, + /* the permalinkCreator */ permalinkCreator: PropTypes.object, }; diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js index daee6558c9..fe0bb19fda 100644 --- a/src/components/views/messages/MessageEvent.js +++ b/src/components/views/messages/MessageEvent.js @@ -45,6 +45,7 @@ export default class MessageEvent extends React.Component { /* the maximum image height to use, if the event is an image */ maxImageHeight: PropTypes.number, + /* the permalinkCreator */ permalinkCreator: PropTypes.object, }; From d0dea91e92986cbcb07d53888a4014cb69a0ba6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:16:40 +0100 Subject: [PATCH 135/916] contextMenuDisplay -> contextMenuDisplayed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 5acd36bde0..bbadad9778 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -59,7 +59,7 @@ export default class ImageView extends React.Component { translationX: 0, translationY: 0, moving: false, - contextMenuDisplay: false, + contextMenuDisplayed: false, }; } @@ -179,13 +179,13 @@ export default class ImageView extends React.Component { onOpenContextMenu = (ev) => { this.setState({ - contextMenuDisplay: true, + contextMenuDisplayed: true, }); } onCloseContextMenu = () => { this.setState({ - contextMenuDisplay: false, + contextMenuDisplayed: false, }); } @@ -231,7 +231,7 @@ export default class ImageView extends React.Component { renderContextMenu() { let contextMenu = null; - if (this.state.contextMenuDisplay) { + if (this.state.contextMenuDisplayed) { contextMenu = ( Date: Thu, 25 Feb 2021 11:19:50 +0100 Subject: [PATCH 136/916] ZOOM shouldn't be a part of the class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index bbadad9778..6e9066ccbf 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -33,6 +33,9 @@ import SettingsStore from "../../../settings/SettingsStore"; import {formatFullDate} from "../../../DateUtils"; import dis from '../../../dispatcher/dispatcher'; +const MIN_ZOOM = 100; +const MAX_ZOOM = 300; + export default class ImageView extends React.Component { static propTypes = { src: PropTypes.string.isRequired, // the source of the image being displayed @@ -68,8 +71,6 @@ export default class ImageView extends React.Component { initY = 0; lastX = 0; lastY = 0; - minZoom = 100; - maxZoom = 300; componentDidMount() { // We have to use addEventListener() because the listener @@ -94,16 +95,16 @@ export default class ImageView extends React.Component { ev.preventDefault(); const newZoom =this.state.zoom - ev.deltaY; - if (newZoom <= this.minZoom) { + if (newZoom <= MIN_ZOOM) { this.setState({ - zoom: this.minZoom, + zoom: MIN_ZOOM, translationX: 0, translationY: 0, }); return; } - if (newZoom >= this.maxZoom) { - this.setState({zoom: this.maxZoom}); + if (newZoom >= MAX_ZOOM) { + this.setState({zoom: MAX_ZOOM}); return; } @@ -146,8 +147,8 @@ export default class ImageView extends React.Component { }; onZoomInClick = (ev) => { - if (this.state.zoom >= this.maxZoom) { - this.setState({zoom: this.maxZoom}); + if (this.state.zoom >= MAX_ZOOM) { + this.setState({zoom: MAX_ZOOM}); return; } @@ -157,9 +158,9 @@ export default class ImageView extends React.Component { }; onZoomOutClick = (ev) => { - if (this.state.zoom <= this.minZoom) { + if (this.state.zoom <= MIN_ZOOM) { this.setState({ - zoom: this.minZoom, + zoom: MIN_ZOOM, translationX: 0, translationY: 0, }); From 80ce4da9b66a2d2cf0044bb4ef59f47410a72a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:21:04 +0100 Subject: [PATCH 137/916] Remove onRedactClick MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 6e9066ccbf..dd5ad56a4a 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -113,27 +113,6 @@ export default class ImageView extends React.Component { }); } - onRedactClick = () => { - const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog"); - Modal.createTrackedDialog('Confirm Redact Dialog', 'Image View', ConfirmRedactDialog, { - onFinished: (proceed) => { - if (!proceed) return; - this.props.onFinished(); - MatrixClientPeg.get().redactEvent( - this.props.mxEvent.getRoomId(), this.props.mxEvent.getId(), - ).catch(function(e) { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - // display error message stating you couldn't delete this. - const code = e.errcode || e.statusCode; - Modal.createTrackedDialog('You cannot delete this image.', '', ErrorDialog, { - title: _t('Error'), - description: _t('You cannot delete this image. (%(code)s)', {code: code}), - }); - }); - }, - }); - }; - onRotateCounterClockwiseClick = (ev) => { const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; From 4c377ae037fc8bbc1a8a320092550470de8a0b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:23:14 +0100 Subject: [PATCH 138/916] Consistent evs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index dd5ad56a4a..c14b8c60bb 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -113,19 +113,19 @@ export default class ImageView extends React.Component { }); } - onRotateCounterClockwiseClick = (ev) => { + onRotateCounterClockwiseClick = () => { const cur = this.state.rotation; const rotationDegrees = (cur - 90) % 360; this.setState({ rotation: rotationDegrees }); }; - onRotateClockwiseClick = (ev) => { + onRotateClockwiseClick = () => { const cur = this.state.rotation; const rotationDegrees = (cur + 90) % 360; this.setState({ rotation: rotationDegrees }); }; - onZoomInClick = (ev) => { + onZoomInClick = () => { if (this.state.zoom >= MAX_ZOOM) { this.setState({zoom: MAX_ZOOM}); return; @@ -136,7 +136,7 @@ export default class ImageView extends React.Component { }); }; - onZoomOutClick = (ev) => { + onZoomOutClick = () => { if (this.state.zoom <= MIN_ZOOM) { this.setState({ zoom: MIN_ZOOM, @@ -150,14 +150,14 @@ export default class ImageView extends React.Component { }); } - onDownloadClick = (ev) => { + onDownloadClick = () => { const a = document.createElement("a"); a.href = this.props.src; a.download = this.props.name; a.click(); } - onOpenContextMenu = (ev) => { + onOpenContextMenu = () => { this.setState({ contextMenuDisplayed: true, }); @@ -169,10 +169,10 @@ export default class ImageView extends React.Component { }); } - onPermalinkClicked = e => { + onPermalinkClicked = (ev) => { // This allows the permalink to be opened in a new tab/window or copied as // matrix.to, but also for it to enable routing within Element when clicked. - e.preventDefault(); + ev.preventDefault(); dis.dispatch({ action: 'view_room', event_id: this.props.mxEvent.getId(), @@ -182,7 +182,7 @@ export default class ImageView extends React.Component { this.props.onFinished(); }; - onStartMoving = ev => { + onStartMoving = (ev) => { ev.stopPropagation(); ev.preventDefault(); @@ -191,7 +191,7 @@ export default class ImageView extends React.Component { this.initY = ev.pageY - this.lastY; } - onMoving = ev => { + onMoving = (ev) => { ev.stopPropagation(); ev.preventDefault(); @@ -205,7 +205,7 @@ export default class ImageView extends React.Component { }); } - onEndMoving = ev => { + onEndMoving = () => { this.setState({moving: false}); } From 436a17bcc91feca49de9c454646a7efeda1404e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:26:34 +0100 Subject: [PATCH 139/916] Remove imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index c14b8c60bb..ea424d9925 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -17,11 +17,8 @@ limitations under the License. import React, { createRef } from 'react'; import PropTypes from 'prop-types'; -import {MatrixClientPeg} from "../../../MatrixClientPeg"; import { _t } from '../../../languageHandler'; import AccessibleTooltipButton from "./AccessibleTooltipButton"; -import Modal from "../../../Modal"; -import * as sdk from "../../../index"; import {Key} from "../../../Keyboard"; import FocusLock from "react-focus-lock"; import MemberAvatar from "../avatars/MemberAvatar"; From dc283241aa08d9cdc24bc35552c8703b38348b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:28:42 +0100 Subject: [PATCH 140/916] Remove wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 6 ------ src/components/views/elements/ImageView.js | 2 -- 2 files changed, 8 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 45968ddaa5..4b786a244b 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -22,12 +22,6 @@ limitations under the License. display: flex; width: 100%; height: 100%; -} - -.mx_ImageView_content { - width: 100%; - - display: flex; flex-direction: column; } diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index ea424d9925..21b16ed89d 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -305,7 +305,6 @@ export default class ImageView extends React.Component { className="mx_ImageView" ref={ref => this.focusLock = ref} > -
{info}
@@ -363,7 +362,6 @@ export default class ImageView extends React.Component { onMouseLeave={this.onEndMoving} />
-
); } From 1955fff08cfb11761d9328718852270367269719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:50:50 +0100 Subject: [PATCH 141/916] CSS cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_ImageView.scss | 31 +++------ src/components/views/elements/ImageView.js | 76 +++++++++++----------- 2 files changed, 47 insertions(+), 60 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 4b786a244b..626878b03e 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -34,10 +34,9 @@ limitations under the License. } .mx_ImageView_image { - object-fit: contain; pointer-events: all; - max-width: 100vw; + max-width: 70vw; max-height: 70vh; min-width: 100px; min-height: 100px; @@ -46,43 +45,33 @@ limitations under the License. .mx_ImageView_panel { width: 100%; height: 68px; - align-self: flex-start; display: flex; justify-content: space-between; align-items: center; } -.mx_ImageView_toolbar { - padding-right: 16px; - right: 0; - display: flex; - align-items: center; -} - -.mx_ImageView_toolbar_buttons { - display: flex; - align-items: center; - pointer-events: all; -} - .mx_ImageView_info_wrapper { + pointer-events: all; padding-left: 32px; - left: 0; - text-align: left; display: flex; - justify-content: center; flex-direction: row; - color: $lightbox-fg-color; align-items: center; + color: $lightbox-fg-color; } .mx_ImageView_info { padding-left: 12px; - pointer-events: all; display: flex; flex-direction: column; } +.mx_ImageView_toolbar { + padding-right: 16px; + pointer-events: all; + display: flex; + align-items: center; +} + .mx_ImageView_button { margin-left: 24px; display: block; diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 21b16ed89d..15bb25d473 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -308,45 +308,43 @@ export default class ImageView extends React.Component {
{info}
-
- - - - - - - - - - - - - - {this.renderContextMenu()} -
+ + + + + + + + + + + + + + {this.renderContextMenu()}
From fc32ceade74c072585c58905e153027f9ab766f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 11:57:39 +0100 Subject: [PATCH 142/916] i18n --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e94e1bbae6..982ee44452 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1860,7 +1860,6 @@ "Please create a new issue on GitHub so that we can investigate this bug.": "Please create a new issue on GitHub so that we can investigate this bug.", "collapse": "collapse", "expand": "expand", - "You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)", "Rotate Right": "Rotate Right", "Rotate Left": "Rotate Left", "Zoom out": "Zoom out", From 7030c636f07c32c3616eeddba64e0a0c8dfc6587 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Feb 2021 15:18:54 +0000 Subject: [PATCH 143/916] Initial Space Store for keeping track of space hierarchies from sync --- src/@types/global.d.ts | 2 + src/stores/SpaceStore.tsx | 462 ++++++++++++++++++ .../notifications/SpaceNotificationState.ts | 82 ++++ 3 files changed, 546 insertions(+) create mode 100644 src/stores/SpaceStore.tsx create mode 100644 src/stores/notifications/SpaceNotificationState.ts diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 28f22780a2..4aa6df5488 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -38,6 +38,7 @@ import UserActivity from "../UserActivity"; import {ModalWidgetStore} from "../stores/ModalWidgetStore"; import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore"; import VoipUserMapper from "../VoipUserMapper"; +import {SpaceStoreClass} from "../stores/SpaceStore"; declare global { interface Window { @@ -68,6 +69,7 @@ declare global { mxUserActivity: UserActivity; mxModalWidgetStore: ModalWidgetStore; mxVoipUserMapper: VoipUserMapper; + mxSpaceStore: SpaceStoreClass; } interface Document { diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx new file mode 100644 index 0000000000..d675879138 --- /dev/null +++ b/src/stores/SpaceStore.tsx @@ -0,0 +1,462 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +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 {throttle, sortBy} from "lodash"; +import {EventType} from "matrix-js-sdk/src/@types/event"; +import {Room} from "matrix-js-sdk/src/models/room"; +import {MatrixEvent} from "matrix-js-sdk/src/models/event"; + +import {AsyncStoreWithClient} from "./AsyncStoreWithClient"; +import defaultDispatcher from "../dispatcher/dispatcher"; +import {ActionPayload} from "../dispatcher/payloads"; +import RoomListStore from "./room-list/RoomListStore"; +import SettingsStore from "../settings/SettingsStore"; +import DMRoomMap from "../utils/DMRoomMap"; +import {FetchRoomFn} from "./notifications/ListNotificationState"; +import {SpaceNotificationState} from "./notifications/SpaceNotificationState"; +import {RoomNotificationStateStore} from "./notifications/RoomNotificationStateStore"; +import {DefaultTagID} from "./room-list/models"; +import {EnhancedMap, mapDiff} from "../utils/maps"; +import {setHasDiff} from "../utils/sets"; +import {objectDiff} from "../utils/objects"; +import {arrayHasDiff} from "../utils/arrays"; + +type SpaceKey = string | symbol; + +interface IState {} + +const ACTIVE_SPACE_LS_KEY = "mx_active_space"; + +export const HOME_SPACE = Symbol("home-space"); + +export const UPDATE_TOP_LEVEL_SPACES = Symbol("top-level-spaces"); +export const UPDATE_SELECTED_SPACE = Symbol("selected-space"); +// Space Room ID/HOME_SPACE will be emitted when a Space's children change + +const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces, rooms] + return arr.reduce((result, room: Room) => { + result[room.isSpaceRoom() ? 0 : 1].push(room); + return result; + }, [[], []]); +}; + +const getOrder = (ev: MatrixEvent): string | null => { + const content = ev.getContent(); + if (typeof content.order === "string" && Array.from(content.order).every((c: string) => { + const charCode = c.charCodeAt(0); + return charCode >= 0x20 && charCode <= 0x7F; + })) { + return content.order; + } + return null; +} + +const getRoomFn: FetchRoomFn = (room: Room) => { + return RoomNotificationStateStore.instance.getRoomState(room); +}; + +export class SpaceStoreClass extends AsyncStoreWithClient { + constructor() { + super(defaultDispatcher, {}); + } + + // The spaces representing the roots of the various tree-like hierarchies + private rootSpaces: Room[] = []; + // The list of rooms not present in any currently joined spaces + private orphanedRooms = new Set(); + // Map from room ID to set of spaces which list it as a child + private parentMap = new EnhancedMap>(); + // Map from space key to SpaceNotificationState instance representing that space + private notificationStateMap = new Map(); + // Map from space key to Set of room IDs that should be shown as part of that space's filter + private spaceFilteredRooms = new Map>(); + // The space currently selected in the Space Panel - if null then `Home` is selected + private _activeSpace?: Room = null; + + public get spacePanelSpaces(): Room[] { + return this.rootSpaces; + } + + public get activeSpace(): Room | null { + return this._activeSpace || null; + } + + public setActiveSpace(space: Room | null) { + if (space === this.activeSpace) return; + + this._activeSpace = space; + this.emit(UPDATE_SELECTED_SPACE, this.activeSpace); + + // persist space selected + if (space) { + window.localStorage.setItem(ACTIVE_SPACE_LS_KEY, space.roomId); + } else { + window.localStorage.removeItem(ACTIVE_SPACE_LS_KEY); + } + } + + public addRoomToSpace(space: Room, roomId: string, via: string[], autoJoin = false) { + return this.matrixClient.sendStateEvent(space.roomId, EventType.SpaceChild, { + via, + auto_join: autoJoin, + }, roomId); + } + + private getChildren(spaceId: string): Room[] { + const room = this.matrixClient?.getRoom(spaceId); + const childEvents = room?.currentState.getStateEvents(EventType.SpaceChild).filter(ev => ev.getContent()?.via); + return sortBy(childEvents, getOrder) + .map(ev => this.matrixClient.getRoom(ev.getStateKey())) + .filter(room => room?.getMyMembership() === "join") || []; + } + + public getChildRooms(spaceId: string): Room[] { + return this.getChildren(spaceId).filter(r => !r.isSpaceRoom()); + } + + public getChildSpaces(spaceId: string): Room[] { + return this.getChildren(spaceId).filter(r => r.isSpaceRoom()); + } + + public getParents(roomId: string, canonicalOnly = false): Room[] { + const room = this.matrixClient?.getRoom(roomId); + return room?.currentState.getStateEvents(EventType.SpaceParent) + .filter(ev => { + const content = ev.getContent(); + if (!content?.via) return false; + // TODO apply permissions check to verify that the parent mapping is valid + if (canonicalOnly && !content?.canonical) return false; + return true; + }) + .map(ev => this.matrixClient.getRoom(ev.getStateKey())) + .filter(Boolean) || []; + } + + public getCanonicalParent(roomId: string): Room | null { + const parents = this.getParents(roomId, true); + return sortBy(parents, r => r.roomId)?.[0] || null; + } + + public getSpaces = () => { + return this.matrixClient.getRooms().filter(r => r.isSpaceRoom() && r.getMyMembership() === "join"); + }; + + public getSpaceFilteredRoomIds = (space: Room | null): Set => { + return this.spaceFilteredRooms.get(space?.roomId || HOME_SPACE) || new Set(); + }; + + public rebuild = throttle(() => { // exported for tests + const visibleRooms = this.matrixClient.getVisibleRooms(); + + // Sort spaces by room ID to force the loop breaking to be deterministic + const spaces = sortBy(this.getSpaces(), space => space.roomId); + const unseenChildren = new Set([...visibleRooms, ...spaces]); + + const backrefs = new EnhancedMap>(); + + // TODO handle cleaning up links when a Space is removed + spaces.forEach(space => { + const children = this.getChildren(space.roomId); + children.forEach(child => { + unseenChildren.delete(child); + + backrefs.getOrCreate(child.roomId, new Set()).add(space.roomId); + }); + }); + + const [rootSpaces, orphanedRooms] = partitionSpacesAndRooms(Array.from(unseenChildren)); + + // untested algorithm to handle full-cycles + const detachedNodes = new Set(spaces); + + const markTreeChildren = (rootSpace: Room, unseen: Set) => { + const stack = [rootSpace]; + while (stack.length) { + const op = stack.pop(); + unseen.delete(op); + this.getChildSpaces(op.roomId).forEach(space => { + if (unseen.has(space)) { + stack.push(space); + } + }); + } + }; + + rootSpaces.forEach(rootSpace => { + markTreeChildren(rootSpace, detachedNodes); + }); + + // Handle spaces forming fully cyclical relationships. + // In order, assume each detachedNode is a root unless it has already + // been claimed as the child of prior detached node. + // Work from a copy of the detachedNodes set as it will be mutated as part of this operation. + Array.from(detachedNodes).forEach(detachedNode => { + if (!detachedNodes.has(detachedNode)) return; + // declare this detached node a new root, find its children, without ever looping back to it + detachedNodes.delete(detachedNode); + rootSpaces.push(detachedNode); + markTreeChildren(detachedNode, detachedNodes); + + // TODO only consider a detached node a root space if it has no *parents other than the ones forming cycles + }); + + // TODO neither of these handle an A->B->C->A with an additional C->D + // detachedNodes.forEach(space => { + // rootSpaces.push(space); + // }); + + this.orphanedRooms = new Set(orphanedRooms); + this.rootSpaces = rootSpaces; + this.parentMap = backrefs; + + // if the currently selected space no longer exists, remove its selection + if (this._activeSpace && detachedNodes.has(this._activeSpace)) { + this.setActiveSpace(null); + } + + this.onRoomsUpdate(); // TODO only do this if a change has happened + this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces); + }, 100, {trailing: true, leading: true}); + + onSpaceUpdate = () => { + this.rebuild(); + } + + private showInHomeSpace = (room: Room) => { + return !this.parentMap.get(room.roomId)?.size // put all orphaned rooms in the Home Space + || DMRoomMap.shared().getUserIdForRoomId(room.roomId) // put all DMs in the Home Space + || RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite) // show all favourites + }; + + // Update a given room due to its tag changing (e.g DM-ness or Fav-ness) + // This can only change whether it shows up in the HOME_SPACE or not + private onRoomUpdate = (room: Room) => { + if (this.showInHomeSpace(room)) { + this.spaceFilteredRooms.get(HOME_SPACE)?.add(room.roomId); + this.emit(HOME_SPACE); + } else if (!this.orphanedRooms.has(room.roomId)) { + this.spaceFilteredRooms.get(HOME_SPACE)?.delete(room.roomId); + this.emit(HOME_SPACE); + } + }; + + private onRoomsUpdate = throttle(() => { + // TODO resolve some updates as deltas + const visibleRooms = this.matrixClient.getVisibleRooms(); + + const oldFilteredRooms = this.spaceFilteredRooms; + this.spaceFilteredRooms = new Map(); + + // put all invites (rooms & spaces) in the Home Space + const invites = this.matrixClient.getRooms().filter(r => r.getMyMembership() === "invite"); + this.spaceFilteredRooms.set(HOME_SPACE, new Set(invites.map(room => room.roomId))); + + visibleRooms.forEach(room => { + if (this.showInHomeSpace(room)) { + this.spaceFilteredRooms.get(HOME_SPACE).add(room.roomId); + } + }); + + this.rootSpaces.forEach(s => { + // traverse each space tree in DFS to build up the supersets as you go up, + // reusing results from like subtrees. + const fn = (spaceId: string, parentPath: Set): Set => { + if (parentPath.has(spaceId)) return; // prevent cycles + + // reuse existing results if multiple similar branches exist + if (this.spaceFilteredRooms.has(spaceId)) { + return this.spaceFilteredRooms.get(spaceId); + } + + const [childSpaces, childRooms] = partitionSpacesAndRooms(this.getChildren(spaceId)); + const roomIds = new Set(childRooms.map(r => r.roomId)); + const space = this.matrixClient?.getRoom(spaceId); + + // Add relevant DMs + space?.getJoinedMembers().forEach(member => { + DMRoomMap.shared().getDMRoomsForUserId(member.userId).forEach(roomId => { + roomIds.add(roomId); + }); + }); + + const newPath = new Set(parentPath).add(spaceId); + childSpaces.forEach(childSpace => { + fn(childSpace.roomId, newPath)?.forEach(roomId => { + roomIds.add(roomId); + }); + }); + this.spaceFilteredRooms.set(spaceId, roomIds); + return roomIds; + }; + + fn(s.roomId, new Set()); + }); + + const diff = mapDiff(oldFilteredRooms, this.spaceFilteredRooms); + // filter out keys which changed by reference only by checking whether the sets differ + const changed = diff.changed.filter(k => setHasDiff(oldFilteredRooms.get(k), this.spaceFilteredRooms.get(k))); + [...diff.added, ...diff.removed, ...changed].forEach(k => { + this.emit(k); + }); + + this.spaceFilteredRooms.forEach((roomIds, s) => { + // Update NotificationStates + const rooms = this.matrixClient.getRooms().filter(room => roomIds.has(room.roomId)); + this.getNotificationState(s)?.setRooms(rooms); + }); + }, 100, {trailing: true, leading: true}); + + private onRoom = (room: Room) => { + if (room?.isSpaceRoom()) { + this.onSpaceUpdate(); + this.emit(room.roomId); + } else { + // this.onRoomUpdate(room); + this.onRoomsUpdate(); + } + }; + + private onRoomState = (ev: MatrixEvent) => { + const room = this.matrixClient.getRoom(ev.getRoomId()); + if (!room) return; + + if (ev.getType() === EventType.SpaceChild && room.isSpaceRoom()) { + this.onSpaceUpdate(); + this.emit(room.roomId); + } else if (ev.getType() === EventType.SpaceParent) { + // TODO rebuild the space parent and not the room - check permissions? + // TODO confirm this after implementing parenting behaviour + if (room.isSpaceRoom()) { + this.onSpaceUpdate(); + } else { + this.onRoomUpdate(room); + } + this.emit(room.roomId); + } + }; + + private onRoomAccountData = (ev: MatrixEvent, room: Room, lastEvent: MatrixEvent) => { + if (ev.getType() === EventType.Tag && !room.isSpaceRoom()) { + // If the room was in favourites and now isn't or the opposite then update its position in the trees + if (!!ev.getContent()[DefaultTagID.Favourite] !== !!lastEvent.getContent()[DefaultTagID.Favourite]) { + this.onRoomUpdate(room); + } + } + } + + private onAccountData = (ev: MatrixEvent, lastEvent: MatrixEvent) => { + if (ev.getType() === EventType.Direct) { + const lastContent = lastEvent.getContent(); + const content = ev.getContent(); + + const diff = objectDiff>(lastContent, content); + // filter out keys which changed by reference only by checking whether the sets differ + const changed = diff.changed.filter(k => arrayHasDiff(lastContent[k], content[k])); + // DM tag changes, refresh relevant rooms + new Set([...diff.added, ...diff.removed, ...changed]).forEach(roomId => { + const room = this.matrixClient?.getRoom(roomId); + if (room) { + this.onRoomUpdate(room); + } + }); + } + }; + + protected async onNotReady() { + if (!SettingsStore.getValue("feature_spaces")) return; + if (this.matrixClient) { + this.matrixClient.removeListener("Room", this.onRoom); + this.matrixClient.removeListener("Room.myMembership", this.onRoom); + this.matrixClient.removeListener("RoomState.events", this.onRoomState); + this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData); + this.matrixClient.removeListener("accountData", this.onAccountData); + } + await this.reset({}); + } + + protected async onReady() { + if (!SettingsStore.getValue("feature_spaces")) return; + this.matrixClient.on("Room", this.onRoom); + this.matrixClient.on("Room.myMembership", this.onRoom); + this.matrixClient.on("RoomState.events", this.onRoomState); + this.matrixClient.on("Room.accountData", this.onRoomAccountData); + this.matrixClient.on("accountData", this.onAccountData); + + await this.onSpaceUpdate(); // trigger an initial update + + // restore selected state from last session if any and still valid + const lastSpaceId = window.localStorage.getItem(ACTIVE_SPACE_LS_KEY); + if (lastSpaceId) { + const space = this.rootSpaces.find(s => s.roomId === lastSpaceId); + if (space) { + this.setActiveSpace(space); + } + } + } + + protected async onAction(payload: ActionPayload) { + switch (payload.action) { + case "view_room": { + const room = this.matrixClient?.getRoom(payload.room_id); + + if (room?.getMyMembership() === "join") { + if (room.isSpaceRoom()) { + this.setActiveSpace(room); + } else if (!this.spaceFilteredRooms.get(this._activeSpace?.roomId || HOME_SPACE).has(room.roomId)) { + // TODO maybe reverse these first 2 clauses once space panel active is fixed + let parent = this.rootSpaces.find(s => this.spaceFilteredRooms.get(s.roomId)?.has(room.roomId)); + if (!parent) { + parent = this.getCanonicalParent(room.roomId); + } + if (!parent) { + const parents = Array.from(this.parentMap.get(room.roomId) || []); + parent = parents.find(p => this.matrixClient.getRoom(p)); + } + if (parent) { + this.setActiveSpace(parent); + } + } + } + break; + } + case "after_leave_room": + if (this._activeSpace && payload.room_id === this._activeSpace.roomId) { + this.setActiveSpace(null); + } + break; + } + } + + public getNotificationState(key: SpaceKey): SpaceNotificationState { + if (this.notificationStateMap.has(key)) { + return this.notificationStateMap.get(key); + } + + const state = new SpaceNotificationState(key, getRoomFn); + this.notificationStateMap.set(key, state); + return state; + } +} + +export default class SpaceStore { + private static internalInstance = new SpaceStoreClass(); + + public static get instance(): SpaceStoreClass { + return SpaceStore.internalInstance; + } +} + +window.mxSpaceStore = SpaceStore.instance; diff --git a/src/stores/notifications/SpaceNotificationState.ts b/src/stores/notifications/SpaceNotificationState.ts new file mode 100644 index 0000000000..61a9701a07 --- /dev/null +++ b/src/stores/notifications/SpaceNotificationState.ts @@ -0,0 +1,82 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +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 { Room } from "matrix-js-sdk/src/models/room"; + +import { NotificationColor } from "./NotificationColor"; +import { arrayDiff } from "../../utils/arrays"; +import { RoomNotificationState } from "./RoomNotificationState"; +import { NOTIFICATION_STATE_UPDATE, NotificationState } from "./NotificationState"; +import { FetchRoomFn } from "./ListNotificationState"; + +export class SpaceNotificationState extends NotificationState { + private rooms: Room[] = []; + private states: { [spaceId: string]: RoomNotificationState } = {}; + + constructor(private spaceId: string | symbol, private getRoomFn: FetchRoomFn) { + super(); + } + + public get symbol(): string { + return null; // This notification state doesn't support symbols + } + + public setRooms(rooms: Room[]) { + const oldRooms = this.rooms; + const diff = arrayDiff(oldRooms, rooms); + this.rooms = rooms; + for (const oldRoom of diff.removed) { + const state = this.states[oldRoom.roomId]; + if (!state) continue; // We likely just didn't have a badge (race condition) + delete this.states[oldRoom.roomId]; + state.off(NOTIFICATION_STATE_UPDATE, this.onRoomNotificationStateUpdate); + } + for (const newRoom of diff.added) { + const state = this.getRoomFn(newRoom); + state.on(NOTIFICATION_STATE_UPDATE, this.onRoomNotificationStateUpdate); + this.states[newRoom.roomId] = state; + } + + this.calculateTotalState(); + } + + public destroy() { + super.destroy(); + for (const state of Object.values(this.states)) { + state.off(NOTIFICATION_STATE_UPDATE, this.onRoomNotificationStateUpdate); + } + this.states = {}; + } + + private onRoomNotificationStateUpdate = () => { + this.calculateTotalState(); + }; + + private calculateTotalState() { + const snapshot = this.snapshot(); + + this._count = 0; + this._color = NotificationColor.None; + for (const state of Object.values(this.states)) { + this._count += state.count; + this._color = Math.max(this.color, state.color); + } + + // finally, publish an update if needed + this.emitIfUpdated(snapshot); + } +} + From ad85764a8e3d1025c76db29e9f53ba690ac2eb4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:23:32 +0100 Subject: [PATCH 144/916] Fix timeline expansion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 1 + res/css/structures/_RoomView.scss | 1 + 2 files changed, 2 insertions(+) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index f05f24d0d7..9597083e9c 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -19,6 +19,7 @@ limitations under the License. flex-direction: row; min-width: 0; height: 100%; + justify-content: space-between; } .mx_MainSplit > .mx_RightPanel_ResizeWrapper { diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index cd8c640132..5240a0650f 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -121,6 +121,7 @@ limitations under the License. position: relative; //for .mx_RoomView_auxPanel_fullHeight display: flex; flex-direction: column; + width: 100%; } .mx_RoomView_body { From aa4ec9fca1a65202306ee77d705b15f1782de188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:27:52 +0100 Subject: [PATCH 145/916] Make $droptarget-bg-color more opaque MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/themes/light/css/_light.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 1c89d83c01..ea7b0472e0 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -68,7 +68,7 @@ $groupFilterPanel-bg-color: rgba(232, 232, 232, 0.77); $plinth-bg-color: $secondary-accent-color; // used by RoomDropTarget -$droptarget-bg-color: rgba(255,255,255,0.5); +$droptarget-bg-color: rgba(255,255,255,0.95); // used by AddressSelector $selected-color: $secondary-accent-color; From 8551855a5626d06e6feda6f849f50b26af85a194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:30:14 +0100 Subject: [PATCH 146/916] Add $droptarget-bg-color to the dark theme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/themes/dark/css/_dark.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index a878aa3cdd..f6f415ce70 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -42,6 +42,9 @@ $preview-bar-bg-color: $header-panel-bg-color; $groupFilterPanel-bg-color: rgba(38, 39, 43, 0.82); $inverted-bg-color: $base-color; +// used by RoomDropTarget +$droptarget-bg-color: rgba(21,25,30,0.95); + // used by AddressSelector $selected-color: $room-highlight-color; From a3001f77e46e7c3ac3479a70eb56db312c6f1361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:30:39 +0100 Subject: [PATCH 147/916] Remove rounded corners of the drop area MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 3 --- 1 file changed, 3 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 5240a0650f..e80ac062b6 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -30,9 +30,6 @@ limitations under the License. pointer-events: none; - border-top-left-radius: 10px; - border-top-right-radius: 10px; - background-color: $droptarget-bg-color; position: absolute; From 26b70b62280b7f4fa21c6287faf20a309a399abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:32:04 +0100 Subject: [PATCH 148/916] Remove label background MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index e80ac062b6..8ba31fac20 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -42,11 +42,6 @@ limitations under the License. .mx_RoomView_fileDropTargetLabel { position: absolute; - - border-radius: 10px; - padding: 10px; - - background-color: $menu-bg-color; } .mx_RoomView_auxPanel { From 6a7340e8be3844881b1c114d74687983b9c0ba20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 18:46:48 +0100 Subject: [PATCH 149/916] Use new upload icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/upload-big.svg | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/res/img/upload-big.svg b/res/img/upload-big.svg index 6099c2e976..9a6a265fdb 100644 --- a/res/img/upload-big.svg +++ b/res/img/upload-big.svg @@ -1,19 +1,3 @@ - - - - icons_upload_drop - Created with bin/sketchtool. - - - - - - - - - - - - - + + From 1c48804d96c3cdda150e38e2d520a2268dd5728e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 19:28:08 +0100 Subject: [PATCH 150/916] Remove unnecessary class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 4 ---- src/components/views/rooms/AuxPanel.tsx | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 8ba31fac20..28d8d1e196 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -40,10 +40,6 @@ limitations under the License. align-items: center; } -.mx_RoomView_fileDropTargetLabel { - position: absolute; -} - .mx_RoomView_auxPanel { min-width: 0px; width: 100%; diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 7966643084..543a50d59f 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -156,7 +156,7 @@ export default class AuxPanel extends React.Component { if (this.props.draggingFile) { fileDropTarget = (
-
+

{ _t("Drop file here to upload") } From 43e1144ae7ca7eff08c1666b4a179ba828d41432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 19:36:55 +0100 Subject: [PATCH 151/916] Don't use TintableSVG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This seemed to have caused a little lag and it was unnecessary Signed-off-by: Šimon Brandner --- src/components/views/rooms/AuxPanel.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 543a50d59f..c9150d588f 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -150,14 +150,12 @@ export default class AuxPanel extends React.Component { } render() { - const TintableSvg = sdk.getComponent("elements.TintableSvg"); - let fileDropTarget = null; if (this.props.draggingFile) { fileDropTarget = (
- +
{ _t("Drop file here to upload") }
From 7277c285a9326928555e05766ee3ce33603de18d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 20:10:38 +0100 Subject: [PATCH 152/916] Fix weird crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/rooms/AuxPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index c9150d588f..cc3408476c 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -155,7 +155,7 @@ export default class AuxPanel extends React.Component { fileDropTarget = (
- +
{ _t("Drop file here to upload") }
From 49ea9a4788243346b20fcf5b4b79f46a7c3a80ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 25 Feb 2021 20:10:58 +0100 Subject: [PATCH 153/916] Remove sdk import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/rooms/AuxPanel.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index cc3408476c..59ea8e237a 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -17,7 +17,6 @@ limitations under the License. import React from 'react'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import { Room } from 'matrix-js-sdk/src/models/room' -import * as sdk from '../../../index'; import dis from "../../../dispatcher/dispatcher"; import * as ObjectUtils from '../../../ObjectUtils'; import AppsDrawer from './AppsDrawer'; From 563620484df6fa79f666ef72f414838cf0e342f1 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 25 Feb 2021 14:39:20 -0500 Subject: [PATCH 154/916] Support replying with a message command Signed-off-by: Robin Townsend --- src/SlashCommands.tsx | 28 +++++++----- .../views/rooms/SendMessageComposer.js | 44 +++++++++++++------ 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 6b5f261374..06468c135e 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -20,6 +20,7 @@ limitations under the License. import * as React from 'react'; +import { ContentHelpers } from 'matrix-js-sdk'; import {MatrixClientPeg} from './MatrixClientPeg'; import dis from './dispatcher/dispatcher'; import * as sdk from './index'; @@ -126,10 +127,10 @@ export class Command { return this.getCommand() + " " + this.args; } - run(roomId: string, args: string, cmd: string) { + run(roomId: string, args: string) { // if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me` if (!this.runFn) return reject(_t("Command error")); - return this.runFn.bind(this)(roomId, args, cmd); + return this.runFn.bind(this)(roomId, args); } getUsage() { @@ -163,7 +164,7 @@ export const Commands = [ if (args) { message = message + ' ' + args; } - return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); + return success(ContentHelpers.makeTextMessage(message)); }, category: CommandCategories.messages, }), @@ -176,7 +177,7 @@ export const Commands = [ if (args) { message = message + ' ' + args; } - return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); + return success(ContentHelpers.makeTextMessage(message)); }, category: CommandCategories.messages, }), @@ -189,7 +190,7 @@ export const Commands = [ if (args) { message = message + ' ' + args; } - return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); + return success(ContentHelpers.makeTextMessage(message)); }, category: CommandCategories.messages, }), @@ -202,7 +203,7 @@ export const Commands = [ if (args) { message = message + ' ' + args; } - return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); + return success(ContentHelpers.makeTextMessage(message)); }, category: CommandCategories.messages, }), @@ -211,7 +212,7 @@ export const Commands = [ args: '', description: _td('Sends a message as plain text, without interpreting it as markdown'), runFn: function(roomId, messages) { - return success(MatrixClientPeg.get().sendTextMessage(roomId, messages)); + return success(ContentHelpers.makeTextMessage(messages)); }, category: CommandCategories.messages, }), @@ -220,7 +221,7 @@ export const Commands = [ args: '', description: _td('Sends a message as html, without interpreting it as markdown'), runFn: function(roomId, messages) { - return success(MatrixClientPeg.get().sendHtmlMessage(roomId, messages, messages)); + return success(ContentHelpers.makeHtmlMessage(messages, messages)); }, category: CommandCategories.messages, }), @@ -966,7 +967,7 @@ export const Commands = [ args: '', runFn: function(roomId, args) { if (!args) return reject(this.getUserId()); - return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, textToHtmlRainbow(args))); + return success(ContentHelpers.makeHtmlMessage(args, textToHtmlRainbow(args))); }, category: CommandCategories.messages, }), @@ -976,7 +977,7 @@ export const Commands = [ args: '', runFn: function(roomId, args) { if (!args) return reject(this.getUserId()); - return success(MatrixClientPeg.get().sendHtmlEmote(roomId, args, textToHtmlRainbow(args))); + return success(ContentHelpers.makeHtmlEmote(args, textToHtmlRainbow(args))); }, category: CommandCategories.messages, }), @@ -1201,10 +1202,13 @@ export function parseCommandString(input: string) { * processing the command, or 'promise' if a request was sent out. * Returns null if the input didn't match a command. */ -export function getCommand(roomId: string, input: string) { +export function getCommand(input: string) { const {cmd, args} = parseCommandString(input); if (CommandMap.has(cmd) && CommandMap.get(cmd).isEnabled()) { - return () => CommandMap.get(cmd).run(roomId, args, cmd); + return { + cmd: CommandMap.get(cmd), + args, + }; } } diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 62c474e417..d1482c0df6 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -33,7 +33,7 @@ import ReplyThread from "../elements/ReplyThread"; import {parseEvent} from '../../../editor/deserialize'; import {findEditableEvent} from '../../../utils/EventUtils'; import SendHistoryManager from "../../../SendHistoryManager"; -import {getCommand} from '../../../SlashCommands'; +import {CommandCategories, getCommand} from '../../../SlashCommands'; import * as sdk from '../../../index'; import Modal from '../../../Modal'; import {_t, _td} from '../../../languageHandler'; @@ -287,15 +287,22 @@ export default class SendMessageComposer extends React.Component { } return text + part.text; }, ""); - return [getCommand(this.props.room.roomId, commandText), commandText]; + const {cmd, args} = getCommand(commandText); + return [cmd, args, commandText]; } - async _runSlashCommand(fn) { - const cmd = fn(); - let error = cmd.error; - if (cmd.promise) { + async _runSlashCommand(cmd, args) { + const result = cmd.run(this.props.room.roomId, args); + let messageContent; + let error = result.error; + if (result.promise) { try { - await cmd.promise; + if (cmd.category === CommandCategories.messages) { + // The command returns a modified message that we need to pass on + messageContent = await result.promise; + } else { + await result.promise; + } } catch (err) { error = err; } @@ -304,7 +311,7 @@ export default class SendMessageComposer extends React.Component { console.error("Command failure: %s", error); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); // assume the error is a server error when the command is async - const isServerError = !!cmd.promise; + const isServerError = !!result.promise; const title = isServerError ? _td("Server error") : _td("Command error"); let errText; @@ -322,6 +329,7 @@ export default class SendMessageComposer extends React.Component { }); } else { console.log("Command success."); + if (messageContent) return messageContent; } } @@ -330,13 +338,22 @@ export default class SendMessageComposer extends React.Component { return; } + const replyToEvent = this.props.replyToEvent; let shouldSend = true; + let content; if (!containsEmote(this.model) && this._isSlashCommand()) { - const [cmd, commandText] = this._getSlashCommand(); + const [cmd, args, commandText] = this._getSlashCommand(); if (cmd) { - shouldSend = false; - this._runSlashCommand(cmd); + if (cmd.category === CommandCategories.messages) { + content = await this._runSlashCommand(cmd, args); + if (replyToEvent) { + addReplyToMessageContent(content, replyToEvent, this.props.permalinkCreator); + } + } else { + this._runSlashCommand(cmd, args); + shouldSend = false; + } } else { // ask the user if their unknown command should be sent as a message const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); @@ -371,11 +388,12 @@ export default class SendMessageComposer extends React.Component { this._sendQuickReaction(); } - const replyToEvent = this.props.replyToEvent; if (shouldSend) { const startTime = CountlyAnalytics.getTimestamp(); const {roomId} = this.props.room; - const content = createMessageContent(this.model, this.props.permalinkCreator, replyToEvent); + if (!content) { + content = createMessageContent(this.model, this.props.permalinkCreator, replyToEvent); + } // don't bother sending an empty message if (!content.body.trim()) return; From 1a7f9091b4fc41eac6d5cc1b71d1dbe61f5d7f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 07:51:03 +0100 Subject: [PATCH 155/916] Animate icon size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 12 ++++++++++++ src/components/views/rooms/AuxPanel.tsx | 10 +++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 28d8d1e196..d5caee5e8b 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -36,10 +36,22 @@ limitations under the License. z-index: 3000; display: flex; + flex-direction: column; justify-content: center; align-items: center; } +@keyframes mx_RoomView_fileDropTarget_image_animation { + from {width: 0px;} + to {width: 32px;} +} + +.mx_RoomView_fileDropTarget_image { + animation: mx_RoomView_fileDropTarget_image_animation; + animation-duration: 0.5s; + margin-bottom: 16px; +} + .mx_RoomView_auxPanel { min-width: 0px; width: 100%; diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 59ea8e237a..b3ef8c3cc8 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -153,11 +153,11 @@ export default class AuxPanel extends React.Component { if (this.props.draggingFile) { fileDropTarget = (
-
- -
- { _t("Drop file here to upload") } -
+ + { _t("Drop file here to upload") }
); } From f0c26846c75559e5be013eb25ebe5f54b4f6e264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 08:11:58 +0100 Subject: [PATCH 156/916] Fix formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index d5caee5e8b..2c3fb2b32b 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -42,8 +42,8 @@ limitations under the License. } @keyframes mx_RoomView_fileDropTarget_image_animation { - from {width: 0px;} - to {width: 32px;} + from {width: 0px;} + to {width: 32px;} } .mx_RoomView_fileDropTarget_image { From 172cc01f7d3dcba08235a621cff118efe3e76d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 08:12:10 +0100 Subject: [PATCH 157/916] Add background animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 2c3fb2b32b..5870e107c6 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -20,6 +20,12 @@ limitations under the License. flex-direction: column; } + +@keyframes mx_RoomView_fileDropTarget_animation { + from {opacity: 0;} + to {opacity: 0.95;} +} + .mx_RoomView_fileDropTarget { min-width: 0px; width: 100%; @@ -30,7 +36,8 @@ limitations under the License. pointer-events: none; - background-color: $droptarget-bg-color; + background-color: $primary-bg-color; + opacity: 0.95; position: absolute; z-index: 3000; @@ -39,6 +46,9 @@ limitations under the License. flex-direction: column; justify-content: center; align-items: center; + + animation: mx_RoomView_fileDropTarget_animation; + animation-duration: 0.5s; } @keyframes mx_RoomView_fileDropTarget_image_animation { From 3e0558f4d97bc618cc8e6d71f411370e894d642f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 08:12:38 +0100 Subject: [PATCH 158/916] Remove droptarget colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/themes/dark/css/_dark.scss | 3 --- res/themes/light/css/_light.scss | 3 --- 2 files changed, 6 deletions(-) diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index f6f415ce70..a878aa3cdd 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -42,9 +42,6 @@ $preview-bar-bg-color: $header-panel-bg-color; $groupFilterPanel-bg-color: rgba(38, 39, 43, 0.82); $inverted-bg-color: $base-color; -// used by RoomDropTarget -$droptarget-bg-color: rgba(21,25,30,0.95); - // used by AddressSelector $selected-color: $room-highlight-color; diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index ea7b0472e0..c92e491ca2 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -67,9 +67,6 @@ $groupFilterPanel-bg-color: rgba(232, 232, 232, 0.77); // used by RoomDirectory permissions $plinth-bg-color: $secondary-accent-color; -// used by RoomDropTarget -$droptarget-bg-color: rgba(255,255,255,0.95); - // used by AddressSelector $selected-color: $secondary-accent-color; From 49ea83edb93dfa0e6f572aeac002adaaa0370fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 08:14:27 +0100 Subject: [PATCH 159/916] i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 5bbbdf60b5..9af8ccc172 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1381,7 +1381,6 @@ "Remove %(phone)s?": "Remove %(phone)s?", "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.", "Phone Number": "Phone Number", - "Drop File Here": "Drop File Here", "Drop file here to upload": "Drop file here to upload", "This user has not verified all of their sessions.": "This user has not verified all of their sessions.", "You have not verified this user.": "You have not verified this user.", From e90ae2ea7596bff850cf4014c1109f93234132b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 08:18:05 +0100 Subject: [PATCH 160/916] Delint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 5870e107c6..5e8d84ff32 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -22,8 +22,12 @@ limitations under the License. @keyframes mx_RoomView_fileDropTarget_animation { - from {opacity: 0;} - to {opacity: 0.95;} + from { + opacity: 0; + } + to { + opacity: 0.95; + } } .mx_RoomView_fileDropTarget { @@ -52,8 +56,12 @@ limitations under the License. } @keyframes mx_RoomView_fileDropTarget_image_animation { - from {width: 0px;} - to {width: 32px;} + from { + width: 0px; + } + to { + width: 32px; + } } .mx_RoomView_fileDropTarget_image { From 819a0b013fda927bea3197e458741f4f4f85a271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 11:08:31 +0100 Subject: [PATCH 161/916] min-width MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to allow the container to be smaller Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 5e8d84ff32..28591ad7a4 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -140,6 +140,7 @@ limitations under the License. display: flex; flex-direction: column; width: 100%; + min-width: 0; } .mx_RoomView_body { From 0d6a9fce67d24f441a62e140a2a73f669b959cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 11:12:14 +0100 Subject: [PATCH 162/916] Remove weird styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/RoomView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index ff09af454e..42eafe5bdc 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2058,7 +2058,7 @@ export default class RoomView extends React.Component { appsShown={this.state.showApps} /> -
+
{auxPanel}
From 11c5aa02d290739fff31bfa8365fe76562032594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 11:19:45 +0100 Subject: [PATCH 163/916] Remove mx_RoomView_container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 1 + res/css/structures/_RoomView.scss | 8 ------- src/components/structures/RoomView.tsx | 30 ++++++++++++-------------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 9597083e9c..5fa62e921d 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -20,6 +20,7 @@ limitations under the License. min-width: 0; height: 100%; justify-content: space-between; + min-height: 0; } .mx_MainSplit > .mx_RightPanel_ResizeWrapper { diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 28591ad7a4..b3dab5f992 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -135,14 +135,6 @@ limitations under the License. height: 50px; } -.mx_RoomView_container { - position: relative; //for .mx_RoomView_auxPanel_fullHeight - display: flex; - flex-direction: column; - width: 100%; - min-width: 0; -} - .mx_RoomView_body { display: flex; flex-direction: column; diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 42eafe5bdc..be2f81a176 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2058,24 +2058,22 @@ export default class RoomView extends React.Component { appsShown={this.state.showApps} /> -
+
{auxPanel} -
-
- {topUnreadMessagesBar} - {jumpToBottom} - {messagePanel} - {searchResultsPanel} -
-
-
-
- {statusBar} -
-
- {previewBar} - {messageComposer} +
+ {topUnreadMessagesBar} + {jumpToBottom} + {messagePanel} + {searchResultsPanel}
+
+
+
+ {statusBar} +
+
+ {previewBar} + {messageComposer}
From 9a5ba072ba0f551dc618cdf8bfb7afba004d01fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 11:23:58 +0100 Subject: [PATCH 164/916] Fix auxPanel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/RoomView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index be2f81a176..5b79f23e0b 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2059,8 +2059,8 @@ export default class RoomView extends React.Component { />
- {auxPanel}
+ {auxPanel} {topUnreadMessagesBar} {jumpToBottom} {messagePanel} From 3bed37463fea1ff1a7c86ef5fe9a0a123e06008f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 26 Feb 2021 11:38:05 +0100 Subject: [PATCH 165/916] Remove unnecessary code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_MainSplit.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 5fa62e921d..2d9ea2729c 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -19,7 +19,6 @@ limitations under the License. flex-direction: row; min-width: 0; height: 100%; - justify-content: space-between; min-height: 0; } From f21aedc6cf38d741de183fa1d158d7f7de673699 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 26 Feb 2021 10:23:09 +0000 Subject: [PATCH 166/916] Add Space Panel with Room List filtering --- res/css/_components.scss | 1 + res/css/structures/_LeftPanel.scss | 22 +- res/css/structures/_SpacePanel.scss | 237 ++++++++++++++++++ res/img/element-icons/expand-space-panel.svg | 4 + res/themes/light/css/_mods.scss | 4 + src/components/structures/LeftPanel.tsx | 23 +- src/components/views/avatars/RoomAvatar.tsx | 2 +- src/components/views/spaces/SpacePanel.tsx | 212 ++++++++++++++++ .../views/spaces/SpaceTreeLevel.tsx | 184 ++++++++++++++ src/i18n/strings/en_EN.json | 4 +- src/stores/room-list/RoomListStore.ts | 23 +- src/stores/room-list/SpaceWatcher.ts | 39 +++ src/stores/room-list/algorithms/Algorithm.ts | 3 + .../room-list/filters/SpaceFilterCondition.ts | 69 +++++ 14 files changed, 796 insertions(+), 31 deletions(-) create mode 100644 res/css/structures/_SpacePanel.scss create mode 100644 res/img/element-icons/expand-space-panel.svg create mode 100644 src/components/views/spaces/SpacePanel.tsx create mode 100644 src/components/views/spaces/SpaceTreeLevel.tsx create mode 100644 src/stores/room-list/SpaceWatcher.ts create mode 100644 src/stores/room-list/filters/SpaceFilterCondition.ts diff --git a/res/css/_components.scss b/res/css/_components.scss index 006bac09c9..29b5262826 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -27,6 +27,7 @@ @import "./structures/_RoomView.scss"; @import "./structures/_ScrollPanel.scss"; @import "./structures/_SearchBox.scss"; +@import "./structures/_SpacePanel.scss"; @import "./structures/_TabbedView.scss"; @import "./structures/_ToastContainer.scss"; @import "./structures/_UploadBar.scss"; diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index 168590502d..f1f27014ee 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -15,6 +15,7 @@ limitations under the License. */ $groupFilterPanelWidth: 56px; // only applies in this file, used for calculations +$roomListCollapsedWidth: 68px; .mx_LeftPanel { background-color: $roomlist-bg-color; @@ -37,18 +38,12 @@ $groupFilterPanelWidth: 56px; // only applies in this file, used for calculation // GroupFilterPanel handles its own CSS } - &:not(.mx_LeftPanel_hasGroupFilterPanel) { - .mx_LeftPanel_roomListContainer { - width: 100%; - } - } - // Note: The 'room list' in this context is actually everything that isn't the tag // panel, such as the menu options, breadcrumbs, filtering, etc .mx_LeftPanel_roomListContainer { - width: calc(100% - $groupFilterPanelWidth); background-color: $roomlist-bg-color; - + flex: 1 0 0; + min-width: 0; // Create another flexbox (this time a column) for the room list components display: flex; flex-direction: column; @@ -168,17 +163,10 @@ $groupFilterPanelWidth: 56px; // only applies in this file, used for calculation // These styles override the defaults for the minimized (66px) layout &.mx_LeftPanel_minimized { min-width: unset; - - // We have to forcefully set the width to override the resizer's style attribute. - &.mx_LeftPanel_hasGroupFilterPanel { - width: calc(68px + $groupFilterPanelWidth) !important; - } - &:not(.mx_LeftPanel_hasGroupFilterPanel) { - width: 68px !important; - } + width: unset !important; .mx_LeftPanel_roomListContainer { - width: 68px; + width: $roomListCollapsedWidth; .mx_LeftPanel_userHeader { flex-direction: row; diff --git a/res/css/structures/_SpacePanel.scss b/res/css/structures/_SpacePanel.scss new file mode 100644 index 0000000000..8de85f95ef --- /dev/null +++ b/res/css/structures/_SpacePanel.scss @@ -0,0 +1,237 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +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. +*/ + +$topLevelHeight: 32px; +$nestedHeight: 24px; +$gutterSize: 21px; +$activeStripeSize: 4px; + +.mx_SpacePanel { + flex: 0 0 auto; + background-color: $groupFilterPanel-bg-color; + padding: 0; + margin: 0; + + // Create another flexbox so the Panel fills the container + display: flex; + flex-direction: column; + overflow-y: auto; + + .mx_SpacePanel_spaceTreeWrapper { + flex: 1; + } + + .mx_SpacePanel_toggleCollapse { + flex: 0 0 auto; + width: 40px; + height: 40px; + mask-position: center; + mask-size: 32px; + mask-repeat: no-repeat; + margin-left: $gutterSize; + margin-bottom: 12px; + background-color: $roomlist-header-color; + mask-image: url('$(res)/img/element-icons/expand-space-panel.svg'); + + &.expanded { + transform: scaleX(-1); + } + } + + ul { + margin: 0; + list-style: none; + padding: 0; + padding-left: 16px; + } + + .mx_AutoHideScrollbar { + padding: 16px 12px 16px 0; + } + + .mx_SpaceButton_toggleCollapse { + cursor: pointer; + } + + .mx_SpaceItem { + position: relative; + } + + .mx_SpaceItem.collapsed { + & > .mx_SpaceButton > .mx_SpaceButton_toggleCollapse { + transform: rotate(-90deg); + } + + & > .mx_SpaceTreeLevel { + display: none; + } + } + + .mx_SpaceItem:not(.hasSubSpaces) > .mx_SpaceButton { + margin-left: $gutterSize; + + &.mx_SpaceButton_active { + &::before { + left: -$gutterSize; + } + } + } + + .mx_SpaceButton { + border-radius: 8px; + position: relative; + margin-bottom: 16px; + display: flex; + align-items: center; + + .mx_SpaceButton_name { + flex: 1; + margin-left: 8px; + white-space: nowrap; + display: block; + max-width: 150px; + text-overflow: ellipsis; + overflow: hidden; + + font-size: $font-14px; + line-height: $font-18px; + } + + .mx_SpaceButton_toggleCollapse { + width: calc($gutterSize - $activeStripeSize); + margin-left: 1px; + height: 20px; + mask-position: center; + mask-size: 20px; + mask-repeat: no-repeat; + background-color: $roomlist-header-color; + mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); + } + + &.mx_SpaceButton_active { + &::before { + position: absolute; + content: ''; + width: $activeStripeSize; + top: 0; + left: 0; + bottom: 0; + background-color: $accent-color; + border-radius: 0 4px 4px 0; + } + } + + .mx_SpaceButton_avatarPlaceholder { + width: $topLevelHeight; + min-width: $topLevelHeight; + height: $topLevelHeight; + border-radius: 8px; + + &::before { + position: absolute; + content: ''; + width: $topLevelHeight; + height: $topLevelHeight; + top: 0; + left: 0; + mask-position: center; + mask-repeat: no-repeat; + mask-size: 18px; + } + } + + &.mx_SpaceButton_home .mx_SpaceButton_avatarPlaceholder { + background-color: #ffffff; + + &::before { + background-color: #3f3d3d; + mask-image: url('$(res)/img/element-icons/home.svg'); + } + } + + &.mx_SpaceButton_newCancel .mx_SpaceButton_avatarPlaceholder { + background-color: $icon-button-color; + + &::before { + transform: rotate(45deg); + } + } + + .mx_BaseAvatar_image { + border-radius: 8px; + } + } + + .mx_SpacePanel_badgeContainer { + height: 16px; + // don't set width so that it takes no space when there is no badge to show + margin: auto 0; // vertically align + + // Create a flexbox to make aligning dot badges easier + display: flex; + align-items: center; + + .mx_NotificationBadge { + margin: 0 2px; // centering + } + + .mx_NotificationBadge_dot { + // make the smaller dot occupy the same width for centering + margin-left: 7px; + margin-right: 7px; + } + } + + &.collapsed { + .mx_SpaceButton { + .mx_SpacePanel_badgeContainer { + position: absolute; + right: -8px; + top: -4px; + } + } + } + + &:not(.collapsed) { + .mx_SpaceButton:hover, + .mx_SpaceButton:focus-within, + .mx_SpaceButton_hasMenuOpen { + // Hide the badge container on hover because it'll be a menu button + .mx_SpacePanel_badgeContainer { + width: 0; + height: 0; + display: none; + } + } + } + + /* root space buttons are bigger and not indented */ + & > .mx_AutoHideScrollbar { + & > .mx_SpaceButton { + height: $topLevelHeight; + + &.mx_SpaceButton_active::before { + height: $topLevelHeight; + } + } + + & > ul { + padding-left: 0; + } + } +} diff --git a/res/img/element-icons/expand-space-panel.svg b/res/img/element-icons/expand-space-panel.svg new file mode 100644 index 0000000000..11232acd58 --- /dev/null +++ b/res/img/element-icons/expand-space-panel.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/themes/light/css/_mods.scss b/res/themes/light/css/_mods.scss index 30aaeedf8f..fbca58dfb1 100644 --- a/res/themes/light/css/_mods.scss +++ b/res/themes/light/css/_mods.scss @@ -16,6 +16,10 @@ backdrop-filter: blur($groupFilterPanel-background-blur-amount); } + .mx_SpacePanel { + backdrop-filter: blur($groupFilterPanel-background-blur-amount); + } + .mx_LeftPanel .mx_LeftPanel_roomListContainer { backdrop-filter: blur($roomlist-background-blur-amount); } diff --git a/src/components/structures/LeftPanel.tsx b/src/components/structures/LeftPanel.tsx index 4445ff3ff8..82dd9443cc 100644 --- a/src/components/structures/LeftPanel.tsx +++ b/src/components/structures/LeftPanel.tsx @@ -39,6 +39,7 @@ import { OwnProfileStore } from "../../stores/OwnProfileStore"; import { MatrixClientPeg } from "../../MatrixClientPeg"; import RoomListNumResults from "../views/rooms/RoomListNumResults"; import LeftPanelWidget from "./LeftPanelWidget"; +import SpacePanel from "../views/spaces/SpacePanel"; interface IProps { isMinimized: boolean; @@ -388,12 +389,19 @@ export default class LeftPanel extends React.Component { } public render(): React.ReactNode { - const groupFilterPanel = !this.state.showGroupFilterPanel ? null : ( -
- - {SettingsStore.getValue("feature_custom_tags") ? : null} -
- ); + let leftLeftPanel; + // Currently TagPanel.enableTagPanel is disabled when Legacy Communities are disabled so for now + // ignore it and force the rendering of SpacePanel if that Labs flag is enabled. + if (SettingsStore.getValue("feature_spaces")) { + leftLeftPanel = ; + } else if (this.state.showGroupFilterPanel) { + leftLeftPanel = ( +
+ + {SettingsStore.getValue("feature_custom_tags") ? : null} +
+ ); + } const roomList = { const containerClasses = classNames({ "mx_LeftPanel": true, - "mx_LeftPanel_hasGroupFilterPanel": !!groupFilterPanel, "mx_LeftPanel_minimized": this.props.isMinimized, }); @@ -417,7 +424,7 @@ export default class LeftPanel extends React.Component { return (
- {groupFilterPanel} + {leftLeftPanel}