diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 301f712ffb..159cfc0aad 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -$name-width: 70px; $icon-width: 14px; $timestamp-width: 45px; $right-padding: 5px; $irc-line-height: $font-18px; .mx_IRCLayout { + --name-width: 70px; line-height: $irc-line-height !important; @@ -48,7 +48,7 @@ $irc-line-height: $font-18px; > .mx_SenderProfile { order: 2; flex-shrink: 0; - width: $name-width; + width: var(--name-width); text-overflow: ellipsis; text-align: right; display: flex; @@ -122,7 +122,7 @@ $irc-line-height: $font-18px; .mx_EventTile_emote { > .mx_EventTile_avatar { - margin-left: calc($name-width + $icon-width + $right-padding); + margin-left: calc(var(--name-width) + $icon-width + $right-padding); } } @@ -132,7 +132,7 @@ $irc-line-height: $font-18px; .mx_EventListSummary { > .mx_EventTile_line { - padding-left: calc($name-width + $icon-width + $timestamp-width + 3 * $right-padding); // 15 px of padding + padding-left: calc(var(--name-width) + $icon-width + $timestamp-width + 3 * $right-padding); // 15 px of padding } .mx_EventListSummary_avatars { @@ -143,12 +143,12 @@ $irc-line-height: $font-18px; .mx_EventTile.mx_EventTile_info { .mx_EventTile_avatar { - left: calc($name-width + 10px + $icon-width); + left: calc(var(--name-width) + 10px + $icon-width); top: 0; } .mx_EventTile_line { - left: calc($name-width + 10px + $icon-width); + left: calc(var(--name-width) + 10px + $icon-width); } .mx_TextualEvent { @@ -198,7 +198,16 @@ $irc-line-height: $font-18px; margin: 0; .mx_SenderProfile { width: unset; - max-width: $name-width; + max-width: var(--name-width); } } + + .mx_ProfileResizer { + position: absolute; + height: 100%; + width: 15px; + left: calc(80px + var(--name-width)); + cursor: col-resize; + z-index: 100; + } } diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index f46df0175a..1c10efb346 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -29,6 +29,7 @@ import SettingsStore from '../../settings/SettingsStore'; import {_t} from "../../languageHandler"; import {haveTileForEvent} from "../views/rooms/EventTile"; import {textForEvent} from "../../TextForEvent"; +import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer"; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes const continuedTypes = ['m.sticker', 'm.room.message']; @@ -819,6 +820,11 @@ export default class MessagePanel extends React.Component { ); } + let ircResizer = null; + if (this.state.useIRCLayout) { + ircResizer = ; + } + return ( { topSpinner } { this._getEventTiles() } diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 4f44c1a169..cb0114b243 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -144,6 +144,11 @@ export default createReactClass({ /* resizeNotifier: ResizeNotifier to know when middle column has changed size */ resizeNotifier: PropTypes.object, + + /* fixedChildren: allows for children to be passed which are rendered outside + * of the wrapper + */ + fixedChildren: PropTypes.node, }, getDefaultProps: function() { @@ -881,6 +886,7 @@ export default createReactClass({ return ( + { this.props.fixedChildren }
    { this.props.children } diff --git a/src/components/views/elements/Draggable.tsx b/src/components/views/elements/Draggable.tsx new file mode 100644 index 0000000000..98f86fd524 --- /dev/null +++ b/src/components/views/elements/Draggable.tsx @@ -0,0 +1,84 @@ +/* +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'; + +interface IProps { + className: string, + dragFunc: (currentLocation: ILocationState, event: MouseEvent) => ILocationState, + onMouseUp: (event: MouseEvent) => void, +} + +interface IState { + onMouseMove: (event: MouseEvent) => void, + onMouseUp: (event: MouseEvent) => void, + location: ILocationState, +} + +export interface ILocationState { + currentX: number, + currentY: number, +} + +export default class Draggable extends React.Component { + + constructor(props: IProps) { + super(props); + + this.state = { + onMouseMove: this.onMouseMove.bind(this), + onMouseUp: this.onMouseUp.bind(this), + location: { + currentX: 0, + currentY: 0, + }, + }; + } + + private onMouseDown = (event: MouseEvent): void => { + this.setState({ + location: { + currentX: event.clientX, + currentY: event.clientY, + }, + }); + + document.addEventListener("mousemove", this.state.onMouseMove); + document.addEventListener("mouseup", this.state.onMouseUp); + console.log("Mouse down") + } + + private onMouseUp = (event: MouseEvent): void => { + document.removeEventListener("mousemove", this.state.onMouseMove); + document.removeEventListener("mouseup", this.state.onMouseUp); + this.props.onMouseUp(event); + console.log("Mouse up") + } + + private onMouseMove(event: MouseEvent): void { + console.log("Mouse Move") + const newLocation = this.props.dragFunc(this.state.location, event); + + this.setState({ + location: newLocation, + }); + } + + render() { + return
    + } + +} \ No newline at end of file diff --git a/src/components/views/elements/ErrorBoundary.js b/src/components/views/elements/ErrorBoundary.js index a043b350ab..1abd11f838 100644 --- a/src/components/views/elements/ErrorBoundary.js +++ b/src/components/views/elements/ErrorBoundary.js @@ -73,7 +73,7 @@ export default class ErrorBoundary extends React.PureComponent { if (this.state.error) { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const newIssueUrl = "https://github.com/vector-im/riot-web/issues/new"; - return
    + return

    {_t("Something went wrong!")}

    {_t( diff --git a/src/components/views/elements/IRCTimelineProfileResizer.tsx b/src/components/views/elements/IRCTimelineProfileResizer.tsx new file mode 100644 index 0000000000..80a86b2005 --- /dev/null +++ b/src/components/views/elements/IRCTimelineProfileResizer.tsx @@ -0,0 +1,86 @@ +/* +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 SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import Draggable, {ILocationState} from './Draggable'; + +interface IProps { + // Current room + roomId: string, + minWidth: number, + maxWidth: number, +}; + +interface IState { + width: number, + IRCLayoutRoot: HTMLElement, +}; + +export default class IRCTimelineProfileResizer extends React.Component { + constructor(props: IProps) { + super(props); + + this.state = { + width: SettingsStore.getValue("ircDisplayNameWidth", this.props.roomId), + IRCLayoutRoot: null, + } + }; + + componentDidMount() { + this.setState({ + IRCLayoutRoot: document.querySelector(".mx_IRCLayout") as HTMLElement, + }, () => this.updateCSSWidth(this.state.width)) + } + + private dragFunc = (location: ILocationState, event: React.MouseEvent): ILocationState => { + const offset = event.clientX - location.currentX; + const newWidth = this.state.width + offset; + + console.log({offset}) + // If we're trying to go smaller than min width, don't. + if (this.state.width <= this.props.minWidth && offset <= 0) { + return location; + } + + if (this.state.width >= this.props.maxWidth && offset >= 0) { + return location; + } + + this.setState({ + width: newWidth, + }); + + this.updateCSSWidth.bind(this)(newWidth); + + return { + currentX: event.clientX, + currentY: location.currentY, + } + } + + private updateCSSWidth(newWidth: number) { + this.state.IRCLayoutRoot.style.setProperty("--name-width", newWidth + "px"); + } + + private onMoueUp(event: MouseEvent) { + SettingsStore.setValue("ircDisplayNameWidth", this.props.roomId, SettingLevel.ROOM_DEVICE, this.state.width); + } + + render() { + return + } +}; \ No newline at end of file diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 032b0ee906..6d741ba3a5 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -531,4 +531,11 @@ export const SETTINGS = { MatrixClient.prototype.setCryptoTrustCrossSignedDevices, true, ), }, + "ircDisplayNameWidth": { + // We specifically want to have room-device > device so that users may set a device default + // with a per-room override. + supportedLevels: ['room-device', 'device'], + displayName: _td("IRC display name width"), + default: 80, + }, };