diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 371623642b..161227a139 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -27,6 +27,7 @@ import dis from "../../dispatcher/dispatcher"; import {MatrixClientPeg} from '../../MatrixClientPeg'; import SettingsStore from '../../settings/SettingsStore'; +import {Layout, LayoutPropType} from "../../settings/Layout"; import {_t} from "../../languageHandler"; import {haveTileForEvent} from "../views/rooms/EventTile"; import {textForEvent} from "../../TextForEvent"; @@ -136,14 +137,13 @@ export default class MessagePanel extends React.Component { // whether to show reactions for an event showReactions: PropTypes.bool, - // whether to use the irc layout - useIRCLayout: PropTypes.bool, + // which layout to use + layout: LayoutPropType, // whether or not to show flair at all enableFlair: PropTypes.bool, }; - // Force props to be loaded for useIRCLayout constructor(props) { super(props); @@ -623,7 +623,7 @@ export default class MessagePanel extends React.Component { isSelectedEvent={highlight} getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} - useIRCLayout={this.props.useIRCLayout} + layout={this.props.layout} enableFlair={this.props.enableFlair} /> @@ -821,7 +821,7 @@ export default class MessagePanel extends React.Component { } let ircResizer = null; - if (this.props.useIRCLayout) { + if (this.props.layout == Layout.IRC) { ircResizer = { statusBarVisible: false, canReact: false, canReply: false, - useIRCLayout: SettingsStore.getValue("useIRCLayout"), + layout: SettingsStore.getValue("layout"), matrixClientIsReady: this.context && this.context.isInitialSyncComplete(), }; @@ -264,7 +265,7 @@ export default class RoomView extends React.Component { this.showReadReceiptsWatchRef = SettingsStore.watchSetting("showReadReceipts", null, this.onReadReceiptsChange); - this.layoutWatcherRef = SettingsStore.watchSetting("useIRCLayout", null, this.onLayoutChange); + this.layoutWatcherRef = SettingsStore.watchSetting("layout", null, this.onLayoutChange); } private onWidgetStoreUpdate = () => { @@ -638,7 +639,7 @@ export default class RoomView extends React.Component { private onLayoutChange = () => { this.setState({ - useIRCLayout: SettingsStore.getValue("useIRCLayout"), + layout: SettingsStore.getValue("layout"), }); }; @@ -1945,8 +1946,8 @@ export default class RoomView extends React.Component { const messagePanelClassNames = classNames( "mx_RoomView_messagePanel", { - "mx_IRCLayout": this.state.useIRCLayout, - "mx_GroupLayout": !this.state.useIRCLayout, + "mx_IRCLayout": this.state.layout == Layout.IRC, + "mx_GroupLayout": this.state.layout == Layout.Group, }); // console.info("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview); @@ -1969,7 +1970,7 @@ export default class RoomView extends React.Component { permalinkCreator={this.getPermalinkCreatorForRoom(this.state.room)} resizeNotifier={this.props.resizeNotifier} showReactions={true} - useIRCLayout={this.state.useIRCLayout} + layout={this.state.layout} />); let topUnreadMessagesBar = null; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 27a384ddb2..e8da5c42d0 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -18,6 +18,7 @@ limitations under the License. */ import SettingsStore from "../../settings/SettingsStore"; +import {LayoutPropType} from "../../settings/Layout"; import React, {createRef} from 'react'; import ReactDOM from "react-dom"; import PropTypes from 'prop-types'; @@ -111,8 +112,8 @@ class TimelinePanel extends React.Component { // whether to show reactions for an event showReactions: PropTypes.bool, - // whether to use the irc layout - useIRCLayout: PropTypes.bool, + // which layout to use + layout: LayoutPropType, } // a map from room id to read marker event timestamp @@ -1442,7 +1443,7 @@ class TimelinePanel extends React.Component { getRelationsForEvent={this.getRelationsForEvent} editState={this.state.editState} showReactions={this.props.showReactions} - useIRCLayout={this.props.useIRCLayout} + layout={this.props.layout} enableFlair={SettingsStore.getValue(UIFeature.Flair)} /> ); diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index 20cca35d62..49c97831bc 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -22,6 +22,7 @@ import * as Avatar from '../../../Avatar'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import EventTile from '../rooms/EventTile'; import SettingsStore from "../../../settings/SettingsStore"; +import {Layout} from "../../../settings/Layout"; import {UIFeature} from "../../../settings/UIFeature"; interface IProps { @@ -33,7 +34,7 @@ interface IProps { /** * Whether to use the irc layout or not */ - useIRCLayout: boolean; + layout: Layout; /** * classnames to apply to the wrapper of the preview @@ -121,14 +122,14 @@ export default class EventTilePreview extends React.Component { const event = this.fakeEvent(this.state); const className = classnames(this.props.className, { - "mx_IRCLayout": this.props.useIRCLayout, - "mx_GroupLayout": !this.props.useIRCLayout, + "mx_IRCLayout": this.props.layout == Layout.IRC, + "mx_GroupLayout": this.props.layout == Layout.Group, }); return
; diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 24b49f2b13..27d773b099 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -24,6 +24,7 @@ import {wantsDateSeparator} from '../../../DateUtils'; import {MatrixEvent} from 'matrix-js-sdk'; import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks"; import SettingsStore from "../../../settings/SettingsStore"; +import {LayoutPropType} from "../../../settings/Layout"; import escapeHtml from "escape-html"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {Action} from "../../../dispatcher/actions"; @@ -42,7 +43,7 @@ export default class ReplyThread extends React.Component { onHeightChanged: PropTypes.func.isRequired, permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired, // Specifies which layout to use. - useIRCLayout: PropTypes.bool, + layout: LayoutPropType, }; static contextType = MatrixClientContext; @@ -209,7 +210,7 @@ export default class ReplyThread extends React.Component { }; } - static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout) { + static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, layout) { if (!ReplyThread.getParentEventId(parentEv)) { return
; } @@ -218,7 +219,7 @@ export default class ReplyThread extends React.Component { onHeightChanged={onHeightChanged} ref={ref} permalinkCreator={permalinkCreator} - useIRCLayout={useIRCLayout} + layout={layout} />; } @@ -386,7 +387,7 @@ export default class ReplyThread extends React.Component { permalinkCreator={this.props.permalinkCreator} isRedacted={ev.isRedacted()} isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} - useIRCLayout={this.props.useIRCLayout} + layout={this.props.layout} enableFlair={SettingsStore.getValue(UIFeature.Flair)} replacingEventId={ev.replacingEventId()} /> diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 4df74f77ce..c856919f5a 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -27,6 +27,7 @@ import * as TextForEvent from "../../../TextForEvent"; import * as sdk from "../../../index"; import dis from '../../../dispatcher/dispatcher'; import SettingsStore from "../../../settings/SettingsStore"; +import {Layout, LayoutPropType} from "../../../settings/Layout"; import {EventStatus} from 'matrix-js-sdk'; import {formatTime} from "../../../DateUtils"; import {MatrixClientPeg} from '../../../MatrixClientPeg'; @@ -227,8 +228,8 @@ export default class EventTile extends React.Component { // whether to show reactions for this event showReactions: PropTypes.bool, - // whether to use the irc layout - useIRCLayout: PropTypes.bool, + // which layout to use + layout: LayoutPropType, // whether or not to show flair at all enableFlair: PropTypes.bool, @@ -734,7 +735,7 @@ export default class EventTile extends React.Component { // joins/parts/etc avatarSize = 14; needsSenderProfile = false; - } else if (this.props.useIRCLayout) { + } else if (this.props.layout == Layout.IRC) { avatarSize = 14; needsSenderProfile = true; } else if (this.props.continuation && this.props.tileShape !== "file_grid") { @@ -845,10 +846,11 @@ export default class EventTile extends React.Component { { timestamp } ; - const groupTimestamp = !this.props.useIRCLayout ? linkedTimestamp : null; - const ircTimestamp = this.props.useIRCLayout ? linkedTimestamp : null; - const groupPadlock = !this.props.useIRCLayout && !isBubbleMessage && this._renderE2EPadlock(); - const ircPadlock = this.props.useIRCLayout && !isBubbleMessage && this._renderE2EPadlock(); + const useIRCLayout = this.props.layout == Layout.IRC; + const groupTimestamp = !useIRCLayout ? linkedTimestamp : null; + const ircTimestamp = useIRCLayout ? linkedTimestamp : null; + const groupPadlock = !useIRCLayout && !isBubbleMessage && this._renderE2EPadlock(); + const ircPadlock = useIRCLayout && !isBubbleMessage && this._renderE2EPadlock(); switch (this.props.tileShape) { case 'notif': { @@ -943,7 +945,7 @@ export default class EventTile extends React.Component { this.props.onHeightChanged, this.props.permalinkCreator, this._replyThread, - this.props.useIRCLayout, + this.props.layout, ); // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index 209f245b11..80a20d8afa 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -28,15 +28,14 @@ import { FontWatcher } from "../../../../../settings/watchers/FontWatcher"; import { RecheckThemePayload } from '../../../../../dispatcher/payloads/RecheckThemePayload'; import { Action } from '../../../../../dispatcher/actions'; import { IValidationResult, IFieldState } from '../../../elements/Validation'; -import StyledRadioButton from '../../../elements/StyledRadioButton'; 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"; -import classNames from 'classnames'; import { SettingLevel } from "../../../../../settings/SettingLevel"; import {UIFeature} from "../../../../../settings/UIFeature"; +import {Layout} from "../../../../../settings/Layout"; interface IProps { } @@ -62,7 +61,7 @@ interface IState extends IThemeState { useSystemFont: boolean; systemFont: string; showAdvanced: boolean; - useIRCLayout: boolean; + layout: Layout; } @@ -83,7 +82,7 @@ export default class AppearanceUserSettingsTab extends React.Component): void => { - const val = e.target.value === "true"; - - this.setState({ - useIRCLayout: val, - }); - - SettingsStore.setValue("useIRCLayout", null, SettingLevel.DEVICE, val); - }; + private onIRCLayoutChange = (enabled: boolean) => { + if (enabled) { + this.setState({layout: Layout.IRC}); + SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC); + } else { + this.setState({layout: Layout.Group}); + SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.Group); + } + } private renderThemeSection() { const themeWatcher = new ThemeWatcher(); @@ -306,7 +305,7 @@ export default class AppearanceUserSettingsTab extends React.Component
Aa
@@ -342,50 +341,6 @@ export default class AppearanceUserSettingsTab extends React.Component; } - private renderLayoutSection = () => { - return
- {_t("Message layout")} - -
-
- - - {_t("Compact")} - -
-
-
- - - {_t("Modern")} - -
-
-
; - }; - private renderAdvancedSection() { if (!SettingsStore.getValue(UIFeature.AdvancedSettings)) return null; @@ -409,14 +364,15 @@ export default class AppearanceUserSettingsTab extends React.Component - this.setState({useIRCLayout: checked})} + disabled={this.state.layout == Layout.IRC} /> + this.onIRCLayoutChange(ev.target.checked)} + > + {_t("Enable experimental, compact IRC style layout")} + + ({ roomLoading: true, @@ -40,7 +41,7 @@ const RoomContext = createContext({ statusBarVisible: false, canReact: false, canReply: false, - useIRCLayout: false, + layout: Layout.Group, matrixClientIsReady: false, }); RoomContext.displayName = "RoomContext"; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d023984f7d..f4caa6ed6f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -844,7 +844,6 @@ "How fast should messages be downloaded.": "How fast should messages be downloaded.", "Manually verify all remote sessions": "Manually verify all remote sessions", "IRC display name width": "IRC display name width", - "Enable experimental, compact IRC style layout": "Enable experimental, compact IRC style layout", "Show chat effects": "Show chat effects", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", @@ -1164,12 +1163,10 @@ "Custom theme URL": "Custom theme URL", "Add theme": "Add theme", "Theme": "Theme", - "Message layout": "Message layout", - "Compact": "Compact", - "Modern": "Modern", "Hide advanced": "Hide advanced", "Show advanced": "Show advanced", "Set the name of a font installed on your system & %(brand)s will attempt to use it.": "Set the name of a font installed on your system & %(brand)s will attempt to use it.", + "Enable experimental, compact IRC style layout": "Enable experimental, compact IRC style layout", "Customise your appearance": "Customise your appearance", "Appearance Settings only affect this %(brand)s session.": "Appearance Settings only affect this %(brand)s session.", "Flair": "Flair", diff --git a/src/settings/Layout.ts b/src/settings/Layout.ts new file mode 100644 index 0000000000..3a42b2b510 --- /dev/null +++ b/src/settings/Layout.ts @@ -0,0 +1,26 @@ +/* +Copyright 2021 Šimon Brandner + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +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 PropTypes from 'prop-types'; + +/* TODO: This should be later reworked into something more generic */ +export enum Layout { + IRC = "irc", + Group = "group" +} + +/* We need this because multiple components are still using JavaScript */ +export const LayoutPropType = PropTypes.oneOf(Object.values(Layout)); diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 385b892478..43dd393ef7 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -36,6 +36,7 @@ import { isMac } from '../Keyboard'; import UIFeatureController from "./controllers/UIFeatureController"; import { UIFeature } from "./UIFeature"; import { OrderedMultiController } from "./controllers/OrderedMultiController"; +import {Layout} from "./Layout"; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = [ @@ -643,10 +644,9 @@ export const SETTINGS: {[setting: string]: ISetting} = { displayName: _td("IRC display name width"), default: 80, }, - "useIRCLayout": { + "layout": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, - displayName: _td("Enable experimental, compact IRC style layout"), - default: false, + default: Layout.Group, }, "showChatEffects": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, diff --git a/src/settings/handlers/DeviceSettingsHandler.ts b/src/settings/handlers/DeviceSettingsHandler.ts index 2096203598..208a265e04 100644 --- a/src/settings/handlers/DeviceSettingsHandler.ts +++ b/src/settings/handlers/DeviceSettingsHandler.ts @@ -20,6 +20,7 @@ import SettingsHandler from "./SettingsHandler"; import {MatrixClientPeg} from "../../MatrixClientPeg"; import {SettingLevel} from "../SettingLevel"; import { CallbackFn, WatchManager } from "../WatchManager"; +import { Layout } from "../Layout"; /** * Gets and sets settings at the "device" level for the current device. @@ -67,6 +68,13 @@ export default class DeviceSettingsHandler extends SettingsHandler { return val['value']; } + // Special case for old useIRCLayout setting + if (settingName === "layout") { + const settings = this.getSettings() || {}; + if (settings["useIRCLayout"]) return Layout.IRC; + return settings[settingName]; + } + const settings = this.getSettings() || {}; return settings[settingName]; } @@ -106,6 +114,18 @@ export default class DeviceSettingsHandler extends SettingsHandler { return Promise.resolve(); } + // Special case for old useIRCLayout setting + if (settingName === "layout") { + const settings = this.getSettings() || {}; + + delete settings["useIRCLayout"]; + settings["layout"] = newValue; + localStorage.setItem("mx_local_settings", JSON.stringify(settings)); + + this.watchers.notifyUpdate(settingName, null, SettingLevel.DEVICE, newValue); + return Promise.resolve(); + } + const settings = this.getSettings() || {}; settings[settingName] = newValue; localStorage.setItem("mx_local_settings", JSON.stringify(settings));