diff --git a/res/css/views/dialogs/_DevtoolsDialog.scss b/res/css/views/dialogs/_DevtoolsDialog.scss index 815e8408b5..2f01f3ecc6 100644 --- a/res/css/views/dialogs/_DevtoolsDialog.scss +++ b/res/css/views/dialogs/_DevtoolsDialog.scss @@ -18,7 +18,12 @@ limitations under the License. margin: 10px 0; } -.mx_DevTools_RoomStateExplorer_button, .mx_DevTools_RoomStateExplorer_query { +.mx_DevTools_ServersInRoomList_button { + /* Set the cursor back to default as `.mx_Dialog button` sets it to pointer */ + cursor: default !important; +} + +.mx_DevTools_RoomStateExplorer_button, .mx_DevTools_ServersInRoomList_button, .mx_DevTools_RoomStateExplorer_query { margin-bottom: 10px; width: 100%; } diff --git a/res/css/views/messages/_ReactionsRowButton.scss b/res/css/views/messages/_ReactionsRowButton.scss index 49e3930979..3c6d019b30 100644 --- a/res/css/views/messages/_ReactionsRowButton.scss +++ b/res/css/views/messages/_ReactionsRowButton.scss @@ -24,6 +24,7 @@ limitations under the License. border-radius: 10px; background-color: $reaction-row-button-bg-color; cursor: pointer; + user-select: none; &:hover { border-color: $reaction-row-button-hover-border-color; diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 763eddbd5d..cd40c7874e 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -175,6 +175,8 @@ class MatrixClientPeg { } _createClient(creds: MatrixClientCreds) { + const aggregateRelations = SettingsStore.isFeatureEnabled("feature_reactions"); + const opts = { baseUrl: creds.homeserverUrl, idBaseUrl: creds.identityServerUrl, @@ -183,7 +185,8 @@ class MatrixClientPeg { deviceId: creds.deviceId, timelineSupport: true, forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer', false), - verificationMethods: [verificationMethods.SAS] + verificationMethods: [verificationMethods.SAS], + unstableClientRelationAggregation: aggregateRelations, }; this.matrixClient = createMatrixClient(opts); diff --git a/src/SlashCommands.js b/src/SlashCommands.js index f72ba1e005..55107db899 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -1,6 +1,7 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2018 New Vector Ltd +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -30,6 +31,8 @@ import MultiInviter from './utils/MultiInviter'; import { linkifyAndSanitizeHtml } from './HtmlUtils'; import QuestionDialog from "./components/views/dialogs/QuestionDialog"; import WidgetUtils from "./utils/WidgetUtils"; +import {textToHtmlRainbow} from "./utils/colour"; +import Promise from "bluebird"; class Command { constructor({name, args='', description, runFn, hideCompletionAfterSpace=false}) { @@ -190,8 +193,8 @@ export const CommandMap = { }, }), - roomnick: new Command({ - name: 'roomnick', + myroomnick: new Command({ + name: 'myroomnick', args: '', description: _td('Changes your display nickname in the current room only'), runFn: function(roomId, args) { @@ -208,6 +211,47 @@ export const CommandMap = { }, }), + myroomavatar: new Command({ + name: 'myroomavatar', + args: '[]', + description: _td('Changes your avatar in this current room only'), + runFn: function(roomId, args) { + const cli = MatrixClientPeg.get(); + const room = cli.getRoom(roomId); + const userId = cli.getUserId(); + + let promise = Promise.resolve(args); + if (!args) { + promise = new Promise((resolve) => { + const fileSelector = document.createElement('input'); + fileSelector.setAttribute('type', 'file'); + fileSelector.onchange = (ev) => { + const file = ev.target.files[0]; + + const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog"); + Modal.createTrackedDialog('Upload Files confirmation', '', UploadConfirmDialog, { + file, + onFinished: (shouldContinue) => { + if (shouldContinue) resolve(cli.uploadContent(file)); + }, + }); + }; + + fileSelector.click(); + }); + } + + return success(promise.then((url) => { + const ev = room.currentState.getStateEvents('m.room.member', userId); + const content = { + ...ev ? ev.getContent() : { membership: 'join' }, + avatar_url: url, + }; + return cli.sendStateEvent(roomId, 'm.room.member', content, userId); + })); + }, + }), + tint: new Command({ name: 'tint', args: ' []', @@ -718,6 +762,26 @@ export const CommandMap = { return success(); }, }), + + rainbow: new Command({ + name: "rainbow", + description: _td("Sends the given message coloured as a rainbow"), + args: '', + runFn: function(roomId, args) { + if (!args) return reject(this.getUserId()); + return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, textToHtmlRainbow(args))); + }, + }), + + rainbowme: new Command({ + name: "rainbowme", + description: _td("Sends the given emote coloured as a rainbow"), + args: '', + runFn: function(roomId, args) { + if (!args) return reject(this.getUserId()); + return success(MatrixClientPeg.get().sendHtmlEmote(roomId, args, textToHtmlRainbow(args))); + }, + }), }; /* eslint-enable babel/no-invalid-this */ @@ -727,6 +791,7 @@ const aliases = { j: "join", newballsplease: "discardsession", goto: "join", // because it handles event permalinks magically + roomnick: "myroomnick", }; diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index b57b659136..2037217710 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -92,6 +92,9 @@ module.exports = React.createClass({ // show timestamps always alwaysShowTimestamps: PropTypes.bool, + + // helper function to access relations for an event + getRelationsForEvent: PropTypes.func, }, componentWillMount: function() { @@ -511,22 +514,27 @@ module.exports = React.createClass({ readReceipts = this._getReadReceiptsForEvent(mxEv); } ret.push( -
  • - -
  • , +
  • + +
  • , ); return ret; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index aa278f2349..17a062be98 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -1168,6 +1168,10 @@ const TimelinePanel = React.createClass({ }); }, + getRelationsForEvent(...args) { + return this.props.timelineSet.getRelationsForEvent(...args); + }, + render: function() { const MessagePanel = sdk.getComponent("structures.MessagePanel"); const Loader = sdk.getComponent("elements.Spinner"); @@ -1193,9 +1197,9 @@ const TimelinePanel = React.createClass({ if (this.state.events.length == 0 && !this.state.canBackPaginate && this.props.empty) { return ( -
    -
    { this.props.empty }
    -
    +
    +
    {this.props.empty}
    +
    ); } @@ -1217,28 +1221,29 @@ const TimelinePanel = React.createClass({ ); return (