From 0638b94cc24333814542c4dae898ccfe504ba19a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Wed, 24 Jun 2020 16:06:50 +0100 Subject: [PATCH 01/47] Move compact checkbox --- .../tabs/user/_AppearanceUserSettingsTab.scss | 8 +++++++- src/components/views/elements/SettingsFlag.tsx | 14 ++++++++++++-- .../tabs/user/AppearanceUserSettingsTab.tsx | 10 ++++++++-- src/i18n/strings/en_EN.json | 2 +- src/settings/Settings.js | 2 +- 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index b24f548d60..d724b164e5 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -209,9 +209,15 @@ limitations under the License. } .mx_AppearanceUserSettingsTab_Advanced { + color: $primary-fg-color; + + > * { + margin-bottom: 16px; + } + .mx_AppearanceUserSettingsTab_AdvancedToggle { color: $accent-color; - margin-bottom: 16px; + cursor: pointer; } .mx_AppearanceUserSettingsTab_systemFont { diff --git a/src/components/views/elements/SettingsFlag.tsx b/src/components/views/elements/SettingsFlag.tsx index 9bdd04d803..4f41db51e2 100644 --- a/src/components/views/elements/SettingsFlag.tsx +++ b/src/components/views/elements/SettingsFlag.tsx @@ -30,6 +30,7 @@ interface IProps { isExplicit?: boolean; // XXX: once design replaces all toggles make this the default useCheckbox?: boolean; + disabled?: boolean; onChange?(checked: boolean): void; } @@ -78,14 +79,23 @@ export default class SettingsFlag extends React.Component { else label = _t(label); if (this.props.useCheckbox) { - return + return {label} ; } else { return (
{label} - +
); } diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index e935663bbe..9846be18d3 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -391,7 +391,13 @@ export default class AppearanceUserSettingsTab extends React.Component + advanced = <> + - ; + ; } return
{toggle} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 74e747726a..4970a650db 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -441,7 +441,7 @@ "Font size": "Font size", "Use custom size": "Use custom size", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", - "Use compact timeline layout": "Use compact timeline layout", + "Use a more compact ‘Modern’ layout": "Use a more compact ‘Modern’ layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", "Show join/leave messages (invites/kicks/bans unaffected)": "Show join/leave messages (invites/kicks/bans unaffected)", "Show avatar changes": "Show avatar changes", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index eb882b2d18..6c26967b1e 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -197,7 +197,7 @@ export const SETTINGS = { }, "useCompactLayout": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, - displayName: _td('Use compact timeline layout'), + displayName: _td('Use a more compact ‘Modern’ layout'), default: false, }, "showRedactions": { From 291914492fefae9162e84b904236baadb3afeb09 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 24 Jun 2020 19:20:43 -0600 Subject: [PATCH 02/47] Fix icons in the new user menu not showing up --- res/css/structures/_UserMenuButton.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/structures/_UserMenuButton.scss b/res/css/structures/_UserMenuButton.scss index 85c3f53aa1..c2bfe5b916 100644 --- a/res/css/structures/_UserMenuButton.scss +++ b/res/css/structures/_UserMenuButton.scss @@ -105,6 +105,7 @@ limitations under the License. content: ''; width: 16px; height: 16px; + display: block; mask-position: center; mask-size: contain; mask-repeat: no-repeat; From 37a415693f670e8193630c5913018c38e8d14e1c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 24 Jun 2020 20:08:26 -0600 Subject: [PATCH 03/47] Allow the user to resize the new sublists to 1 tile For dogfooding https://github.com/vector-im/riot-web/issues/14137 To change the default: `localStorage.setItem("mx_dogfood_rl_defTiles", 4);` --- src/components/views/rooms/RoomSublist2.tsx | 2 +- src/stores/room-list/ListLayout.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 6e3e90f805..5cbe10e160 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -117,7 +117,7 @@ export default class RoomSublist2 extends React.Component { }; private onShowLessClick = () => { - this.props.layout.visibleTiles = this.props.layout.minVisibleTiles; + this.props.layout.visibleTiles = this.props.layout.defaultVisibleTiles; this.forceUpdate(); // because the layout doesn't trigger a re-render }; diff --git a/src/stores/room-list/ListLayout.ts b/src/stores/room-list/ListLayout.ts index ebc7b95854..370777ef8b 100644 --- a/src/stores/room-list/ListLayout.ts +++ b/src/stores/room-list/ListLayout.ts @@ -67,6 +67,7 @@ export class ListLayout { } public get visibleTiles(): number { + if (this._n === 0) return this.defaultVisibleTiles; return Math.max(this._n, this.minVisibleTiles); } @@ -78,7 +79,13 @@ export class ListLayout { public get minVisibleTiles(): number { // the .65 comes from the CSS where the show more button is // mathematically 65% of a tile when floating. - return 4.65; + return 1.65; + } + + public get defaultVisibleTiles(): number { + // TODO: Remove dogfood flag + const val = Number(localStorage.getItem("mx_dogfood_rl_defTiles") || 4); + return val + 0.65; // see minVisibleTiles for where the .65 comes from } public calculateTilesToPixelsMin(maxTiles: number, n: number, possiblePadding: number): number { From 90ff4585d5979f354af2b118f52ca1e5f7400d53 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 24 Jun 2020 20:14:01 -0600 Subject: [PATCH 04/47] Remove extraneous debug from the new left panel --- src/components/structures/LeftPanel2.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index ec846bd177..27583f26ee 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -161,7 +161,6 @@ export default class LeftPanel2 extends React.Component { }; private onResize = () => { - console.log("Resize width"); if (!this.listContainerRef.current) return; // ignore: no headers to sticky this.handleStickyHeaders(this.listContainerRef.current); }; From dc099efb19d41360dc248e626b65592a9b06baf0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 08:43:35 +0100 Subject: [PATCH 05/47] make Notifier getSoundForRoom synchronous Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Notifier.js | 4 ++-- .../settings/tabs/room/NotificationSettingsTab.js | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index cd328ba565..b6690959d2 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -122,7 +122,7 @@ const Notifier = { } }, - getSoundForRoom: async function(roomId) { + getSoundForRoom: function(roomId) { // We do no caching here because the SDK caches setting // and the browser will cache the sound. const content = SettingsStore.getValue("notificationSound", roomId); @@ -151,7 +151,7 @@ const Notifier = { }, _playAudioNotification: async function(ev, room) { - const sound = await this.getSoundForRoom(room.roomId); + const sound = this.getSoundForRoom(room.roomId); console.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`); try { diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.js b/src/components/views/settings/tabs/room/NotificationSettingsTab.js index 96e6b3d354..c521e228e0 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.js +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.js @@ -39,12 +39,11 @@ export default class NotificationsSettingsTab extends React.Component { // TODO: [REACT-WARNING] Replace component with real class, use constructor for refs UNSAFE_componentWillMount() { // eslint-disable-line camelcase - Notifier.getSoundForRoom(this.props.roomId).then((soundData) => { - if (!soundData) { - return; - } - this.setState({currentSound: soundData.name || soundData.url}); - }); + const soundData = Notifier.getSoundForRoom(this.props.roomId); + if (!soundData) { + return; + } + this.setState({currentSound: soundData.name || soundData.url}); this._soundUpload = createRef(); } From dbe575d523b35cb3972e76e75e7ff681118df262 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 08:44:24 +0100 Subject: [PATCH 06/47] Remove DEBUG Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/toasts/AnalyticsToast.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/toasts/AnalyticsToast.tsx b/src/toasts/AnalyticsToast.tsx index 7cd59222dd..b186a65d9d 100644 --- a/src/toasts/AnalyticsToast.tsx +++ b/src/toasts/AnalyticsToast.tsx @@ -24,14 +24,12 @@ import GenericToast from "../components/views/toasts/GenericToast"; import ToastStore from "../stores/ToastStore"; const onAccept = () => { - console.log("DEBUG onAccept AnalyticsToast"); dis.dispatch({ action: 'accept_cookies', }); }; const onReject = () => { - console.log("DEBUG onReject AnalyticsToast"); dis.dispatch({ action: "reject_cookies", }); From d4eebd5202387dd4df9bff6710222827c995c614 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 08:58:54 +0100 Subject: [PATCH 07/47] Fix alwaysShowBadgeCounts settings defn Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/settings/Settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index eb882b2d18..5948a4ec9e 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -192,7 +192,7 @@ export const SETTINGS = { }, // TODO: Wire up appropriately to UI (FTUE notifications) "Notifications.alwaysShowBadgeCounts": { - supportedLevels: ['account'], + supportedLevels: LEVELS_ROOM_OR_ACCOUNT, default: false, }, "useCompactLayout": { From 8743af56ad8f9b71dc32f507fedede5b51b0d767 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 09:00:13 +0100 Subject: [PATCH 08/47] Bring notification utils into this century to simplify ongoing Notifications work Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../{ContentRules.js => ContentRules.ts} | 57 ++++++--- ...ificationUtils.js => NotificationUtils.ts} | 26 ++-- ...eVectorState.js => PushRuleVectorState.ts} | 47 ++++---- ...{StandardActions.js => StandardActions.ts} | 2 - src/notifications/types.ts | 111 ++++++++++++++++++ 5 files changed, 189 insertions(+), 54 deletions(-) rename src/notifications/{ContentRules.js => ContentRules.ts} (69%) rename src/notifications/{NotificationUtils.js => NotificationUtils.ts} (80%) rename src/notifications/{PushRuleVectorState.js => PushRuleVectorState.ts} (69%) rename src/notifications/{StandardActions.js => StandardActions.ts} (98%) create mode 100644 src/notifications/types.ts diff --git a/src/notifications/ContentRules.js b/src/notifications/ContentRules.ts similarity index 69% rename from src/notifications/ContentRules.js rename to src/notifications/ContentRules.ts index 8c285220c7..a3ec017e37 100644 --- a/src/notifications/ContentRules.js +++ b/src/notifications/ContentRules.ts @@ -1,6 +1,6 @@ /* Copyright 2016 OpenMarket Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 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. @@ -15,9 +15,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -'use strict'; +import {PushRuleVectorState, State} from "./PushRuleVectorState"; +import {IExtendedPushRule, IPushRuleSet, IRuleSets} from "./types"; -import {PushRuleVectorState} from "./PushRuleVectorState"; +export interface IContentRules { + vectorState: State; + rules: IExtendedPushRule[]; + externalRules: IExtendedPushRule[]; +} + +export const SCOPE = "global"; +export const KIND = "content"; export class ContentRules { /** @@ -31,7 +39,7 @@ export class ContentRules { * externalRules: a list of other keyword rules, with states other than * vectorState */ - static parseContentRules(rulesets) { + static parseContentRules(rulesets: IRuleSets): IContentRules { // first categorise the keyword rules in terms of their actions const contentRules = this._categoriseContentRules(rulesets); @@ -51,59 +59,72 @@ export class ContentRules { if (contentRules.loud.length) { return { - vectorState: PushRuleVectorState.LOUD, + vectorState: State.Loud, rules: contentRules.loud, - externalRules: [].concat(contentRules.loud_but_disabled, contentRules.on, contentRules.on_but_disabled, contentRules.other), + externalRules: [ + ...contentRules.loud_but_disabled, + ...contentRules.on, + ...contentRules.on_but_disabled, + ...contentRules.other, + ], }; } else if (contentRules.loud_but_disabled.length) { return { - vectorState: PushRuleVectorState.OFF, + vectorState: State.Off, rules: contentRules.loud_but_disabled, - externalRules: [].concat(contentRules.on, contentRules.on_but_disabled, contentRules.other), + externalRules: [...contentRules.on, ...contentRules.on_but_disabled, ...contentRules.other], }; } else if (contentRules.on.length) { return { - vectorState: PushRuleVectorState.ON, + vectorState: State.On, rules: contentRules.on, - externalRules: [].concat(contentRules.on_but_disabled, contentRules.other), + externalRules: [...contentRules.on_but_disabled, ...contentRules.other], }; } else if (contentRules.on_but_disabled.length) { return { - vectorState: PushRuleVectorState.OFF, + vectorState: State.Off, rules: contentRules.on_but_disabled, externalRules: contentRules.other, }; } else { return { - vectorState: PushRuleVectorState.ON, + vectorState: State.On, rules: [], externalRules: contentRules.other, }; } } - static _categoriseContentRules(rulesets) { - const contentRules = {on: [], on_but_disabled: [], loud: [], loud_but_disabled: [], other: []}; + static _categoriseContentRules(rulesets: IRuleSets) { + const contentRules: Record<"on"|"on_but_disabled"|"loud"|"loud_but_disabled"|"other", IExtendedPushRule[]> = { + on: [], + on_but_disabled: [], + loud: [], + loud_but_disabled: [], + other: [], + }; + for (const kind in rulesets.global) { for (let i = 0; i < Object.keys(rulesets.global[kind]).length; ++i) { const r = rulesets.global[kind][i]; // check it's not a default rule - if (r.rule_id[0] === '.' || kind !== 'content') { + if (r.rule_id[0] === '.' || kind !== "content") { continue; } - r.kind = kind; // is this needed? not sure + // this is needed as we are flattening an object of arrays into a single array + r.kind = kind; switch (PushRuleVectorState.contentRuleVectorStateKind(r)) { - case PushRuleVectorState.ON: + case State.On: if (r.enabled) { contentRules.on.push(r); } else { contentRules.on_but_disabled.push(r); } break; - case PushRuleVectorState.LOUD: + case State.Loud: if (r.enabled) { contentRules.loud.push(r); } else { diff --git a/src/notifications/NotificationUtils.js b/src/notifications/NotificationUtils.ts similarity index 80% rename from src/notifications/NotificationUtils.js rename to src/notifications/NotificationUtils.ts index bf393da060..e3b7f66447 100644 --- a/src/notifications/NotificationUtils.js +++ b/src/notifications/NotificationUtils.ts @@ -1,6 +1,6 @@ /* Copyright 2016 OpenMarket Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 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. @@ -15,7 +15,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -'use strict'; +import {Action, Actions} from "./types"; + +interface IEncodedActions { + notify: boolean; + sound?: string; + highlight?: boolean; +} export class NotificationUtils { // Encodes a dictionary of { @@ -24,12 +30,12 @@ export class NotificationUtils { // "highlight: true/false, // } // to a list of push actions. - static encodeActions(action) { + static encodeActions(action: IEncodedActions) { const notify = action.notify; const sound = action.sound; const highlight = action.highlight; if (notify) { - const actions = ["notify"]; + const actions: Action[] = [Actions.Notify]; if (sound) { actions.push({"set_tweak": "sound", "value": sound}); } @@ -40,7 +46,7 @@ export class NotificationUtils { } return actions; } else { - return ["dont_notify"]; + return [Actions.DontNotify]; } } @@ -50,18 +56,18 @@ export class NotificationUtils { // "highlight: true/false, // } // If the actions couldn't be decoded then returns null. - static decodeActions(actions) { + static decodeActions(actions: Action[]): IEncodedActions { let notify = false; let sound = null; let highlight = false; for (let i = 0; i < actions.length; ++i) { const action = actions[i]; - if (action === "notify") { + if (action === Actions.Notify) { notify = true; - } else if (action === "dont_notify") { + } else if (action === Actions.DontNotify) { notify = false; - } else if (typeof action === 'object') { + } else if (typeof action === "object") { if (action.set_tweak === "sound") { sound = action.value; } else if (action.set_tweak === "highlight") { @@ -81,7 +87,7 @@ export class NotificationUtils { highlight = true; } - const result = {notify: notify, highlight: highlight}; + const result: IEncodedActions = { notify, highlight }; if (sound !== null) { result.sound = sound; } diff --git a/src/notifications/PushRuleVectorState.js b/src/notifications/PushRuleVectorState.ts similarity index 69% rename from src/notifications/PushRuleVectorState.js rename to src/notifications/PushRuleVectorState.ts index 263226ce1c..d33426cfc4 100644 --- a/src/notifications/PushRuleVectorState.js +++ b/src/notifications/PushRuleVectorState.ts @@ -1,6 +1,6 @@ /* Copyright 2016 OpenMarket Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 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. @@ -15,43 +15,42 @@ See the License for the specific language governing permissions and limitations under the License. */ -'use strict'; - import {StandardActions} from "./StandardActions"; import {NotificationUtils} from "./NotificationUtils"; +import {IPushRule} from "./types"; + +export enum State { + /** The push rule is disabled */ + Off = "off", + /** The user will receive push notification for this rule */ + On = "on", + /** The user will receive push notification for this rule with sound and + highlight if this is legitimate */ + Loud = "loud", +} export class PushRuleVectorState { - // Backwards compatibility (things should probably be using .states instead) - static OFF = "off"; - static ON = "on"; - static LOUD = "loud"; + // Backwards compatibility (things should probably be using the enum above instead) + static OFF = State.Off; + static ON = State.On; + static LOUD = State.Loud; /** * Enum for state of a push rule as defined by the Vector UI. * @readonly * @enum {string} */ - static states = { - /** The push rule is disabled */ - OFF: PushRuleVectorState.OFF, - - /** The user will receive push notification for this rule */ - ON: PushRuleVectorState.ON, - - /** The user will receive push notification for this rule with sound and - highlight if this is legitimate */ - LOUD: PushRuleVectorState.LOUD, - }; + static states = State; /** * Convert a PushRuleVectorState to a list of actions * * @return [object] list of push-rule actions */ - static actionsFor(pushRuleVectorState) { - if (pushRuleVectorState === PushRuleVectorState.ON) { + static actionsFor(pushRuleVectorState: State) { + if (pushRuleVectorState === State.On) { return StandardActions.ACTION_NOTIFY; - } else if (pushRuleVectorState === PushRuleVectorState.LOUD) { + } else if (pushRuleVectorState === State.Loud) { return StandardActions.ACTION_HIGHLIGHT_DEFAULT_SOUND; } } @@ -63,7 +62,7 @@ export class PushRuleVectorState { * category or in PushRuleVectorState.LOUD, regardless of its enabled * state. Returns null if it does not match these categories. */ - static contentRuleVectorStateKind(rule) { + static contentRuleVectorStateKind(rule: IPushRule): State { const decoded = NotificationUtils.decodeActions(rule.actions); if (!decoded) { @@ -81,10 +80,10 @@ export class PushRuleVectorState { let stateKind = null; switch (tweaks) { case 0: - stateKind = PushRuleVectorState.ON; + stateKind = State.On; break; case 2: - stateKind = PushRuleVectorState.LOUD; + stateKind = State.Loud; break; } return stateKind; diff --git a/src/notifications/StandardActions.js b/src/notifications/StandardActions.ts similarity index 98% rename from src/notifications/StandardActions.js rename to src/notifications/StandardActions.ts index b54cea332a..c17010af9a 100644 --- a/src/notifications/StandardActions.js +++ b/src/notifications/StandardActions.ts @@ -15,8 +15,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -'use strict'; - import {NotificationUtils} from "./NotificationUtils"; const encodeActions = NotificationUtils.encodeActions; diff --git a/src/notifications/types.ts b/src/notifications/types.ts new file mode 100644 index 0000000000..9622193740 --- /dev/null +++ b/src/notifications/types.ts @@ -0,0 +1,111 @@ +/* +Copyright 2020 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. +*/ + +export enum NotificationSetting { + AllMessages = "all_messages", // .m.rule.message = notify + DirectMessagesMentionsKeywords = "dm_mentions_keywords", // .m.rule.message = mark_unread. This is the new default. + MentionsKeywordsOnly = "mentions_keywords", // .m.rule.message = mark_unread; .m.rule.room_one_to_one = mark_unread + Never = "never", // .m.rule.master = enabled (dont_notify) +} + +export interface ISoundTweak { + set_tweak: "sound"; + value: string; +} +export interface IHighlightTweak { + set_tweak: "highlight"; + value?: boolean; +} + +export type Tweak = ISoundTweak | IHighlightTweak; + +export enum Actions { + Notify = "notify", + DontNotify = "dont_notify", // no-op + Coalesce = "coalesce", // unused + MarkUnread = "mark_unread", // new +} + +export type Action = Actions | Tweak; + +// Push rule kinds in descending priority order +export enum Kind { + Override = "override", + ContentSpecific = "content", + RoomSpecific = "room", + SenderSpecific = "sender", + Underride = "underride", +} + +export interface IEventMatchCondition { + kind: "event_match"; + key: string; + pattern: string; +} + +export interface IContainsDisplayNameCondition { + kind: "contains_display_name"; +} + +export interface IRoomMemberCountCondition { + kind: "room_member_count"; + is: string; +} + +export interface ISenderNotificationPermissionCondition { + kind: "sender_notification_permission"; + key: string; +} + +export type Condition = + IEventMatchCondition | + IContainsDisplayNameCondition | + IRoomMemberCountCondition | + ISenderNotificationPermissionCondition; + +export enum RuleIds { + MasterRule = ".m.rule.master", // The master rule (all notifications disabling) + MessageRule = ".m.rule.message", + EncryptedMessageRule = ".m.rule.encrypted", + RoomOneToOneRule = ".m.rule.room_one_to_one", + EncryptedRoomOneToOneRule = ".m.rule.room_one_to_one", +} + +export interface IPushRule { + enabled: boolean; + rule_id: RuleIds | string; + actions: Action[]; + default: boolean; + conditions?: Condition[]; // only applicable to `underride` and `override` rules + pattern?: string; // only applicable to `content` rules +} + +// push rule extended with kind, used by ContentRules and js-sdk's pushprocessor +export interface IExtendedPushRule extends IPushRule { + kind: Kind; +} + +export interface IPushRuleSet { + override: IPushRule[]; + content: IPushRule[]; + room: IPushRule[]; + sender: IPushRule[]; + underride: IPushRule[]; +} + +export interface IRuleSets { + global: IPushRuleSet; +} From a3b38a2b5f0a683c9633a9c7fbb3b4b6264735d1 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 07:14:02 -0600 Subject: [PATCH 09/47] Make LoggedInView a real component because it uses shouldComponentUpdate React demands this. --- src/components/structures/LoggedInView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 1bc656e6a3..9c01480df2 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -123,7 +123,7 @@ interface IState { * * Components mounted below us can access the matrix client via the react context. */ -class LoggedInView extends React.PureComponent { +class LoggedInView extends React.Component { static displayName = 'LoggedInView'; static propTypes = { From 41c59cc75e6bce6968e8e841306686663cd6edf9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 25 Jun 2020 14:21:08 +0100 Subject: [PATCH 10/47] Fix deactivated checked checkbox styling --- res/css/views/elements/_StyledCheckbox.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_StyledCheckbox.scss b/res/css/views/elements/_StyledCheckbox.scss index aab448605c..60f1bf0277 100644 --- a/res/css/views/elements/_StyledCheckbox.scss +++ b/res/css/views/elements/_StyledCheckbox.scss @@ -77,8 +77,8 @@ limitations under the License. } &:checked:disabled + label > .mx_Checkbox_background { - background-color: $muted-fg-color; - border-color: rgba($muted-fg-color, 0.5); + background-color: $accent-color; + border-color: $accent-color; } } } From 61618d51628db53bc7175e1c820278930b2eb90c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 15:02:52 +0100 Subject: [PATCH 11/47] tidy up Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/RoomNotifs.js | 5 +++-- src/components/structures/MatrixChat.tsx | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/RoomNotifs.js b/src/RoomNotifs.js index c67acaf314..4614bef378 100644 --- a/src/RoomNotifs.js +++ b/src/RoomNotifs.js @@ -56,10 +56,11 @@ export function countRoomsWithNotif(rooms) { } export function aggregateNotificationCount(rooms) { - return rooms.reduce((result, room, index) => { + return rooms.reduce((result, room) => { const roomNotifState = getRoomNotifsState(room.roomId); const highlight = room.getUnreadNotificationCount('highlight') > 0; - const notificationCount = room.getUnreadNotificationCount(); + // use helper method to include highlights in the previous version of the room + const notificationCount = getUnreadNotificationCount(room); const notifBadges = notificationCount > 0 && shouldShowNotifBadge(roomNotifState); const mentionBadges = highlight && shouldShowMentionBadge(roomNotifState); diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index fa6cd8a4d8..63d2ec6fd9 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1828,7 +1828,9 @@ export default class MatrixChat extends React.PureComponent { } updateStatusIndicator(state: string, prevState: string) { - const notifCount = countRoomsWithNotif(MatrixClientPeg.get().getRooms()).count; + // only count visible rooms to not torment the user with notification counts in rooms they can't see + // it will include highlights from the previous version of the room internally + const notifCount = countRoomsWithNotif(MatrixClientPeg.get().getVisibleRooms()).count; if (PlatformPeg.get()) { PlatformPeg.get().setErrorStatus(state === 'ERROR'); From ed634a2bde594bd67212b43b26a013115f1e0528 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 16:35:40 +0100 Subject: [PATCH 12/47] Add StyledRadioGroup to simplify use of StyledRadioButton and use in Appearance Tab Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/elements/StyledRadioGroup.tsx | 61 +++++++++++++++++++ .../tabs/user/AppearanceUserSettingsTab.tsx | 29 +++++---- 2 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 src/components/views/elements/StyledRadioGroup.tsx diff --git a/src/components/views/elements/StyledRadioGroup.tsx b/src/components/views/elements/StyledRadioGroup.tsx new file mode 100644 index 0000000000..050a8b7adb --- /dev/null +++ b/src/components/views/elements/StyledRadioGroup.tsx @@ -0,0 +1,61 @@ +/* +Copyright 2020 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 React from "react"; +import classNames from "classnames"; + +import StyledRadioButton from "./StyledRadioButton"; + +interface IDefinition { + value: T; + className?: string; + disabled?: boolean; + label: React.ReactChild; + description?: React.ReactChild; +} + +interface IProps { + name: string; + className?: string; + definitions: IDefinition[]; + value?: T; // if not provided no options will be selected + onChange(newValue: T); +} + +function StyledRadioGroup({name, definitions, value, className, onChange}: IProps) { + const _onChange = e => { + onChange(e.target.value); + }; + + return + {definitions.map(d => + + {d.label} + + {d.description} + )} + ; +} + +export default StyledRadioGroup; diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index e935663bbe..42f8cb01de 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -33,6 +33,7 @@ import StyledCheckbox from '../../../elements/StyledCheckbox'; import SettingsFlag from '../../../elements/SettingsFlag'; import Field from '../../../elements/Field'; import EventTilePreview from '../../../elements/EventTilePreview'; +import StyledRadioGroup from "../../../elements/StyledRadioGroup"; interface IProps { } @@ -116,8 +117,7 @@ export default class AppearanceUserSettingsTab extends React.Component): void => { - const newTheme = e.target.value; + private onThemeChange = (newTheme: string): void => { if (this.state.theme === newTheme) return; // doing getValue in the .catch will still return the value we failed to set, @@ -277,19 +277,18 @@ export default class AppearanceUserSettingsTab extends React.Component {_t("Theme")} {systemThemeSection} -
- {orderedThemes.map(theme => { - return - {theme.name} - ; - })} +
+ ({ + value: t.id, + label: t.name, + disabled: this.state.useSystemTheme, + className: "mx_ThemeSelector_" + t.id, + }))} + onChange={this.onThemeChange} + value={this.state.useSystemTheme ? undefined : this.state.theme} + />
{customThemeForm} From c920cf784e155f545264be990d5e2d1f3742efb5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 23 Jun 2020 08:08:48 -0600 Subject: [PATCH 13/47] Create a StaticNotificationState for representative purposes --- src/components/views/rooms/NotificationBadge.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/components/views/rooms/NotificationBadge.tsx b/src/components/views/rooms/NotificationBadge.tsx index 523b5a55cc..6929341845 100644 --- a/src/components/views/rooms/NotificationBadge.tsx +++ b/src/components/views/rooms/NotificationBadge.tsx @@ -141,6 +141,20 @@ export default class NotificationBadge extends React.PureComponent Date: Thu, 25 Jun 2020 16:45:01 +0100 Subject: [PATCH 14/47] Add account and room-account data hooks Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/hooks/useAccountData.ts | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/hooks/useAccountData.ts diff --git a/src/hooks/useAccountData.ts b/src/hooks/useAccountData.ts new file mode 100644 index 0000000000..b9b89d1350 --- /dev/null +++ b/src/hooks/useAccountData.ts @@ -0,0 +1,50 @@ +/* +Copyright 2020 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 {useCallback, useState} from "react"; +import {MatrixClient} from "matrix-js-sdk/src/client"; +import {MatrixEvent} from "matrix-js-sdk/src/models/event"; +import {Room} from "matrix-js-sdk/src/models/room"; + +import {useEventEmitter} from "./useEventEmitter"; + +const tryGetContent = (ev?: MatrixEvent) => ev ? ev.getContent() : undefined; + +// Hook to simplify listening to Matrix account data +export const useAccountData = (cli: MatrixClient, eventType: string) => { + const [value, setValue] = useState(() => tryGetContent(cli.getAccountData(eventType))); + + const handler = useCallback((event) => { + if (event.getType() !== eventType) return; + setValue(event.getContent()); + }, [cli, eventType]); + useEventEmitter(cli, "accountData", handler); + + return value || {}; +}; + +// Hook to simplify listening to Matrix room account data +export const useRoomAccountData = (room: Room, eventType: string) => { + const [value, setValue] = useState(() => tryGetContent(room.getAccountData(eventType))); + + const handler = useCallback((event) => { + if (event.getType() !== eventType) return; + setValue(event.getContent()); + }, [room, eventType]); + useEventEmitter(room, "Room.accountData", handler); + + return value || {}; +}; From 4885615a40770ed7823e697e9bc79d9a4aacb790 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 16:55:38 +0100 Subject: [PATCH 15/47] improve typing Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/hooks/useAccountData.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useAccountData.ts b/src/hooks/useAccountData.ts index b9b89d1350..dd0d53f0d3 100644 --- a/src/hooks/useAccountData.ts +++ b/src/hooks/useAccountData.ts @@ -33,7 +33,7 @@ export const useAccountData = (cli: MatrixClient, eventType: strin }, [cli, eventType]); useEventEmitter(cli, "accountData", handler); - return value || {}; + return value || {} as T; }; // Hook to simplify listening to Matrix room account data @@ -46,5 +46,5 @@ export const useRoomAccountData = (room: Room, eventType: string) }, [room, eventType]); useEventEmitter(room, "Room.accountData", handler); - return value || {}; + return value || {} as T; }; From 5efa5d2c809582be2a51bdd2c6ea377d9a54ebb7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 10:07:23 -0600 Subject: [PATCH 16/47] Implement new resize handle for dogfooding Smaller handle width, small shadow on the top of the show more button if there's more rooms to be shown. The resize handle also only shows when you're hovering in the area now. The original design called for the shadow to show up only if the user is cutting a tile or dragging, however that is complicated implementation-wise. For speed and encouraging a dogfooding pattern we're going ahead with this behaviour instead. --- res/css/views/rooms/_RoomSublist2.scss | 40 +++++++++++++-------- src/components/views/rooms/RoomSublist2.tsx | 12 +++++-- src/stores/room-list/ListLayout.ts | 14 +++++--- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index c7dae56353..8dfc117533 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -226,6 +226,16 @@ limitations under the License. .mx_RoomSublist2_showLessButtonChevron { mask-image: url('$(res)/img/feather-customised/chevron-up.svg'); } + + &.mx_RoomSublist2_isCutting::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + box-shadow: 0px -2px 3px rgba(46, 47, 50, 0.08); + } } // Class name comes from the ResizableBox component @@ -233,31 +243,31 @@ limitations under the License. // so that selector is below and one level higher. .react-resizable-handle { cursor: ns-resize; - border-radius: 2px; + border-radius: 3px; + + // Update the render() function for RoomSublist2 if this changes + height: 3px; // This is positioned directly below the 'show more' button. position: absolute; bottom: 0; - left: 0; - right: 0; - // This is to visually align the bar in the list. Should be 12px from - // either side of the list. We define this after the positioning to - // trick the browser. - margin-left: 4px; - margin-right: 4px; + // Together, these make the bar 48px wide + left: calc(50% - 24px); + right: calc(50% - 24px); + } + + // TODO: Use less sketchy selector by replacing the resize component entirely + // This causes flickering. + .mx_RoomSublist2_showNButton:hover + .react-resizable-handle, + .react-resizable-handle:hover { + opacity: 0.8; + background-color: $primary-fg-color; } } // The aforementioned selector for the hover state. &:hover, &.mx_RoomSublist2_hasMenuOpen { - .react-resizable-handle { - opacity: 0.2; - - // Update the render() function for RoomSublist2 if this changes - border: 2px solid $primary-fg-color; - } - &:not(.mx_RoomSublist2_minimized) > .mx_RoomSublist2_headerContainer { // If the header doesn't have an aux button we still need to hide the badge for // the menu button. diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 5cbe10e160..015ad5b646 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -43,7 +43,7 @@ import { TagID } from "../../../stores/room-list/models"; *******************************************************************/ const SHOW_N_BUTTON_HEIGHT = 32; // As defined by CSS -const RESIZE_HANDLE_HEIGHT = 4; // As defined by CSS +const RESIZE_HANDLE_HEIGHT = 3; // As defined by CSS const MAX_PADDING_HEIGHT = SHOW_N_BUTTON_HEIGHT + RESIZE_HANDLE_HEIGHT; @@ -356,6 +356,12 @@ export default class RoomSublist2 extends React.Component { const nVisible = Math.floor(layout.visibleTiles); const visibleTiles = tiles.slice(0, nVisible); + const maxTilesFactored = layout.tilesWithResizerBoxFactor(tiles.length); + const showMoreBtnClasses = classNames({ + 'mx_RoomSublist2_showNButton': true, + 'mx_RoomSublist2_isCutting': layout.visibleTiles < maxTilesFactored, + }); + // If we're hiding rooms, show a 'show more' button to the user. This button // floats above the resize handle, if we have one present. If the user has all // tiles visible, it becomes 'show less'. @@ -370,7 +376,7 @@ export default class RoomSublist2 extends React.Component { ); if (this.props.isMinimized) showMoreText = null; showNButton = ( -
+
{/* set by CSS masking */} @@ -386,7 +392,7 @@ export default class RoomSublist2 extends React.Component { ); if (this.props.isMinimized) showLessText = null; showNButton = ( -
+
{/* set by CSS masking */} diff --git a/src/stores/room-list/ListLayout.ts b/src/stores/room-list/ListLayout.ts index 370777ef8b..8ca8ad637b 100644 --- a/src/stores/room-list/ListLayout.ts +++ b/src/stores/room-list/ListLayout.ts @@ -18,6 +18,10 @@ import { TagID } from "./models"; const TILE_HEIGHT_PX = 44; +// the .65 comes from the CSS where the show more button is +// mathematically 65% of a tile when floating. +const RESIZER_BOX_FACTOR = 0.65; + interface ISerializedListLayout { numTiles: number; showPreviews: boolean; @@ -77,15 +81,13 @@ export class ListLayout { } public get minVisibleTiles(): number { - // the .65 comes from the CSS where the show more button is - // mathematically 65% of a tile when floating. - return 1.65; + return 1 + RESIZER_BOX_FACTOR; } public get defaultVisibleTiles(): number { // TODO: Remove dogfood flag const val = Number(localStorage.getItem("mx_dogfood_rl_defTiles") || 4); - return val + 0.65; // see minVisibleTiles for where the .65 comes from + return val + RESIZER_BOX_FACTOR; } public calculateTilesToPixelsMin(maxTiles: number, n: number, possiblePadding: number): number { @@ -99,6 +101,10 @@ export class ListLayout { return this.tilesToPixels(Math.min(maxTiles, n)) + padding; } + public tilesWithResizerBoxFactor(n: number): number { + return n + RESIZER_BOX_FACTOR; + } + public tilesWithPadding(n: number, paddingPx: number): number { return this.pixelsToTiles(this.tilesToPixelsWithPadding(n, paddingPx)); } From 0af1507eed14767b28e3acbbab8d505d96d02e18 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 10:54:51 -0600 Subject: [PATCH 17/47] Update sublists for new hover states Fixes https://github.com/vector-im/riot-web/issues/14135 Unblocks https://github.com/vector-im/riot-web/issues/14089 --- res/css/views/rooms/_RoomSublist2.scss | 110 ++++++-------------- src/components/views/rooms/RoomSublist2.tsx | 2 +- 2 files changed, 34 insertions(+), 78 deletions(-) diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index 8dfc117533..2efedc8cc9 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -85,23 +85,24 @@ limitations under the License. // *************************** .mx_RoomSublist2_badgeContainer { - opacity: 0.8; - width: 16px; - margin-right: 5px; // aligns with the room tile's badge - // Create another flexbox row because it's super easy to position the badge this way. display: flex; align-items: center; justify-content: center; + + // Apply the width and margin to the badge so the container doesn't occupy dead space + .mx_NotificationBadge { + width: 16px; + margin-left: 8px; // same as menu+aux buttons + } } - // Both of these buttons are hidden by default until the list is hovered .mx_RoomSublist2_auxButton, .mx_RoomSublist2_menuButton { - width: 0; - margin: 0; - visibility: hidden; + margin-left: 8px; // should be the same as the notification badge position: relative; + width: 24px; + height: 24px; border-radius: 32px; &::before { @@ -118,6 +119,13 @@ limitations under the License. } } + // Hide the menu button by default + .mx_RoomSublist2_menuButton { + visibility: hidden; + width: 0; + margin: 0; + } + .mx_RoomSublist2_auxButton::before { mask-image: url('$(res)/img/feather-customised/plus.svg'); } @@ -142,11 +150,9 @@ limitations under the License. .mx_RoomSublist2_collapseBtn { display: inline-block; position: relative; - - // Default hidden - visibility: hidden; - width: 0; - height: 0; + width: 12px; + height: 12px; + margin-right: 8px; &::before { content: ''; @@ -158,7 +164,7 @@ limitations under the License. mask-position: center; mask-size: contain; mask-repeat: no-repeat; - background: $primary-fg-color; + background-color: $primary-fg-color; mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); } @@ -266,46 +272,12 @@ limitations under the License. } } - // The aforementioned selector for the hover state. - &:hover, &.mx_RoomSublist2_hasMenuOpen { - &:not(.mx_RoomSublist2_minimized) > .mx_RoomSublist2_headerContainer { - // If the header doesn't have an aux button we still need to hide the badge for - // the menu button. - .mx_RoomSublist2_badgeContainer { - // Completely hide the badge - width: 0; - margin: 0; - visibility: hidden; - } - - &:not(.mx_RoomSublist2_headerContainer_withAux) { - // The menu button will be the rightmost button, so make it correctly aligned. - .mx_RoomSublist2_menuButton { - margin-right: 1px; // line it up with the badges on the room tiles - } - } - - // Both of these buttons have circled backgrounds and are visible at this point, - // so make them so. - .mx_RoomSublist2_auxButton, - .mx_RoomSublist2_menuButton { - width: 24px; - height: 24px; - margin-left: 16px; - visibility: visible; - background-color: $roomlist2-button-bg-color; - } - } - - .mx_RoomSublist2_headerContainer { - .mx_RoomSublist2_headerText { - .mx_RoomSublist2_collapseBtn { - visibility: visible; - width: 12px; - height: 12px; - margin-right: 4px; - } - } + &.mx_RoomSublist2_hasMenuOpen, + &:not(.mx_RoomSublist2_minimized) > .mx_RoomSublist2_headerContainer:hover { + .mx_RoomSublist2_menuButton { + visibility: visible; + width: 24px; + margin-left: 8px; } } @@ -354,7 +326,12 @@ limitations under the License. } } - &:hover, &.mx_RoomSublist2_hasMenuOpen { + .mx_RoomSublist2_menuButton { + height: 16px; + } + + &.mx_RoomSublist2_hasMenuOpen, + & > .mx_RoomSublist2_headerContainer:hover { .mx_RoomSublist2_menuButton { visibility: visible; position: absolute; @@ -375,7 +352,7 @@ limitations under the License. } } - .mx_RoomSublist2_headerContainer:not(.mx_RoomSublist2_headerContainer_withAux) { + &.mx_RoomSublist2_headerContainer:not(.mx_RoomSublist2_headerContainer_withAux) { .mx_RoomSublist2_menuButton { bottom: 8px; // align to the middle of name, 40px less than the `bottom` above. } @@ -384,27 +361,6 @@ limitations under the License. } } -// We have a hover style on the room list with no specific list hovered, so account for that -.mx_RoomList2:hover .mx_RoomSublist2:not(.mx_RoomSublist2_minimized), -.mx_RoomSublist2_hasMenuOpen:not(.mx_RoomSublist2_minimized) { - .mx_RoomSublist2_headerContainer_withAux { - .mx_RoomSublist2_badgeContainer { - // Completely hide the badge - width: 0; - margin: 0; - visibility: hidden; - } - - .mx_RoomSublist2_auxButton { - // Show the aux button, but not the list button - width: 24px; - height: 24px; - margin-right: 1px; // line it up with the badges on the room tiles - visibility: visible; - } - } -} - .mx_RoomSublist2_contextMenu { padding: 20px 16px; width: 250px; diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 015ad5b646..6510251bc5 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -320,8 +320,8 @@ export default class RoomSublist2 extends React.Component { {this.props.label} {this.renderMenu()} - {this.props.isMinimized ? null : addRoomButton} {this.props.isMinimized ? null : badgeContainer} + {this.props.isMinimized ? null : addRoomButton}
{this.props.isMinimized ? badgeContainer : null} {this.props.isMinimized ? addRoomButton : null} From 3524d678f7f5dd1a842a9a409f3bee0b97b2bd64 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 21:24:24 +0100 Subject: [PATCH 18/47] Fix Welcome.html URLs Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/BasePlatform.ts | 15 ++++++++++----- src/components/views/auth/Welcome.js | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index d54dc7dd23..aed063ca32 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -227,6 +227,15 @@ export default abstract class BasePlatform { return url; } + // persist hs url and is url for when the user is returned to the app with the login token + // MUST be called before using URLs from getSSOCallbackUrl, internally called by startSingleSignOn + persistSSODetails(mxClient: MatrixClient) { + localStorage.setItem(HOMESERVER_URL_KEY, mxClient.getHomeserverUrl()); + if (mxClient.getIdentityServerUrl()) { + localStorage.setItem(ID_SERVER_URL_KEY, mxClient.getIdentityServerUrl()); + } + } + /** * Begin Single Sign On flows. * @param {MatrixClient} mxClient the matrix client using which we should start the flow @@ -234,11 +243,7 @@ export default abstract class BasePlatform { * @param {string} fragmentAfterLogin the hash to pass to the app during sso callback. */ startSingleSignOn(mxClient: MatrixClient, loginType: "sso" | "cas", fragmentAfterLogin: string) { - // persist hs url and is url for when the user is returned to the app with the login token - localStorage.setItem(HOMESERVER_URL_KEY, mxClient.getHomeserverUrl()); - if (mxClient.getIdentityServerUrl()) { - localStorage.setItem(ID_SERVER_URL_KEY, mxClient.getIdentityServerUrl()); - } + this.persistSSODetails(mxClient); const callbackUrl = this.getSSOCallbackUrl(fragmentAfterLogin); window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType); // redirect to SSO } diff --git a/src/components/views/auth/Welcome.js b/src/components/views/auth/Welcome.js index 91ba368f70..c01b846739 100644 --- a/src/components/views/auth/Welcome.js +++ b/src/components/views/auth/Welcome.js @@ -45,8 +45,8 @@ export default class Welcome extends React.PureComponent { idBaseUrl: isUrl, }); const plaf = PlatformPeg.get(); - const callbackUrl = plaf.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl(), - this.props.fragmentAfterLogin); + plaf.persistSSODetails(tmpClient); + const callbackUrl = plaf.getSSOCallbackUrl(this.props.fragmentAfterLogin); return ( From 1c00ae8dd35857bc052b21ee882e863e205e9e2b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 21:59:46 +0100 Subject: [PATCH 19/47] Move to mx_sso_hs_url and co for sso persistance to not conflict with guest creds Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/BasePlatform.ts | 21 ++++++++------------ src/Lifecycle.js | 9 ++++++--- src/components/structures/auth/SoftLogout.js | 6 +++--- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index aed063ca32..1d11495e61 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -25,8 +25,8 @@ import {CheckUpdatesPayload} from "./dispatcher/payloads/CheckUpdatesPayload"; import {Action} from "./dispatcher/actions"; import {hideToast as hideUpdateToast} from "./toasts/UpdateToast"; -export const HOMESERVER_URL_KEY = "mx_hs_url"; -export const ID_SERVER_URL_KEY = "mx_is_url"; +export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url"; +export const SSO_ID_SERVER_URL_KEY = "mx_sso_is_url"; export enum UpdateCheckStatus { Checking = "CHECKING", @@ -221,21 +221,12 @@ export default abstract class BasePlatform { setLanguage(preferredLangs: string[]) {} - getSSOCallbackUrl(fragmentAfterLogin: string): URL { + protected getSSOCallbackUrl(fragmentAfterLogin: string): URL { const url = new URL(window.location.href); url.hash = fragmentAfterLogin || ""; return url; } - // persist hs url and is url for when the user is returned to the app with the login token - // MUST be called before using URLs from getSSOCallbackUrl, internally called by startSingleSignOn - persistSSODetails(mxClient: MatrixClient) { - localStorage.setItem(HOMESERVER_URL_KEY, mxClient.getHomeserverUrl()); - if (mxClient.getIdentityServerUrl()) { - localStorage.setItem(ID_SERVER_URL_KEY, mxClient.getIdentityServerUrl()); - } - } - /** * Begin Single Sign On flows. * @param {MatrixClient} mxClient the matrix client using which we should start the flow @@ -243,7 +234,11 @@ export default abstract class BasePlatform { * @param {string} fragmentAfterLogin the hash to pass to the app during sso callback. */ startSingleSignOn(mxClient: MatrixClient, loginType: "sso" | "cas", fragmentAfterLogin: string) { - this.persistSSODetails(mxClient); + // persist hs url and is url for when the user is returned to the app with the login token + localStorage.setItem(SSO_HOMESERVER_URL_KEY, mxClient.getHomeserverUrl()); + if (mxClient.getIdentityServerUrl()) { + localStorage.setItem(SSO_ID_SERVER_URL_KEY, mxClient.getIdentityServerUrl()); + } const callbackUrl = this.getSSOCallbackUrl(fragmentAfterLogin); window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType); // redirect to SSO } diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 96cefaf593..facde3011c 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -41,7 +41,10 @@ import {IntegrationManagers} from "./integrations/IntegrationManagers"; import {Mjolnir} from "./mjolnir/Mjolnir"; import DeviceListener from "./DeviceListener"; import {Jitsi} from "./widgets/Jitsi"; -import {HOMESERVER_URL_KEY, ID_SERVER_URL_KEY} from "./BasePlatform"; +import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY} from "./BasePlatform"; + +export const HOMESERVER_URL_KEY = "mx_hs_url"; +export const ID_SERVER_URL_KEY = "mx_is_url"; /** * Called at startup, to attempt to build a logged-in Matrix session. It tries @@ -164,8 +167,8 @@ export function attemptTokenLogin(queryParams, defaultDeviceDisplayName) { return Promise.resolve(false); } - const homeserver = localStorage.getItem(HOMESERVER_URL_KEY); - const identityServer = localStorage.getItem(ID_SERVER_URL_KEY); + const homeserver = localStorage.getItem(SSO_HOMESERVER_URL_KEY); + const identityServer = localStorage.getItem(SSO_ID_SERVER_URL_KEY); if (!homeserver) { console.warn("Cannot log in with token: can't determine HS URL to use"); return Promise.resolve(false); diff --git a/src/components/structures/auth/SoftLogout.js b/src/components/structures/auth/SoftLogout.js index a2824b63a3..6577386fae 100644 --- a/src/components/structures/auth/SoftLogout.js +++ b/src/components/structures/auth/SoftLogout.js @@ -25,7 +25,7 @@ import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {sendLoginRequest} from "../../../Login"; import AuthPage from "../../views/auth/AuthPage"; import SSOButton from "../../views/elements/SSOButton"; -import {HOMESERVER_URL_KEY, ID_SERVER_URL_KEY} from "../../../BasePlatform"; +import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY} from "../../../BasePlatform"; const LOGIN_VIEW = { LOADING: 1, @@ -158,8 +158,8 @@ export default class SoftLogout extends React.Component { async trySsoLogin() { this.setState({busy: true}); - const hsUrl = localStorage.getItem(HOMESERVER_URL_KEY); - const isUrl = localStorage.getItem(ID_SERVER_URL_KEY) || MatrixClientPeg.get().getIdentityServerUrl(); + const hsUrl = localStorage.getItem(SSO_HOMESERVER_URL_KEY); + const isUrl = localStorage.getItem(SSO_ID_SERVER_URL_KEY) || MatrixClientPeg.get().getIdentityServerUrl(); const loginType = "m.login.token"; const loginParams = { token: this.props.realQueryParams['loginToken'], From c65ccbcacf1ab3096c405434da6ec91349aebf75 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 22:00:22 +0100 Subject: [PATCH 20/47] Instead of passing sso and cas urls to Welcome, route via start_sso and start_cas Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 26 ++++++++++++++++++++---- src/components/views/auth/Welcome.js | 15 ++------------ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index d5f73fa3df..a48b1e62a9 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -18,6 +18,7 @@ limitations under the License. */ import React, { createRef } from 'react'; +import { createClient } from "matrix-js-sdk"; import { InvalidStoreError } from "matrix-js-sdk/src/errors"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; @@ -1612,6 +1613,19 @@ export default class MatrixChat extends React.PureComponent { }); } else if (screen === 'directory') { dis.fire(Action.ViewRoomDirectory); + } else if (screen === "start_sso" || screen === "start_cas") { + // TODO if logged in, skip SSO + let cli = MatrixClientPeg.get(); + if (!cli) { + const {hsUrl, isUrl} = this.props.serverConfig; + cli = createClient({ + baseUrl: hsUrl, + idBaseUrl: isUrl, + }); + } + + const type = screen === "start_sso" ? "sso" : "cas"; + PlatformPeg.get().startSingleSignOn(cli, type, this.getFragmentAfterLogin()); } else if (screen === 'groups') { dis.dispatch({ action: 'view_my_groups', @@ -1922,9 +1936,7 @@ export default class MatrixChat extends React.PureComponent { this.onLoggedIn(); }; - render() { - // console.log(`Rendering MatrixChat with view ${this.state.view}`); - + getFragmentAfterLogin() { let fragmentAfterLogin = ""; if (this.props.initialScreenAfterLogin && // XXX: workaround for https://github.com/vector-im/riot-web/issues/11643 causing a login-loop @@ -1932,7 +1944,13 @@ export default class MatrixChat extends React.PureComponent { ) { fragmentAfterLogin = `/${this.props.initialScreenAfterLogin.screen}`; } + return fragmentAfterLogin; + } + render() { + // console.log(`Rendering MatrixChat with view ${this.state.view}`); + + const fragmentAfterLogin = this.getFragmentAfterLogin(); let view; if (this.state.view === Views.LOADING) { @@ -2011,7 +2029,7 @@ export default class MatrixChat extends React.PureComponent { } } else if (this.state.view === Views.WELCOME) { const Welcome = sdk.getComponent('auth.Welcome'); - view = ; + view = ; } else if (this.state.view === Views.REGISTER) { const Registration = sdk.getComponent('structures.auth.Registration'); view = ( diff --git a/src/components/views/auth/Welcome.js b/src/components/views/auth/Welcome.js index c01b846739..5a30a02490 100644 --- a/src/components/views/auth/Welcome.js +++ b/src/components/views/auth/Welcome.js @@ -18,9 +18,7 @@ import React from 'react'; import * as sdk from '../../../index'; import SdkConfig from '../../../SdkConfig'; import AuthPage from "./AuthPage"; -import * as Matrix from "matrix-js-sdk"; import {_td} from "../../../languageHandler"; -import PlatformPeg from "../../../PlatformPeg"; // translatable strings for Welcome pages _td("Sign in with SSO"); @@ -39,15 +37,6 @@ export default class Welcome extends React.PureComponent { pageUrl = 'welcome.html'; } - const {hsUrl, isUrl} = this.props.serverConfig; - const tmpClient = Matrix.createClient({ - baseUrl: hsUrl, - idBaseUrl: isUrl, - }); - const plaf = PlatformPeg.get(); - plaf.persistSSODetails(tmpClient); - const callbackUrl = plaf.getSSOCallbackUrl(this.props.fragmentAfterLogin); - return (
@@ -55,8 +44,8 @@ export default class Welcome extends React.PureComponent { className="mx_WelcomePage" url={pageUrl} replaceMap={{ - "$riot:ssoUrl": tmpClient.getSsoLoginUrl(callbackUrl.toString(), "sso"), - "$riot:casUrl": tmpClient.getSsoLoginUrl(callbackUrl.toString(), "cas"), + "$riot:ssoUrl": "#/start_sso", + "$riot:casUrl": "#/start_cas", }} /> From f02c52b758439022b1746d0006d2e54dd1163ec0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 22:01:41 +0100 Subject: [PATCH 21/47] unexport things which need not exporting Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Lifecycle.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index facde3011c..9ae4ae7e03 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -43,8 +43,8 @@ import DeviceListener from "./DeviceListener"; import {Jitsi} from "./widgets/Jitsi"; import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY} from "./BasePlatform"; -export const HOMESERVER_URL_KEY = "mx_hs_url"; -export const ID_SERVER_URL_KEY = "mx_is_url"; +const HOMESERVER_URL_KEY = "mx_hs_url"; +const ID_SERVER_URL_KEY = "mx_is_url"; /** * Called at startup, to attempt to build a logged-in Matrix session. It tries From 29b0505bdbbfe21162d4a0c978a06238dc5f0991 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 22:02:39 +0100 Subject: [PATCH 22/47] Welcome no longer needs any props Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index a48b1e62a9..26621fb439 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -2029,7 +2029,7 @@ export default class MatrixChat extends React.PureComponent { } } else if (this.state.view === Views.WELCOME) { const Welcome = sdk.getComponent('auth.Welcome'); - view = ; + view = ; } else if (this.state.view === Views.REGISTER) { const Registration = sdk.getComponent('structures.auth.Registration'); view = ( From 2b58875c7f3859241ea811cf179d59b998cdeaa4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 15:07:38 -0600 Subject: [PATCH 23/47] Fix alignment issues with the user menu objects --- res/css/structures/_LeftPanel2.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index dd28a3107c..0765b628f6 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -48,7 +48,7 @@ $tagPanelWidth: 70px; // only applies in this file, used for calculations flex-direction: column; .mx_LeftPanel2_userHeader { - padding: 14px 12px 20px; // 14px top, 12px sides, 20px bottom + padding: 12px 12px 20px; // 12px top, 12px sides, 20px bottom // Create another flexbox column for the rows to stack within display: flex; @@ -65,6 +65,7 @@ $tagPanelWidth: 70px; // only applies in this file, used for calculations .mx_LeftPanel2_userAvatarContainer { position: relative; // to make default avatars work margin-right: 8px; + height: 32px; // to remove the unknown 4px gap the browser puts below it .mx_LeftPanel2_userAvatar { border-radius: 32px; // should match avatar size From 9f5a716cc5fd82a2af3d46aafab01e4293feecdd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 15:11:04 -0600 Subject: [PATCH 24/47] Adjust padding and margins on user menu --- res/css/_common.scss | 6 +++--- res/css/structures/_UserMenuButton.scss | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index e83c6aaeda..cf48358a4e 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -596,14 +596,14 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { } &:last-child { - padding-bottom: 20px; + padding-bottom: 16px; } } .mx_IconizedContextMenu_optionList { // the notFirst class is for cases where the optionList might be under a header of sorts. &:nth-child(n + 2), .mx_IconizedContextMenu_optionList_notFirst { - margin-top: 20px; + margin-top: 12px; // This is a bit of a hack when we could just use a simple border-top property, // however we have a (kinda) good reason for doing it this way: we need opacity. @@ -634,7 +634,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { li { margin: 0; - padding: 20px 0 0; + padding: 12px 0 0; .mx_AccessibleButton { text-decoration: none; diff --git a/res/css/structures/_UserMenuButton.scss b/res/css/structures/_UserMenuButton.scss index c2bfe5b916..cdb6adb8bd 100644 --- a/res/css/structures/_UserMenuButton.scss +++ b/res/css/structures/_UserMenuButton.scss @@ -45,11 +45,6 @@ limitations under the License. display: flex; align-items: center; - &:nth-child(n + 1) { - // The first header will have appropriate padding, subsequent ones need a margin. - margin-top: 10px; - } - .mx_UserMenuButton_contextMenu_name { // Create another flexbox of columns to handle large user IDs display: flex; From 7b79dd6be14fcc3f7baa36e32e1bdab556f59706 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 15:13:28 -0600 Subject: [PATCH 25/47] Make the sign out button red --- res/css/structures/_UserMenuButton.scss | 10 ++++++++++ src/components/structures/UserMenuButton.tsx | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/res/css/structures/_UserMenuButton.scss b/res/css/structures/_UserMenuButton.scss index cdb6adb8bd..f1dffbd1f5 100644 --- a/res/css/structures/_UserMenuButton.scss +++ b/res/css/structures/_UserMenuButton.scss @@ -40,6 +40,16 @@ limitations under the License. .mx_UserMenuButton_contextMenu { width: 247px; + .mx_UserMenuButton_contextMenu_redRow { + .mx_AccessibleButton { + color: $warning-color !important; // !important to override styles from context menu + } + + .mx_IconizedContextMenu_icon::before { + background-color: $warning-color; + } + } + .mx_UserMenuButton_contextMenu_header { // Create a flexbox to organize the header a bit easier display: flex; diff --git a/src/components/structures/UserMenuButton.tsx b/src/components/structures/UserMenuButton.tsx index 27dfdac5a1..7613a4a9ae 100644 --- a/src/components/structures/UserMenuButton.tsx +++ b/src/components/structures/UserMenuButton.tsx @@ -263,7 +263,7 @@ export default class UserMenuButton extends React.Component {
    -
  • +
  • {_t("Sign out")} From 129ff3a6e043799631ad0c6b2b02695c07abfb1f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 15:17:19 -0600 Subject: [PATCH 26/47] Match line colour from user menu in sublist menu --- res/css/views/rooms/_RoomSublist2.scss | 1 + res/themes/dark/css/_dark.scss | 2 +- res/themes/light/css/_light.scss | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index 8dfc117533..3117ebcc5f 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -414,6 +414,7 @@ limitations under the License. margin-bottom: 16px; margin-right: 16px; // additional 16px border: 1px solid $roomsublist2-divider-color; + opacity: 0.1; } .mx_RoomSublist2_contextMenu_title { diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 69fc91f222..1546e7a400 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -113,7 +113,7 @@ $theme-button-bg-color: #e3e8f0; $roomlist2-button-bg-color: #1A1D23; // Buttons include the filter box, explore button, and sublist buttons $roomlist2-bg-color: $header-panel-bg-color; -$roomsublist2-divider-color: #e9eaeb; +$roomsublist2-divider-color: $primary-fg-color; $roomtile2-preview-color: #9e9e9e; $roomtile2-default-badge-bg-color: #61708b; diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 57dc1fa5e0..c4b4262642 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -180,7 +180,7 @@ $theme-button-bg-color: #e3e8f0; $roomlist2-button-bg-color: #fff; // Buttons include the filter box, explore button, and sublist buttons $roomlist2-bg-color: $header-panel-bg-color; -$roomsublist2-divider-color: #e9eaeb; +$roomsublist2-divider-color: $primary-fg-color; $roomtile2-preview-color: #9e9e9e; $roomtile2-default-badge-bg-color: #61708b; From aacedfaf1380d42d090136aeef737e8170ef42b9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 15:19:03 -0600 Subject: [PATCH 27/47] Remove opacity from sublist header text, increase weight --- res/css/views/rooms/_RoomSublist2.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index 3117ebcc5f..5f2e9e093b 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -130,9 +130,9 @@ limitations under the License. flex: 1; max-width: calc(100% - 16px); // 16px is the badge width text-transform: uppercase; - opacity: 0.5; line-height: $font-16px; font-size: $font-12px; + font-weight: 600; // Ellipsize any text overflow text-overflow: ellipsis; From 0cb54ed2a401646c8fab2d232f828131dcdac4ee Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 15:42:44 -0600 Subject: [PATCH 28/47] Align the badge count on non-aux lists with other badges --- res/css/views/rooms/_RoomSublist2.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index 2efedc8cc9..4f1c8e9323 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -97,6 +97,12 @@ limitations under the License. } } + &:not(.mx_RoomSublist2_headerContainer_withAux) { + .mx_NotificationBadge { + margin-right: 4px; // just to push it over a bit, aligning it with the other elements + } + } + .mx_RoomSublist2_auxButton, .mx_RoomSublist2_menuButton { margin-left: 8px; // should be the same as the notification badge From 555078a9935ade0ed3b0181a012a0a6a59cf2ef5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 16:03:56 -0600 Subject: [PATCH 29/47] Iterate on the new room list resize handle Only show shadow when resizing, increase the hit area, and make the handle show up when the list itself is hovered. --- res/css/views/rooms/_RoomSublist2.scss | 21 ++++++++++----------- src/components/views/rooms/RoomSublist2.tsx | 16 ++++++++++++++-- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index 5f2e9e093b..cbe471e4da 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -245,24 +245,23 @@ limitations under the License. cursor: ns-resize; border-radius: 3px; - // Update the render() function for RoomSublist2 if this changes - height: 3px; + // Update RESIZE_HANDLE_HEIGHT if this changes + height: 4px; // This is positioned directly below the 'show more' button. position: absolute; bottom: 0; - // Together, these make the bar 48px wide - left: calc(50% - 24px); - right: calc(50% - 24px); + // Together, these make the bar 64px wide + left: calc(50% - 32px); + right: calc(50% - 32px); } - // TODO: Use less sketchy selector by replacing the resize component entirely - // This causes flickering. - .mx_RoomSublist2_showNButton:hover + .react-resizable-handle, - .react-resizable-handle:hover { - opacity: 0.8; - background-color: $primary-fg-color; + &:hover, &.mx_RoomSublist2_hasMenuOpen { + .react-resizable-handle { + opacity: 0.8; + background-color: $primary-fg-color; + } } } diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 015ad5b646..7273e80c61 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -43,7 +43,7 @@ import { TagID } from "../../../stores/room-list/models"; *******************************************************************/ const SHOW_N_BUTTON_HEIGHT = 32; // As defined by CSS -const RESIZE_HANDLE_HEIGHT = 3; // As defined by CSS +const RESIZE_HANDLE_HEIGHT = 4; // As defined by CSS const MAX_PADDING_HEIGHT = SHOW_N_BUTTON_HEIGHT + RESIZE_HANDLE_HEIGHT; @@ -70,6 +70,7 @@ interface IProps { interface IState { notificationState: ListNotificationState; menuDisplayed: boolean; + isResizing: boolean; } export default class RoomSublist2 extends React.Component { @@ -82,6 +83,7 @@ export default class RoomSublist2 extends React.Component { this.state = { notificationState: new ListNotificationState(this.props.isInvite, this.props.tagId), menuDisplayed: false, + isResizing: false, }; this.state.notificationState.setRooms(this.props.rooms); } @@ -111,6 +113,14 @@ export default class RoomSublist2 extends React.Component { this.forceUpdate(); // because the layout doesn't trigger a re-render }; + private onResizeStart = () => { + this.setState({isResizing: true}); + }; + + private onResizeStop = () => { + this.setState({isResizing: false}); + }; + private onShowAllClick = () => { this.props.layout.visibleTiles = this.props.layout.tilesWithPadding(this.numTiles, MAX_PADDING_HEIGHT); this.forceUpdate(); // because the layout doesn't trigger a re-render @@ -359,7 +369,7 @@ export default class RoomSublist2 extends React.Component { const maxTilesFactored = layout.tilesWithResizerBoxFactor(tiles.length); const showMoreBtnClasses = classNames({ 'mx_RoomSublist2_showNButton': true, - 'mx_RoomSublist2_isCutting': layout.visibleTiles < maxTilesFactored, + 'mx_RoomSublist2_isCutting': this.state.isResizing && layout.visibleTiles < maxTilesFactored, }); // If we're hiding rooms, show a 'show more' button to the user. This button @@ -438,6 +448,8 @@ export default class RoomSublist2 extends React.Component { resizeHandles={handles} onResize={this.onResize} className="mx_RoomSublist2_resizeBox" + onResizeStart={this.onResizeStart} + onResizeStop={this.onResizeStop} > {visibleTiles} {showNButton} From 6116cfc2b96d753dbbe32b88c0ae8f715ad9a2de Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jun 2020 23:52:32 +0100 Subject: [PATCH 30/47] js-sdk imports suck Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 26621fb439..7f838f1e9e 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -18,7 +18,7 @@ limitations under the License. */ import React, { createRef } from 'react'; -import { createClient } from "matrix-js-sdk"; +import { createClient } from "matrix-js-sdk/src"; import { InvalidStoreError } from "matrix-js-sdk/src/errors"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; @@ -1948,8 +1948,6 @@ export default class MatrixChat extends React.PureComponent { } render() { - // console.log(`Rendering MatrixChat with view ${this.state.view}`); - const fragmentAfterLogin = this.getFragmentAfterLogin(); let view; From 6ea5dc7b7c550a9edd2c79735c8bba8883841a4e Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 24 Apr 2020 22:14:41 +0100 Subject: [PATCH 31/47] Change the look of the spinner --- res/css/_common.scss | 4 ++ res/css/views/elements/_InlineSpinner.scss | 8 ++++ res/css/views/elements/_Spinner.scss | 10 +++++ res/img/spinner.svg | 3 ++ src/components/structures/MessagePanel.js | 2 +- src/components/views/elements/AppTile.js | 4 +- .../views/elements/InlineSpinner.js | 10 ++++- .../views/elements/MessageSpinner.js | 35 ----------------- src/components/views/elements/Spinner.js | 38 ++++++++++++------- src/components/views/messages/MAudioBody.js | 3 +- src/components/views/messages/MImageBody.js | 8 +--- src/components/views/messages/MVideoBody.js | 3 +- 12 files changed, 67 insertions(+), 61 deletions(-) create mode 100644 res/img/spinner.svg delete mode 100644 src/components/views/elements/MessageSpinner.js diff --git a/res/css/_common.scss b/res/css/_common.scss index cf48358a4e..c087df04cb 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -428,6 +428,10 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { border-radius: 8px; padding: 0px; box-shadow: none; + + /* Don't show scroll-bars on spinner dialogs */ + overflow-x: hidden; + overflow-y: hidden; } // TODO: Review mx_GeneralButton usage to see if it can use a different class diff --git a/res/css/views/elements/_InlineSpinner.scss b/res/css/views/elements/_InlineSpinner.scss index 612b6209c6..561b6cfb82 100644 --- a/res/css/views/elements/_InlineSpinner.scss +++ b/res/css/views/elements/_InlineSpinner.scss @@ -21,4 +21,12 @@ limitations under the License. .mx_InlineSpinner img { margin: 0px 6px; vertical-align: -3px; + + animation: spin 1s linear infinite; +} + +@keyframes spin { + 100% { + transform: rotate(360deg); + } } diff --git a/res/css/views/elements/_Spinner.scss b/res/css/views/elements/_Spinner.scss index 01b4f23c2c..db9f54b56c 100644 --- a/res/css/views/elements/_Spinner.scss +++ b/res/css/views/elements/_Spinner.scss @@ -23,6 +23,16 @@ limitations under the License. flex: 1; } +.mx_Spinner img { + animation: spin 1s linear infinite; +} + +@keyframes spin { + 100% { + transform: rotate(360deg); + } +} + .mx_MatrixChat_middlePanel .mx_Spinner { height: auto; } diff --git a/res/img/spinner.svg b/res/img/spinner.svg new file mode 100644 index 0000000000..a18140c7e2 --- /dev/null +++ b/res/img/spinner.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index d11fee6360..481741dfd2 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -770,7 +770,7 @@ export default class MessagePanel extends React.Component { topSpinner =
  • ; } if (this.props.forwardPaginating) { - bottomSpinner =
  • ; + bottomSpinner =
  • ; } const style = this.props.hidden ? { display: 'none' } : {}; diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 9129b8fe48..60cd1a2eba 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -29,7 +29,7 @@ import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; import AppPermission from './AppPermission'; import AppWarning from './AppWarning'; -import MessageSpinner from './MessageSpinner'; +import Spinner from './Spinner'; import WidgetUtils from '../../../utils/WidgetUtils'; import dis from '../../../dispatcher/dispatcher'; import ActiveWidgetStore from '../../../stores/ActiveWidgetStore'; @@ -740,7 +740,7 @@ export default class AppTile extends React.Component { if (this.props.show) { const loadingElement = (
    - +
    ); if (!this.state.hasPermissionToLoad) { diff --git a/src/components/views/elements/InlineSpinner.js b/src/components/views/elements/InlineSpinner.js index ad70471d89..4842cba0c6 100644 --- a/src/components/views/elements/InlineSpinner.js +++ b/src/components/views/elements/InlineSpinner.js @@ -16,6 +16,7 @@ limitations under the License. import React from "react"; import createReactClass from 'create-react-class'; +import {_t} from "../../../languageHandler"; export default createReactClass({ displayName: 'InlineSpinner', @@ -24,10 +25,17 @@ export default createReactClass({ const w = this.props.w || 16; const h = this.props.h || 16; const imgClass = this.props.imgClassName || ""; + const alt = this.props.alt || _t("Loading..."); return (
    - + {alt}
    ); }, diff --git a/src/components/views/elements/MessageSpinner.js b/src/components/views/elements/MessageSpinner.js deleted file mode 100644 index 1775fdd4d7..0000000000 --- a/src/components/views/elements/MessageSpinner.js +++ /dev/null @@ -1,35 +0,0 @@ -/* -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 createReactClass from 'create-react-class'; - -export default createReactClass({ - displayName: 'MessageSpinner', - - render: function() { - const w = this.props.w || 32; - const h = this.props.h || 32; - const imgClass = this.props.imgClassName || ""; - const msg = this.props.msg || "Loading..."; - return ( -
    -
    { msg }
      - -
    - ); - }, -}); diff --git a/src/components/views/elements/Spinner.js b/src/components/views/elements/Spinner.js index b1fe97d5d2..3d5697c72e 100644 --- a/src/components/views/elements/Spinner.js +++ b/src/components/views/elements/Spinner.js @@ -16,19 +16,29 @@ limitations under the License. */ import React from "react"; -import createReactClass from 'create-react-class'; +import PropTypes from "prop-types"; +import {_t} from "../../../languageHandler"; -export default createReactClass({ - displayName: 'Spinner', +const Spinner = ({w = 32, h = 32, imgClassName, alt, message}) => { + return ( +
    + { message &&
    { message}
     
    } + {alt +
    + ); +}; +Spinner.propTypes = { + w: PropTypes.number, + h: PropTypes.number, + imgClassName: PropTypes.string, + alt: PropTypes.string, + message: PropTypes.node, +}; - render: function() { - const w = this.props.w || 32; - const h = this.props.h || 32; - const imgClass = this.props.imgClassName || ""; - return ( -
    - -
    - ); - }, -}); +export default Spinner; diff --git a/src/components/views/messages/MAudioBody.js b/src/components/views/messages/MAudioBody.js index a642936fec..421ec8fc47 100644 --- a/src/components/views/messages/MAudioBody.js +++ b/src/components/views/messages/MAudioBody.js @@ -22,6 +22,7 @@ import MFileBody from './MFileBody'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import { decryptFile } from '../../../utils/DecryptFile'; import { _t } from '../../../languageHandler'; +import InlineSpinner from '../elements/InlineSpinner'; export default class MAudioBody extends React.Component { constructor(props) { @@ -94,7 +95,7 @@ export default class MAudioBody extends React.Component { // Not sure how tall the audio player is so not sure how tall it should actually be. return ( - {content.body} + ); } diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index ad238a728e..4d12dcf5d7 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -26,6 +26,7 @@ import { decryptFile } from '../../../utils/DecryptFile'; import { _t } from '../../../languageHandler'; import SettingsStore from "../../../settings/SettingsStore"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import InlineSpinner from '../elements/InlineSpinner'; export default class MImageBody extends React.Component { static propTypes = { @@ -365,12 +366,7 @@ export default class MImageBody extends React.Component { // e2e image hasn't been decrypted yet if (content.file !== undefined && this.state.decryptedUrl === null) { - placeholder = {content.body}; + placeholder = ; } else if (!this.state.imgLoaded) { // Deliberately, getSpinner is left unimplemented here, MStickerBody overides placeholder = this.getPlaceholder(); diff --git a/src/components/views/messages/MVideoBody.js b/src/components/views/messages/MVideoBody.js index 03f345e042..1fba9d2ead 100644 --- a/src/components/views/messages/MVideoBody.js +++ b/src/components/views/messages/MVideoBody.js @@ -23,6 +23,7 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg'; import { decryptFile } from '../../../utils/DecryptFile'; import { _t } from '../../../languageHandler'; import SettingsStore from "../../../settings/SettingsStore"; +import InlineSpinner from '../elements/InlineSpinner'; export default createReactClass({ displayName: 'MVideoBody', @@ -147,7 +148,7 @@ export default createReactClass({ return (
    - {content.body} +
    ); From 87f961df3f99feb0cbcc784a65550bbdbf42c4c5 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 26 Jun 2020 00:00:30 +0100 Subject: [PATCH 32/47] Put behind a labs flag --- res/css/views/elements/_Spinner.scss | 2 +- .../views/elements/InlineSpinner.js | 15 +++++++++-- src/components/views/elements/Spinner.js | 27 +++++++++++++------ src/i18n/strings/en_EN.json | 1 + src/settings/Settings.js | 6 +++++ 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/res/css/views/elements/_Spinner.scss b/res/css/views/elements/_Spinner.scss index db9f54b56c..6966a60e52 100644 --- a/res/css/views/elements/_Spinner.scss +++ b/res/css/views/elements/_Spinner.scss @@ -23,7 +23,7 @@ limitations under the License. flex: 1; } -.mx_Spinner img { +.mx_Spinner_spin img { animation: spin 1s linear infinite; } diff --git a/src/components/views/elements/InlineSpinner.js b/src/components/views/elements/InlineSpinner.js index 4842cba0c6..dba2479d57 100644 --- a/src/components/views/elements/InlineSpinner.js +++ b/src/components/views/elements/InlineSpinner.js @@ -17,6 +17,7 @@ limitations under the License. import React from "react"; import createReactClass from 'create-react-class'; import {_t} from "../../../languageHandler"; +import SettingsStore from "../../../settings/SettingsStore"; export default createReactClass({ displayName: 'InlineSpinner', @@ -27,10 +28,20 @@ export default createReactClass({ const imgClass = this.props.imgClassName || ""; const alt = this.props.alt || _t("Loading..."); + let divClass; + let imageSource; + if (SettingsStore.isFeatureEnabled('feature_new_spinner')) { + divClass = "mx_InlineSpinner mx_Spinner_spin"; + imageSource = require("../../../../res/img/spinner.svg"); + } else { + divClass = "mx_InlineSpinner"; + imageSource = require("../../../../res/img/spinner.gif"); + } + return ( -
    +
    { + let divClass; + let imageSource; + if (SettingsStore.isFeatureEnabled('feature_new_spinner')) { + divClass = "mx_Spinner mx_Spinner_spin"; + imageSource = require("../../../../res/img/spinner.svg"); + } else { + divClass = "mx_Spinner"; + imageSource = require("../../../../res/img/spinner.gif"); + } + return ( -
    +
    { message &&
    { message}
     
    } - {alt + {alt
    ); }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 4970a650db..ae44d59c59 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -427,6 +427,7 @@ "Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.", "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", "Failed to join room": "Failed to join room", + "New spinner design": "New spinner design", "Font scaling": "Font scaling", "Message Pinning": "Message Pinning", "Custom user status messages": "Custom user status messages", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index f19b827307..820329f6c6 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -97,6 +97,12 @@ export const SETTINGS = { // // not use this for new settings. // invertedSettingName: "my-negative-setting", // }, + "feature_new_spinner": { + isFeature: true, + displayName: _td("New spinner design"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, "feature_font_scaling": { isFeature: true, displayName: _td("Font scaling"), From b00d822bc0782c07622068af8edc2672fce934e0 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 26 Jun 2020 01:18:02 +0100 Subject: [PATCH 33/47] Remove alt, use aria-label --- src/components/views/elements/InlineSpinner.js | 3 +-- src/components/views/elements/Spinner.js | 5 ++--- src/components/views/messages/MAudioBody.js | 2 +- src/components/views/messages/MImageBody.js | 2 +- src/components/views/messages/MVideoBody.js | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/views/elements/InlineSpinner.js b/src/components/views/elements/InlineSpinner.js index dba2479d57..ad88868790 100644 --- a/src/components/views/elements/InlineSpinner.js +++ b/src/components/views/elements/InlineSpinner.js @@ -26,7 +26,6 @@ export default createReactClass({ const w = this.props.w || 16; const h = this.props.h || 16; const imgClass = this.props.imgClassName || ""; - const alt = this.props.alt || _t("Loading..."); let divClass; let imageSource; @@ -45,7 +44,7 @@ export default createReactClass({ width={w} height={h} className={imgClass} - alt={alt} + aria-label={_t("Loading...")} />
    ); diff --git a/src/components/views/elements/Spinner.js b/src/components/views/elements/Spinner.js index c192f7d499..ee4351fed6 100644 --- a/src/components/views/elements/Spinner.js +++ b/src/components/views/elements/Spinner.js @@ -20,7 +20,7 @@ import PropTypes from "prop-types"; import {_t} from "../../../languageHandler"; import SettingsStore from "../../../settings/SettingsStore"; -const Spinner = ({w = 32, h = 32, imgClassName, alt, message}) => { +const Spinner = ({w = 32, h = 32, imgClassName, message}) => { let divClass; let imageSource; if (SettingsStore.isFeatureEnabled('feature_new_spinner')) { @@ -39,7 +39,7 @@ const Spinner = ({w = 32, h = 32, imgClassName, alt, message}) => { width={w} height={h} className={imgClassName} - alt={alt || _t("Loading...")} + aria-label={_t("Loading...")} />
    ); @@ -48,7 +48,6 @@ Spinner.propTypes = { w: PropTypes.number, h: PropTypes.number, imgClassName: PropTypes.string, - alt: PropTypes.string, message: PropTypes.node, }; diff --git a/src/components/views/messages/MAudioBody.js b/src/components/views/messages/MAudioBody.js index 421ec8fc47..37f85a108f 100644 --- a/src/components/views/messages/MAudioBody.js +++ b/src/components/views/messages/MAudioBody.js @@ -95,7 +95,7 @@ export default class MAudioBody extends React.Component { // Not sure how tall the audio player is so not sure how tall it should actually be. return ( - + ); } diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index 4d12dcf5d7..c92ae475bf 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -366,7 +366,7 @@ export default class MImageBody extends React.Component { // e2e image hasn't been decrypted yet if (content.file !== undefined && this.state.decryptedUrl === null) { - placeholder = ; + placeholder = ; } else if (!this.state.imgLoaded) { // Deliberately, getSpinner is left unimplemented here, MStickerBody overides placeholder = this.getPlaceholder(); diff --git a/src/components/views/messages/MVideoBody.js b/src/components/views/messages/MVideoBody.js index 1fba9d2ead..fdc04deffc 100644 --- a/src/components/views/messages/MVideoBody.js +++ b/src/components/views/messages/MVideoBody.js @@ -148,7 +148,7 @@ export default createReactClass({ return (
    - +
    ); From dafce40d1b7f1a5b6bf8f3f69a1034810fa3953d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 19:29:12 -0600 Subject: [PATCH 34/47] Rename UserMenuButton to UserMenu for new scope --- res/css/_components.scss | 2 +- res/css/structures/{_UserMenuButton.scss => _UserMenu.scss} | 0 src/components/structures/LeftPanel2.tsx | 4 ++-- .../structures/{UserMenuButton.tsx => UserMenu.tsx} | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename res/css/structures/{_UserMenuButton.scss => _UserMenu.scss} (100%) rename src/components/structures/{UserMenuButton.tsx => UserMenu.tsx} (99%) diff --git a/res/css/_components.scss b/res/css/_components.scss index 66eb98ea9d..afc40ca0d6 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -30,7 +30,7 @@ @import "./structures/_ToastContainer.scss"; @import "./structures/_TopLeftMenuButton.scss"; @import "./structures/_UploadBar.scss"; -@import "./structures/_UserMenuButton.scss"; +@import "./structures/_UserMenu.scss"; @import "./structures/_ViewSource.scss"; @import "./structures/auth/_CompleteSecurity.scss"; @import "./structures/auth/_Login.scss"; diff --git a/res/css/structures/_UserMenuButton.scss b/res/css/structures/_UserMenu.scss similarity index 100% rename from res/css/structures/_UserMenuButton.scss rename to res/css/structures/_UserMenu.scss diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index 27583f26ee..4731dad1fc 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -24,7 +24,7 @@ import RoomList2 from "../views/rooms/RoomList2"; import { Action } from "../../dispatcher/actions"; import { MatrixClientPeg } from "../../MatrixClientPeg"; import BaseAvatar from '../views/avatars/BaseAvatar'; -import UserMenuButton from "./UserMenuButton"; +import UserMenu from "./UserMenuButton"; import RoomSearch from "./RoomSearch"; import AccessibleButton from "../views/elements/AccessibleButton"; import RoomBreadcrumbs2 from "../views/rooms/RoomBreadcrumbs2"; @@ -184,7 +184,7 @@ export default class LeftPanel2 extends React.Component { let name = {OwnProfileStore.instance.displayName}; let buttons = ( - + ); if (this.props.isMinimized) { diff --git a/src/components/structures/UserMenuButton.tsx b/src/components/structures/UserMenu.tsx similarity index 99% rename from src/components/structures/UserMenuButton.tsx rename to src/components/structures/UserMenu.tsx index 7613a4a9ae..c927e5c7a7 100644 --- a/src/components/structures/UserMenuButton.tsx +++ b/src/components/structures/UserMenu.tsx @@ -44,7 +44,7 @@ interface IState { isDarkTheme: boolean; } -export default class UserMenuButton extends React.Component { +export default class UserMenu extends React.Component { private dispatcherRef: string; private themeWatcherRef: string; private buttonRef: React.RefObject = createRef(); From bcfdd4d98406ed26c3df7b3e10bd0de92ed2a6dd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 19:38:11 -0600 Subject: [PATCH 35/47] Move all of the UserMenu into the UserMenu component --- res/css/structures/_LeftPanel2.scss | 28 +--------- res/css/structures/_UserMenu.scss | 60 ++++++++++++++++----- src/components/structures/LeftPanel2.tsx | 54 +------------------ src/components/structures/UserMenu.tsx | 67 ++++++++++++++++++------ 4 files changed, 100 insertions(+), 109 deletions(-) diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index 0765b628f6..837a1d1f0d 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -54,7 +54,7 @@ $tagPanelWidth: 70px; // only applies in this file, used for calculations display: flex; flex-direction: column; - // There's 2 rows when breadcrumbs are present: the top bit and the breadcrumbs + // This is basically just breadcrumbs. The row above that is handled by the UserMenu .mx_LeftPanel2_headerRow { // Create yet another flexbox, this time within the row, to ensure items stay // aligned correctly. This is also a row-based flexbox. @@ -62,32 +62,6 @@ $tagPanelWidth: 70px; // only applies in this file, used for calculations align-items: center; } - .mx_LeftPanel2_userAvatarContainer { - position: relative; // to make default avatars work - margin-right: 8px; - height: 32px; // to remove the unknown 4px gap the browser puts below it - - .mx_LeftPanel2_userAvatar { - border-radius: 32px; // should match avatar size - } - } - - .mx_LeftPanel2_userName { - font-weight: 600; - font-size: $font-15px; - line-height: $font-20px; - flex: 1; - - // Ellipsize any text overflow - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - - .mx_LeftPanel2_headerButtons { - // No special styles: the rest of the layout happens to make it work. - } - .mx_LeftPanel2_breadcrumbsContainer { width: 100%; overflow: hidden; diff --git a/res/css/structures/_UserMenu.scss b/res/css/structures/_UserMenu.scss index f1dffbd1f5..a15eeb6c81 100644 --- a/res/css/structures/_UserMenu.scss +++ b/res/css/structures/_UserMenu.scss @@ -14,6 +14,38 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_UserMenu { + // Create a row-based flexbox to ensure items stay aligned correctly. + display: flex; + align-items: center; + + .mx_UserMenu_userAvatarContainer { + position: relative; // to make default avatars work + margin-right: 8px; + height: 32px; // to remove the unknown 4px gap the browser puts below it + + .mx_UserMenu_userAvatar { + border-radius: 32px; // should match avatar size + } + } + + .mx_UserMenu_userName { + font-weight: 600; + font-size: $font-15px; + line-height: $font-20px; + flex: 1; + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + .mx_UserMenu_headerButtons { + // No special styles: the rest of the layout happens to make it work. + } +} + .mx_UserMenuButton { > span { width: 16px; @@ -37,10 +69,10 @@ limitations under the License. } } -.mx_UserMenuButton_contextMenu { +.mx_UserMenu_contextMenu { width: 247px; - .mx_UserMenuButton_contextMenu_redRow { + .mx_UserMenu_contextMenu_redRow { .mx_AccessibleButton { color: $warning-color !important; // !important to override styles from context menu } @@ -50,12 +82,12 @@ limitations under the License. } } - .mx_UserMenuButton_contextMenu_header { + .mx_UserMenu_contextMenu_header { // Create a flexbox to organize the header a bit easier display: flex; align-items: center; - .mx_UserMenuButton_contextMenu_name { + .mx_UserMenu_contextMenu_name { // Create another flexbox of columns to handle large user IDs display: flex; flex-direction: column; @@ -72,19 +104,19 @@ limitations under the License. white-space: nowrap; } - .mx_UserMenuButton_contextMenu_displayName { + .mx_UserMenu_contextMenu_displayName { font-weight: bold; font-size: $font-15px; line-height: $font-20px; } - .mx_UserMenuButton_contextMenu_userId { + .mx_UserMenu_contextMenu_userId { font-size: $font-15px; line-height: $font-24px; } } - .mx_UserMenuButton_contextMenu_themeButton { + .mx_UserMenu_contextMenu_themeButton { min-width: 32px; max-width: 32px; width: 32px; @@ -118,31 +150,31 @@ limitations under the License. } } - .mx_UserMenuButton_iconHome::before { + .mx_UserMenu_iconHome::before { mask-image: url('$(res)/img/feather-customised/home.svg'); } - .mx_UserMenuButton_iconBell::before { + .mx_UserMenu_iconBell::before { mask-image: url('$(res)/img/feather-customised/notifications.svg'); } - .mx_UserMenuButton_iconLock::before { + .mx_UserMenu_iconLock::before { mask-image: url('$(res)/img/feather-customised/lock.svg'); } - .mx_UserMenuButton_iconSettings::before { + .mx_UserMenu_iconSettings::before { mask-image: url('$(res)/img/feather-customised/settings.svg'); } - .mx_UserMenuButton_iconArchive::before { + .mx_UserMenu_iconArchive::before { mask-image: url('$(res)/img/feather-customised/archive.svg'); } - .mx_UserMenuButton_iconMessage::before { + .mx_UserMenu_iconMessage::before { mask-image: url('$(res)/img/feather-customised/message-circle.svg'); } - .mx_UserMenuButton_iconSignOut::before { + .mx_UserMenu_iconSignOut::before { mask-image: url('$(res)/img/feather-customised/sign-out.svg'); } } diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index 4731dad1fc..32d6748f94 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -22,18 +22,13 @@ import dis from "../../dispatcher/dispatcher"; import { _t } from "../../languageHandler"; import RoomList2 from "../views/rooms/RoomList2"; import { Action } from "../../dispatcher/actions"; -import { MatrixClientPeg } from "../../MatrixClientPeg"; -import BaseAvatar from '../views/avatars/BaseAvatar'; -import UserMenu from "./UserMenuButton"; +import UserMenu from "./UserMenu"; import RoomSearch from "./RoomSearch"; import AccessibleButton from "../views/elements/AccessibleButton"; import RoomBreadcrumbs2 from "../views/rooms/RoomBreadcrumbs2"; import { BreadcrumbsStore } from "../../stores/BreadcrumbsStore"; import { UPDATE_EVENT } from "../../stores/AsyncStore"; import ResizeNotifier from "../../utils/ResizeNotifier"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import { throttle } from 'lodash'; -import { OwnProfileStore } from "../../stores/OwnProfileStore"; /******************************************************************* * CAUTION * @@ -76,32 +71,13 @@ export default class LeftPanel2 extends React.Component { // We watch the middle panel because we don't actually get resized, the middle panel does. // We listen to the noisy channel to avoid choppy reaction times. this.props.resizeNotifier.on("middlePanelResizedNoisy", this.onResize); - - OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate); } public componentWillUnmount() { BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate); this.props.resizeNotifier.off("middlePanelResizedNoisy", this.onResize); - OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate); } - // TSLint wants this to be a member, but we don't want that. - // tslint:disable-next-line - private onRoomStateUpdate = throttle((ev: MatrixEvent) => { - const myUserId = MatrixClientPeg.get().getUserId(); - if (ev.getType() === 'm.room.member' && ev.getSender() === myUserId && ev.getStateKey() === myUserId) { - // noinspection JSIgnoredPromiseFromCall - this.onProfileUpdate(); - } - }, 200, {trailing: true, leading: true}); - - private onProfileUpdate = async () => { - // the store triggered an update, so force a layout update. We don't - // have any state to store here for that to magically happen. - this.forceUpdate(); - }; - private onSearch = (term: string): void => { this.setState({searchFilter: term}); }; @@ -170,7 +146,6 @@ export default class LeftPanel2 extends React.Component { // TODO: Presence // TODO: Breadcrumbs toggle // TODO: Menu button - const avatarSize = 32; // should match border-radius of the avatar let breadcrumbs; if (this.state.showBreadcrumbs) { @@ -181,34 +156,9 @@ export default class LeftPanel2 extends React.Component { ); } - let name = {OwnProfileStore.instance.displayName}; - let buttons = ( - - - - ); - if (this.props.isMinimized) { - name = null; - buttons = null; - } - return (
    -
    - - - - {name} - {buttons} -
    + {breadcrumbs}
    ); diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index c927e5c7a7..89593aa702 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -35,8 +35,10 @@ import SdkConfig from "../../SdkConfig"; import {getHomePageUrl} from "../../utils/pages"; import { OwnProfileStore } from "../../stores/OwnProfileStore"; import { UPDATE_EVENT } from "../../stores/AsyncStore"; +import BaseAvatar from '../views/avatars/BaseAvatar'; interface IProps { + isMinimized: boolean; } interface IState { @@ -158,14 +160,14 @@ export default class UserMenu extends React.Component { defaultDispatcher.dispatch({action: 'view_home_page'}); }; - public render() { + private renderMenuButton(): React.ReactNode { let contextMenu; if (this.state.menuDisplayed) { let hostingLink; const signupLink = getHostingLink("user-context-menu"); if (signupLink) { hostingLink = ( -
    +
    {_t( "Upgrade to your own domain", {}, { @@ -188,7 +190,7 @@ export default class UserMenu extends React.Component { homeButton = (
  • - + {_t("Home")}
  • @@ -203,18 +205,18 @@ export default class UserMenu extends React.Component { top={elementRect.top + elementRect.height} onFinished={this.onCloseMenu} > -
    -
    -
    - +
    +
    +
    + {OwnProfileStore.instance.displayName} - + {MatrixClientPeg.get().getUserId()}
    @@ -231,31 +233,31 @@ export default class UserMenu extends React.Component { {homeButton}
  • this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}> - + {_t("Notification settings")}
  • this.onSettingsOpen(e, USER_SECURITY_TAB)}> - + {_t("Security & privacy")}
  • this.onSettingsOpen(e, null)}> - + {_t("All settings")}
  • - + {_t("Archived rooms")}
  • - + {_t("Feedback")}
  • @@ -263,9 +265,9 @@ export default class UserMenu extends React.Component {
      -
    • +
    • - + {_t("Sign out")}
    • @@ -291,4 +293,37 @@ export default class UserMenu extends React.Component { ); } + + public render() { + const avatarSize = 32; // should match border-radius of the avatar + + let name = {OwnProfileStore.instance.displayName}; + let buttons = ( + + {this.renderMenuButton()} + + ); + if (this.props.isMinimized) { + name = null; + buttons = null; + } + + return ( +
      + + + + {name} + {buttons} +
      + ); + } } From 411271422c9f33e802a167074908037ca02a96e9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 19:54:17 -0600 Subject: [PATCH 36/47] Make the whole UserMenu a button to open the menu --- res/css/structures/_LeftPanel2.scss | 10 - res/css/structures/_UserMenu.scss | 76 ++++--- src/components/structures/UserMenu.tsx | 293 +++++++++++++------------ 3 files changed, 193 insertions(+), 186 deletions(-) diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index 837a1d1f0d..98f23a058b 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -132,16 +132,6 @@ $tagPanelWidth: 70px; // only applies in this file, used for calculations .mx_LeftPanel2_roomListContainer { width: 68px; - .mx_LeftPanel2_userHeader { - .mx_LeftPanel2_headerRow { - justify-content: center; - } - - .mx_LeftPanel2_userAvatarContainer { - margin-right: 0; - } - } - .mx_LeftPanel2_filterContainer { // Organize the flexbox into a centered column layout flex-direction: column; diff --git a/res/css/structures/_UserMenu.scss b/res/css/structures/_UserMenu.scss index a15eeb6c81..bbb1e1cc7b 100644 --- a/res/css/structures/_UserMenu.scss +++ b/res/css/structures/_UserMenu.scss @@ -15,39 +15,7 @@ limitations under the License. */ .mx_UserMenu { - // Create a row-based flexbox to ensure items stay aligned correctly. - display: flex; - align-items: center; - - .mx_UserMenu_userAvatarContainer { - position: relative; // to make default avatars work - margin-right: 8px; - height: 32px; // to remove the unknown 4px gap the browser puts below it - - .mx_UserMenu_userAvatar { - border-radius: 32px; // should match avatar size - } - } - - .mx_UserMenu_userName { - font-weight: 600; - font-size: $font-15px; - line-height: $font-20px; - flex: 1; - - // Ellipsize any text overflow - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - .mx_UserMenu_headerButtons { - // No special styles: the rest of the layout happens to make it work. - } -} - -.mx_UserMenuButton { - > span { width: 16px; height: 16px; position: relative; @@ -67,6 +35,50 @@ limitations under the License. mask-image: url('$(res)/img/feather-customised/more-horizontal.svg'); } } + + .mx_UserMenu_row { + // Create a row-based flexbox to ensure items stay aligned correctly. + display: flex; + align-items: center; + + .mx_UserMenu_userAvatarContainer { + position: relative; // to make default avatars work + margin-right: 8px; + height: 32px; // to remove the unknown 4px gap the browser puts below it + + .mx_UserMenu_userAvatar { + border-radius: 32px; // should match avatar size + } + } + + .mx_UserMenu_userName { + font-weight: 600; + font-size: $font-15px; + line-height: $font-20px; + flex: 1; + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + .mx_UserMenu_headerButtons { + // No special styles: the rest of the layout happens to make it work. + } + } + + &.mx_UserMenu_minimized { + .mx_UserMenu_userHeader { + .mx_UserMenu_row { + justify-content: center; + } + + .mx_UserMenu_userAvatarContainer { + margin-right: 0; + } + } + } } .mx_UserMenu_contextMenu { diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index 89593aa702..6e3670447e 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -36,6 +36,7 @@ import {getHomePageUrl} from "../../utils/pages"; import { OwnProfileStore } from "../../stores/OwnProfileStore"; import { UPDATE_EVENT } from "../../stores/AsyncStore"; import BaseAvatar from '../views/avatars/BaseAvatar'; +import classNames from "classnames"; interface IProps { isMinimized: boolean; @@ -108,7 +109,9 @@ export default class UserMenu extends React.Component { this.setState({menuDisplayed: true}); }; - private onCloseMenu = () => { + private onCloseMenu = (ev: InputEvent) => { + ev.preventDefault(); + ev.stopPropagation(); this.setState({menuDisplayed: false}); }; @@ -160,147 +163,132 @@ export default class UserMenu extends React.Component { defaultDispatcher.dispatch({action: 'view_home_page'}); }; - private renderMenuButton(): React.ReactNode { - let contextMenu; - if (this.state.menuDisplayed) { - let hostingLink; - const signupLink = getHostingLink("user-context-menu"); - if (signupLink) { - hostingLink = ( -
      - {_t( - "Upgrade to your own domain", {}, - { - a: sub => ( - {sub} - ), - }, - )} -
      - ); - } + private renderContextMenu = (): React.ReactNode => { + if (!this.state.menuDisplayed) return null; - let homeButton = null; - if (this.hasHomePage) { - homeButton = ( -
    • - - - {_t("Home")} - -
    • - ); - } - - const elementRect = this.buttonRef.current.getBoundingClientRect(); - contextMenu = ( - -
      -
      -
      - - {OwnProfileStore.instance.displayName} - - - {MatrixClientPeg.get().getUserId()} - -
      -
      - {_t("Switch -
      -
      - {hostingLink} -
      -
        - {homeButton} -
      • - this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}> - - {_t("Notification settings")} - -
      • -
      • - this.onSettingsOpen(e, USER_SECURITY_TAB)}> - - {_t("Security & privacy")} - -
      • -
      • - this.onSettingsOpen(e, null)}> - - {_t("All settings")} - -
      • -
      • - - - {_t("Archived rooms")} - -
      • -
      • - - - {_t("Feedback")} - -
      • -
      -
      -
      -
        -
      • - - - {_t("Sign out")} - -
      • -
      -
      -
      -
      + let hostingLink; + const signupLink = getHostingLink("user-context-menu"); + if (signupLink) { + hostingLink = ( +
      + {_t( + "Upgrade to your own domain", {}, + { + a: sub => ( + {sub} + ), + }, + )} +
      ); } + let homeButton = null; + if (this.hasHomePage) { + homeButton = ( +
    • + + + {_t("Home")} + +
    • + ); + } + + const elementRect = this.buttonRef.current.getBoundingClientRect(); return ( - - - {/* masked image in CSS */} - - {contextMenu} - + +
      +
      +
      + + {OwnProfileStore.instance.displayName} + + + {MatrixClientPeg.get().getUserId()} + +
      +
      + {_t("Switch +
      +
      + {hostingLink} +
      +
        + {homeButton} +
      • + this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}> + + {_t("Notification settings")} + +
      • +
      • + this.onSettingsOpen(e, USER_SECURITY_TAB)}> + + {_t("Security & privacy")} + +
      • +
      • + this.onSettingsOpen(e, null)}> + + {_t("All settings")} + +
      • +
      • + + + {_t("Archived rooms")} + +
      • +
      • + + + {_t("Feedback")} + +
      • +
      +
      +
      +
        +
      • + + + {_t("Sign out")} + +
      • +
      +
      +
      +
      ); - } + }; public render() { + console.log(this.state); const avatarSize = 32; // should match border-radius of the avatar let name = {OwnProfileStore.instance.displayName}; let buttons = ( - {this.renderMenuButton()} + {/* masked image in CSS */} ); if (this.props.isMinimized) { @@ -308,22 +296,39 @@ export default class UserMenu extends React.Component { buttons = null; } + const classes = classNames({ + 'mx_UserMenu': true, + 'mx_UserMenu_minimized': this.props.isMinimized, + }); + return ( -
      - - - - {name} - {buttons} -
      + + +
      + + + + {name} + {buttons} +
      + {this.renderContextMenu()} +
      +
      + ); } } From 588fea3a9b7d8616b7dd4db18bcd4b817231183a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 19:55:08 -0600 Subject: [PATCH 37/47] Make the menu show up where it was before --- src/components/structures/UserMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index 6e3670447e..a824971761 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -204,7 +204,7 @@ export default class UserMenu extends React.Component { return ( From 1888cda5eef0c06e6b70d381515fd3c6e2a0dc1c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 20:22:41 -0600 Subject: [PATCH 38/47] Remove debug --- src/components/structures/UserMenu.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index a824971761..540c8388d1 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -282,7 +282,6 @@ export default class UserMenu extends React.Component { }; public render() { - console.log(this.state); const avatarSize = 32; // should match border-radius of the avatar let name = {OwnProfileStore.instance.displayName}; From 555758d3d2b037256f28c10ba30fa19a0d4ebdd1 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 20:23:37 -0600 Subject: [PATCH 39/47] Remove extra space --- src/components/structures/UserMenu.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index 540c8388d1..19e57ac51b 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -327,7 +327,6 @@ export default class UserMenu extends React.Component { {this.renderContextMenu()} - ); } } From 7ce3cc1db7f43dafbfa556e44c93974e850b4f18 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Jun 2020 20:35:40 -0600 Subject: [PATCH 40/47] Allow the tag panel to be disabled in the new room list Fixes https://github.com/vector-im/riot-web/issues/14156 --- res/css/structures/_LeftPanel2.scss | 13 ++++++++++++- src/components/structures/LeftPanel2.tsx | 11 ++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index 0765b628f6..4bd2f20c89 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -38,6 +38,12 @@ $tagPanelWidth: 70px; // only applies in this file, used for calculations // TagPanel handles its own CSS } + &:not(.mx_LeftPanel2_hasTagPanel) { + .mx_LeftPanel2_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_LeftPanel2_roomListContainer { @@ -153,7 +159,12 @@ $tagPanelWidth: 70px; // only applies in this file, used for calculations min-width: unset; // We have to forcefully set the width to override the resizer's style attribute. - width: calc(68px + $tagPanelWidth) !important; + &.mx_LeftPanel2_hasTagPanel { + width: calc(68px + $tagPanelWidth) !important; + } + &:not(.mx_LeftPanel2_hasTagPanel) { + width: 68px !important; + } .mx_LeftPanel2_roomListContainer { width: 68px; diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index 27583f26ee..9d86fa2b7c 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -34,6 +34,7 @@ import ResizeNotifier from "../../utils/ResizeNotifier"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { throttle } from 'lodash'; import { OwnProfileStore } from "../../stores/OwnProfileStore"; +import SettingsStore from "../../settings/SettingsStore"; /******************************************************************* * CAUTION * @@ -51,10 +52,12 @@ interface IProps { interface IState { searchFilter: string; // TODO: Move search into room list? showBreadcrumbs: boolean; + showTagPanel: boolean; } export default class LeftPanel2 extends React.Component { private listContainerRef: React.RefObject = createRef(); + private tagPanelWatcherRef: string; // TODO: Properly support TagPanel // TODO: Properly support searching/filtering @@ -69,9 +72,13 @@ export default class LeftPanel2 extends React.Component { this.state = { searchFilter: "", showBreadcrumbs: BreadcrumbsStore.instance.visible, + showTagPanel: SettingsStore.getValue('TagPanel.enableTagPanel'), }; BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate); + this.tagPanelWatcherRef = SettingsStore.watchSetting("TagPanel.enableTagPanel", null, () => { + this.setState({showTagPanel: SettingsStore.getValue("TagPanel.enableTagPanel")}); + }); // We watch the middle panel because we don't actually get resized, the middle panel does. // We listen to the noisy channel to avoid choppy reaction times. @@ -81,6 +88,7 @@ export default class LeftPanel2 extends React.Component { } public componentWillUnmount() { + SettingsStore.unwatchSetting(this.tagPanelWatcherRef); BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate); this.props.resizeNotifier.off("middlePanelResizedNoisy", this.onResize); OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate); @@ -231,7 +239,7 @@ export default class LeftPanel2 extends React.Component { } public render(): React.ReactNode { - const tagPanel = ( + const tagPanel = !this.state.showTagPanel ? null : (
      @@ -252,6 +260,7 @@ export default class LeftPanel2 extends React.Component { const containerClasses = classNames({ "mx_LeftPanel2": true, + "mx_LeftPanel2_hasTagPanel": !!tagPanel, "mx_LeftPanel2_minimized": this.props.isMinimized, }); From 9391d151f363289b9860014c33a5508104a8edfb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 26 Jun 2020 09:15:02 +0100 Subject: [PATCH 41/47] ts-ignore because something is made of fail Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 7f838f1e9e..a205a4fb26 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1620,6 +1620,7 @@ export default class MatrixChat extends React.PureComponent { const {hsUrl, isUrl} = this.props.serverConfig; cli = createClient({ baseUrl: hsUrl, + // @ts-ignore - XXX: remove me when it doesn't break tests idBaseUrl: isUrl, }); } From 228a6adfdf17f4b3a8d8c81649f8f7c050280c63 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 26 Jun 2020 09:27:33 +0100 Subject: [PATCH 42/47] indentation --- src/components/views/elements/Spinner.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/views/elements/Spinner.js b/src/components/views/elements/Spinner.js index ee4351fed6..08ba0cf921 100644 --- a/src/components/views/elements/Spinner.js +++ b/src/components/views/elements/Spinner.js @@ -34,13 +34,13 @@ const Spinner = ({w = 32, h = 32, imgClassName, message}) => { return (
      { message &&
      { message}
       
      } - +
      ); }; From 274e6f3825cdd0ffcaf67d34bff44f10c2730e15 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 26 Jun 2020 09:35:29 +0100 Subject: [PATCH 43/47] make js-sdk import happy? Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index a205a4fb26..9ee8a50c50 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -18,7 +18,7 @@ limitations under the License. */ import React, { createRef } from 'react'; -import { createClient } from "matrix-js-sdk/src"; +import * as Matrix from "matrix-js-sdk"; import { InvalidStoreError } from "matrix-js-sdk/src/errors"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; @@ -1618,9 +1618,8 @@ export default class MatrixChat extends React.PureComponent { let cli = MatrixClientPeg.get(); if (!cli) { const {hsUrl, isUrl} = this.props.serverConfig; - cli = createClient({ + cli = Matrix.createClient({ baseUrl: hsUrl, - // @ts-ignore - XXX: remove me when it doesn't break tests idBaseUrl: isUrl, }); } From a905028d3a12c241004cb290d569a044feb1e52b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 26 Jun 2020 09:37:55 +0100 Subject: [PATCH 44/47] bandaid Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 9ee8a50c50..7da565739e 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -18,6 +18,7 @@ limitations under the License. */ import React, { createRef } from 'react'; +// @ts-ignore - XXX: no idea why this import fails import * as Matrix from "matrix-js-sdk"; import { InvalidStoreError } from "matrix-js-sdk/src/errors"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; From a2e33a23861da421c21bd5e51eab8d501038a416 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 26 Jun 2020 09:41:18 +0100 Subject: [PATCH 45/47] Prevent old InlineSpinner gif from spinning --- res/css/views/elements/_InlineSpinner.scss | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/res/css/views/elements/_InlineSpinner.scss b/res/css/views/elements/_InlineSpinner.scss index 561b6cfb82..bdd879b23d 100644 --- a/res/css/views/elements/_InlineSpinner.scss +++ b/res/css/views/elements/_InlineSpinner.scss @@ -18,15 +18,7 @@ limitations under the License. display: inline; } -.mx_InlineSpinner img { +.mx_InlineSpinner_spin img { margin: 0px 6px; vertical-align: -3px; - - animation: spin 1s linear infinite; -} - -@keyframes spin { - 100% { - transform: rotate(360deg); - } -} +} \ No newline at end of file From 96e4b938b2c65d2a87cae1da2c74fa337ce3e47f Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 26 Jun 2020 09:42:44 +0100 Subject: [PATCH 46/47] Don't modify the size of the MessagePanel spinner --- src/components/structures/MessagePanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 481741dfd2..d11fee6360 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -770,7 +770,7 @@ export default class MessagePanel extends React.Component { topSpinner =
    • ; } if (this.props.forwardPaginating) { - bottomSpinner =
    • ; + bottomSpinner =
    • ; } const style = this.props.hidden ? { display: 'none' } : {}; From e790a31f09e09c9c3046416ff72cd06e8829aa30 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 26 Jun 2020 09:44:47 +0100 Subject: [PATCH 47/47] Include newline at end of _InlineSpinner.scss --- res/css/views/elements/_InlineSpinner.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_InlineSpinner.scss b/res/css/views/elements/_InlineSpinner.scss index bdd879b23d..6b91e45923 100644 --- a/res/css/views/elements/_InlineSpinner.scss +++ b/res/css/views/elements/_InlineSpinner.scss @@ -21,4 +21,4 @@ limitations under the License. .mx_InlineSpinner_spin img { margin: 0px 6px; vertical-align: -3px; -} \ No newline at end of file +}