From 4394a20f87922e768852dd693b7d3eb8ef3f90f4 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Tue, 18 Aug 2020 09:56:38 +0200 Subject: [PATCH 01/66] setting added to User Settings -> Preferences -> Timeline as an opt out for users with german translation --- .../views/settings/tabs/user/PreferencesUserSettingsTab.js | 1 + src/i18n/strings/de_DE.json | 3 ++- src/i18n/strings/en_EN.json | 1 + src/settings/Settings.ts | 5 +++++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index a77815a68c..6ed2fc2e39 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -49,6 +49,7 @@ export default class PreferencesUserSettingsTab extends React.Component { 'showAvatarChanges', 'showDisplaynameChanges', 'showImages', + 'dontShowChatEffects', ]; static ADVANCED_SETTINGS = [ diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 09dbcb2e18..edfe21d9d6 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -2361,5 +2361,6 @@ "%(brand)s encountered an error during upload of:": "%(brand)s hat einen Fehler festgestellt beim hochladen von:", "Use your account to sign in to the latest version of the app at ": "Verwende dein Konto um dich an der neusten Version der App anzumelden", "We’re excited to announce Riot is now Element!": "Wir freuen uns bekanntzugeben: Riot ist jetzt Element!", - "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot" + "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot", + "Don't show chat effects": "Chat Effekte nicht zeigen" } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 974a96406f..98aee655fe 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -503,6 +503,7 @@ "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", + "Don't show chat effects": "Don't show chat effects", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading report": "Uploading report", diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 714d80f983..59a3a4799b 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -586,4 +586,9 @@ export const SETTINGS: {[setting: string]: ISetting} = { displayName: _td("Enable experimental, compact IRC style layout"), default: false, }, + "dontShowChatEffects": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + displayName: _td("Don't show chat effects"), + default: false, + }, }; From ecd4d6e19ef58f6c0b99a94890a5cd82a53e7c2a Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Tue, 18 Aug 2020 17:57:51 +0200 Subject: [PATCH 02/66] test commit for confetti changes --- src/SlashCommands.tsx | 13 ++ src/components/structures/RoomView.js | 7 +- src/components/views/elements/Confetti.js | 209 ++++++++++++++++++++++ src/i18n/strings/de_DE.json | 3 +- src/i18n/strings/en_EN.json | 1 + 5 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 src/components/views/elements/Confetti.js diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 2063ad3149..2d4d484899 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -44,6 +44,7 @@ import { ensureDMExists } from "./createRoom"; import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload"; import { Action } from "./dispatcher/actions"; import { EffectiveMembership, getEffectiveMembership } from "./utils/membership"; +import {func} from "prop-types"; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -1026,6 +1027,18 @@ export const Commands = [ }, category: CommandCategories.actions, }), + new Command({ + command: "confetti", + description: _td("Throws confetti animation in the chat room"), + args: '/confetti + ', + runFn: function(roomId, args, command) { + return success((async () => { + const cli = MatrixClientPeg.get(); + await cli.sendHtmlMessage(roomId, args); + })()); + }, + category: CommandCategories.messages, + }), // Command definitions for autocompletion ONLY: // /me is special because its not handled by SlashCommands.js and is instead done inside the Composer classes diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 9a61523941..85cb1df848 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -57,6 +57,7 @@ import MatrixClientContext from "../../contexts/MatrixClientContext"; import { shieldStatusForRoom } from '../../utils/ShieldUtils'; import {Action} from "../../dispatcher/actions"; import {SettingLevel} from "../../settings/SettingLevel"; +import Confetti from "../views/elements/Confetti"; const DEBUG = false; let debuglog = function() {}; @@ -67,7 +68,7 @@ if (DEBUG) { // using bind means that we get to keep useful line numbers in the console debuglog = console.log.bind(console); } - +let confetti; export default createReactClass({ displayName: 'RoomView', propTypes: { @@ -624,12 +625,14 @@ export default createReactClass({ ev.preventDefault(); } }, - onAction: function(payload) { switch (payload.action) { case 'message_send_failed': case 'message_sent': this._checkIfAlone(this.state.room); + confetti = new Confetti('100', '100'); + console.log('confetti sent'); + confetti.animateConfetti('test', 'message'); break; case 'post_sticker_message': this.injectSticker( diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js new file mode 100644 index 0000000000..e9dc2c34c0 --- /dev/null +++ b/src/components/views/elements/Confetti.js @@ -0,0 +1,209 @@ +import React from "react"; +import SettingsStore from "../../../../lib/settings/SettingsStore"; +import PropTypes from "prop-types"; + +export default class Confetti extends React.Component { + displayName: 'confetti'; + constructor(props) { + super(props); + this.animateConfetti = this.animateConfetti.bind(this); + this.confetti.start = this.startConfetti; + this.startConfetti = this.startConfetti.bind(this); + this.confetti.stop = this.stopConfetti; + this.confetti.remove = this.removeConfetti; + this.confetti.isRunning = this.isConfettiRunning; + } + static propTypes = { + width: PropTypes.string.isRequired, + height: PropTypes.string.isRequired, + } + confetti = { + //set max confetti count + maxCount: 150, + //set the particle animation speed + speed: 3, + //the confetti animation frame interval in milliseconds + frameInterval: 15, + //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) + alpha: 1.0, + start: null, + }; + colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,", + "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,", + "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,", + "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"]; + streamingConfetti = false; + animationTimer = null; + lastFrameTime = Date.now(); + particles = []; + waveAngle = 0; + context = null; + supportsAnimationFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame; + + resetParticle(particle, width, height) { + particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.confetti.alpha + ")"); + particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.confetti.alpha + ")"); + particle.x = Math.random() * width; + particle.y = Math.random() * height - height; + particle.diameter = Math.random() * 10 + 5; + particle.tilt = Math.random() * 10 - 10; + particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05; + particle.tiltAngle = Math.random() * Math.PI; + return particle; + } + + startConfetti(timeout) { + const width = window.innerWidth; + const height = window.innerHeight; + window.requestAnimationFrame = () => { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback) { + return window.setTimeout(callback, this.confetti.frameInterval); + }; + }; + let canvas = document.getElementById("confetti-canvas"); + if (canvas === null) { + canvas = document.createElement("canvas"); + canvas.setAttribute("id", "confetti-canvas"); + canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0"); + document.body.prepend(canvas); + canvas.width = width; + canvas.height = height; + window.addEventListener("resize", function () { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + }, true); + this.context = canvas.getContext("2d"); + } else if (this.context === null) { + this.context = canvas.getContext("2d"); + } + const count = this.confetti.maxCount; + while (this.particles.length < count) { + this.particles.push(this.resetParticle({}, width, height)); + } + this.streamingConfetti = true; + this.runAnimation(); + if (timeout) { + window.setTimeout(this.stopConfetti, timeout); + } + } + + stopConfetti() { + this.streamingConfetti = false; + } + + runAnimation() { + if (this.particles.length === 0) { + this.context.clearRect(0, 0, window.innerWidth, window.innerHeight); + this.animationTimer = null; + } else { + const now = Date.now(); + const delta = now - this.lastFrameTime; + if (!this.supportsAnimationFrame || delta > this.confetti.frameInterval) { + this.context.clearRect(0, 0, window.innerWidth, window.innerHeight); + this.updateParticles(); + this.drawParticles(this.context); + this.lastFrameTime = now - (delta % this.confetti.frameInterval); + } + this.animationTimer = requestAnimationFrame(this.runAnimation); + } + } + + removeConfetti() { + stop(); + this.particles = []; + } + + isConfettiRunning() { + return this.streamingConfetti; + } + + drawParticles(context) { + let particle; + let x; + let x2; + let y2; + for (let i = 0; i < this.particles.length; i++) { + particle = this.particles[i]; + context.beginPath(); + context.lineWidth = particle.diameter; + x2 = particle.x + particle.tilt; + x = x2 + particle.diameter / 2; + y2 = particle.y + particle.tilt + particle.diameter / 2; + context.strokeStyle = particle.color; + context.moveTo(x, particle.y); + context.lineTo(x2, y2); + context.stroke(); + } + } + + updateParticles() { + const width = window.innerWidth; + const height = window.innerHeight; + let particle; + this.waveAngle += 0.01; + for (let i = 0; i < this.particles.length; i++) { + particle = this.particles[i]; + if (!this.streamingConfetti && particle.y < -15) { + particle.y = height + 100; + } else { + particle.tiltAngle += particle.tiltAngleIncrement; + particle.x += Math.sin(this.waveAngle) - 0.5; + particle.y += (Math.cos(this.waveAngle) + particle.diameter + this.confetti.speed) * 0.5; + particle.tilt = Math.sin(particle.tiltAngle) * 15; + } + if (particle.x > width + 20 || particle.x < -20 || particle.y > height) { + if (this.streamingConfetti && this.particles.length <= this.confetti.maxCount) { + this.resetParticle(particle, width, height); + } else { + this.particles.splice(i, 1); + i--; + } + } + } + } + + convertToHex(content) { + const contentBodyToHexArray = []; + let hex; + for (let i = 0; i < content.body.length; i++) { + hex = content.body.codePointAt(i).toString(16); + contentBodyToHexArray.push(hex); + } + return contentBodyToHexArray; + } + + isChatEffectsDisabled() { + console.log('return value', SettingsStore.getValue('dontShowChatEffects')); + return SettingsStore.getValue('dontShowChatEffects'); + } + + isConfettiEmoji(content) { + const hexArray = this.convertToHex(content); + return !!(hexArray.includes('1f389') || hexArray.includes('1f38a')); + } + + animateConfetti(userId, message) { + // const shortendUserId = userId.slice(1).split(":").slice(0, 1); + console.log('in animate confetti method'); + if (!this.isChatEffectsDisabled()) { + this.confetti.start(3000); + } + if (!message) { + return ('*' + userId + ' throws confetti '); + } + } + + render() { + return ( ); + } +} diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index edfe21d9d6..e4311c2111 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -2362,5 +2362,6 @@ "Use your account to sign in to the latest version of the app at ": "Verwende dein Konto um dich an der neusten Version der App anzumelden", "We’re excited to announce Riot is now Element!": "Wir freuen uns bekanntzugeben: Riot ist jetzt Element!", "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot", - "Don't show chat effects": "Chat Effekte nicht zeigen" + "Don't show chat effects": "Chat Effekte nicht zeigen", + "Throws confetti animation in the chat room": "Throws confetti animation in the chat room" } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 98aee655fe..f09ec685ee 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -213,6 +213,7 @@ "Thank you!": "Thank you!", "Opens chat with the given user": "Opens chat with the given user", "Sends a message to the given user": "Sends a message to the given user", + "Throws confetti animation in the chat room": "Throws confetti animation in the chat room", "Displays action": "Displays action", "Reason": "Reason", "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", From 69227dd456bb9d7d78e16157dcabda2603345ae3 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Mon, 24 Aug 2020 10:26:20 +0200 Subject: [PATCH 03/66] translations added --- src/i18n/strings/de_DE.json | 3 ++- src/i18n/strings/en_EN.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index e4311c2111..5e5639942b 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -2363,5 +2363,6 @@ "We’re excited to announce Riot is now Element!": "Wir freuen uns bekanntzugeben: Riot ist jetzt Element!", "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot", "Don't show chat effects": "Chat Effekte nicht zeigen", - "Throws confetti animation in the chat room": "Throws confetti animation in the chat room" + "Sends the given message with confetti": "Sendet die Nachricht mit Konfetti", + " sends confetti": " sendet Konfetti" } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f09ec685ee..efd68d06a6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -213,7 +213,8 @@ "Thank you!": "Thank you!", "Opens chat with the given user": "Opens chat with the given user", "Sends a message to the given user": "Sends a message to the given user", - "Throws confetti animation in the chat room": "Throws confetti animation in the chat room", + "Sends the given message with confetti": "Sends the given message with confetti", + " sends confetti": " sends confetti", "Displays action": "Displays action", "Reason": "Reason", "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", From 34cee20140d4da373fc0be630da4e11709409ed9 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Mon, 24 Aug 2020 10:43:41 +0200 Subject: [PATCH 04/66] added confetti on command /confetti --- src/SlashCommands.tsx | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 2d4d484899..8322512b73 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -45,6 +45,7 @@ import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload"; import { Action } from "./dispatcher/actions"; import { EffectiveMembership, getEffectiveMembership } from "./utils/membership"; import {func} from "prop-types"; +import SettingsStore from "./settings/SettingsStore"; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -1029,15 +1030,24 @@ export const Commands = [ }), new Command({ command: "confetti", - description: _td("Throws confetti animation in the chat room"), - args: '/confetti + ', - runFn: function(roomId, args, command) { + description: _td("Sends the given message with confetti"), + args: '', + runFn: function(roomId, args) { return success((async () => { - const cli = MatrixClientPeg.get(); - await cli.sendHtmlMessage(roomId, args); + const cli = MatrixClientPeg.get(); + const userId = cli.getUserId(); + const userName = userId.slice(1).split(":").slice(0, 1); + const isChatEffectsDisabled = SettingsStore.getValue('dontShowChatEffects'); + if (!args || isChatEffectsDisabled) { + args = '*' + userName + _td(' sends confetti'); + } + if (!isChatEffectsDisabled) { + dis.dispatch({action: 'confetti'}); + } + cli.sendHtmlMessage(roomId, args); })()); }, - category: CommandCategories.messages, + category: CommandCategories.actions, }), // Command definitions for autocompletion ONLY: From a7567b2e31bb403a05490a299e7ca17fd595760c Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Mon, 24 Aug 2020 10:44:32 +0200 Subject: [PATCH 05/66] confetti animationsd handeled on roomViewTimeline --- src/components/structures/RoomView.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 85cb1df848..e48063530d 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -57,7 +57,7 @@ import MatrixClientContext from "../../contexts/MatrixClientContext"; import { shieldStatusForRoom } from '../../utils/ShieldUtils'; import {Action} from "../../dispatcher/actions"; import {SettingLevel} from "../../settings/SettingLevel"; -import Confetti from "../views/elements/Confetti"; +import {animateConfetti, forceStopConfetti} from "../views/elements/Confetti"; const DEBUG = false; let debuglog = function() {}; @@ -68,7 +68,6 @@ if (DEBUG) { // using bind means that we get to keep useful line numbers in the console debuglog = console.log.bind(console); } -let confetti; export default createReactClass({ displayName: 'RoomView', propTypes: { @@ -511,6 +510,7 @@ export default createReactClass({ this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged); + this.context.removeListener("Event.decrypted", this.onEventDecrypted); } window.removeEventListener('beforeunload', this.onPageUnload); @@ -630,9 +630,9 @@ export default createReactClass({ case 'message_send_failed': case 'message_sent': this._checkIfAlone(this.state.room); - confetti = new Confetti('100', '100'); - console.log('confetti sent'); - confetti.animateConfetti('test', 'message'); + break; + case 'confetti': + animateConfetti(this._roomView.current.offsetWidth); break; case 'post_sticker_message': this.injectSticker( @@ -750,6 +750,18 @@ export default createReactClass({ }); } } + if (!SettingsStore.getValue('dontShowChatEffects')) { + this.context.on('Event.decrypted', this.onEventDecrypted); + } + }, + onEventDecrypted(ev) { + if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; + this.handleConfetti(); + }, + handleConfetti() { + if (this.context.isInitialSyncComplete()) { + dis.dispatch({action: 'confetti'}); + } }, onRoomName: function(room) { @@ -786,6 +798,7 @@ export default createReactClass({ this._calculateRecommendedVersion(room); this._updateE2EStatus(room); this._updatePermissions(room); + forceStopConfetti(); }, _calculateRecommendedVersion: async function(room) { From 77de63bf4b06c5235e00c74673f2c0082a064195 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Mon, 24 Aug 2020 10:44:49 +0200 Subject: [PATCH 06/66] confetti file added --- src/components/views/elements/Confetti.js | 252 +++++++++++----------- 1 file changed, 121 insertions(+), 131 deletions(-) diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js index e9dc2c34c0..df2b004ce0 100644 --- a/src/components/views/elements/Confetti.js +++ b/src/components/views/elements/Confetti.js @@ -1,52 +1,48 @@ -import React from "react"; -import SettingsStore from "../../../../lib/settings/SettingsStore"; -import PropTypes from "prop-types"; +const confetti = { + //set max confetti count + maxCount: 150, + //syarn addet the particle animation speed + speed: 3, + //the confetti animation frame interval in milliseconds + frameInterval: 15, + //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) + alpha: 1.0, + //call to start confetti animation (with optional timeout in milliseconds) + start: null, + //call to stop adding confetti + stop: null, + //call to stop the confetti animation and remove all confetti immediately + remove: null, + isRunning: null, + //call and returns true or false depending on whether the animation is running + animate: null, +}; -export default class Confetti extends React.Component { - displayName: 'confetti'; - constructor(props) { - super(props); - this.animateConfetti = this.animateConfetti.bind(this); - this.confetti.start = this.startConfetti; - this.startConfetti = this.startConfetti.bind(this); - this.confetti.stop = this.stopConfetti; - this.confetti.remove = this.removeConfetti; - this.confetti.isRunning = this.isConfettiRunning; - } - static propTypes = { - width: PropTypes.string.isRequired, - height: PropTypes.string.isRequired, - } - confetti = { - //set max confetti count - maxCount: 150, - //set the particle animation speed - speed: 3, - //the confetti animation frame interval in milliseconds - frameInterval: 15, - //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) - alpha: 1.0, - start: null, - }; - colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,", - "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,", - "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,", - "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"]; - streamingConfetti = false; - animationTimer = null; - lastFrameTime = Date.now(); - particles = []; - waveAngle = 0; - context = null; - supportsAnimationFrame = window.requestAnimationFrame || +(function() { + confetti.start = startConfetti; + confetti.stop = stopConfetti; + confetti.remove = removeConfetti; + confetti.isRunning = isConfettiRunning; + confetti.animate = animateConfetti; + const supportsAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame; + const colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,", + "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,", + "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,", + "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"]; + let streamingConfetti = false; + let animationTimer = null; + let lastFrameTime = Date.now(); + let particles = []; + let waveAngle = 0; + let context = null; - resetParticle(particle, width, height) { - particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.confetti.alpha + ")"); - particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.confetti.alpha + ")"); + function resetParticle(particle, width, height) { + particle.color = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")"); + particle.color2 = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")"); particle.x = Math.random() * width; particle.y = Math.random() * height - height; particle.diameter = Math.random() * 10 + 5; @@ -56,154 +52,148 @@ export default class Confetti extends React.Component { return particle; } - startConfetti(timeout) { - const width = window.innerWidth; + function runAnimation() { + if (particles.length === 0) { + context.clearRect(0, 0, window.innerWidth, window.innerHeight); + animationTimer = null; + } else { + const now = Date.now(); + const delta = now - lastFrameTime; + if (!supportsAnimationFrame || delta > confetti.frameInterval) { + context.clearRect(0, 0, window.innerWidth, window.innerHeight); + updateParticles(); + drawParticles(context); + lastFrameTime = now - (delta % confetti.frameInterval); + } + animationTimer = requestAnimationFrame(runAnimation); + } + } + + function startConfetti(roomWidth, timeout) { + const width = roomWidth; const height = window.innerHeight; - window.requestAnimationFrame = () => { + window.requestAnimationFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, this.confetti.frameInterval); + function (callback) { + return window.setTimeout(callback, confetti.frameInterval); }; - }; + })(); let canvas = document.getElementById("confetti-canvas"); if (canvas === null) { canvas = document.createElement("canvas"); canvas.setAttribute("id", "confetti-canvas"); - canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0"); + canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0; right:0"); document.body.prepend(canvas); canvas.width = width; canvas.height = height; - window.addEventListener("resize", function () { - canvas.width = window.innerWidth; + window.addEventListener("resize", function() { + canvas.width = roomWidth; canvas.height = window.innerHeight; }, true); - this.context = canvas.getContext("2d"); - } else if (this.context === null) { - this.context = canvas.getContext("2d"); + context = canvas.getContext("2d"); + } else if (context === null) { + context = canvas.getContext("2d"); } - const count = this.confetti.maxCount; - while (this.particles.length < count) { - this.particles.push(this.resetParticle({}, width, height)); + const count = confetti.maxCount; + while (particles.length < count) { + particles.push(resetParticle({}, width, height)); } - this.streamingConfetti = true; - this.runAnimation(); + streamingConfetti = true; + runAnimation(); if (timeout) { - window.setTimeout(this.stopConfetti, timeout); + window.setTimeout(stopConfetti, timeout); } } - stopConfetti() { - this.streamingConfetti = false; + function stopConfetti() { + streamingConfetti = false; } - runAnimation() { - if (this.particles.length === 0) { - this.context.clearRect(0, 0, window.innerWidth, window.innerHeight); - this.animationTimer = null; - } else { - const now = Date.now(); - const delta = now - this.lastFrameTime; - if (!this.supportsAnimationFrame || delta > this.confetti.frameInterval) { - this.context.clearRect(0, 0, window.innerWidth, window.innerHeight); - this.updateParticles(); - this.drawParticles(this.context); - this.lastFrameTime = now - (delta % this.confetti.frameInterval); - } - this.animationTimer = requestAnimationFrame(this.runAnimation); - } - } - - removeConfetti() { + function removeConfetti() { stop(); - this.particles = []; + particles = []; } - isConfettiRunning() { - return this.streamingConfetti; + function isConfettiRunning() { + return streamingConfetti; } - drawParticles(context) { + function drawParticles(context) { let particle; - let x; - let x2; - let y2; - for (let i = 0; i < this.particles.length; i++) { - particle = this.particles[i]; + let x; let x2; let y2; + for (let i = 0; i < particles.length; i++) { + particle = particles[i]; context.beginPath(); context.lineWidth = particle.diameter; x2 = particle.x + particle.tilt; x = x2 + particle.diameter / 2; y2 = particle.y + particle.tilt + particle.diameter / 2; - context.strokeStyle = particle.color; + if (confetti.gradient) { + const gradient = context.createLinearGradient(x, particle.y, x2, y2); + gradient.addColorStop("0", particle.color); + gradient.addColorStop("1.0", particle.color2); + context.strokeStyle = gradient; + } else { + context.strokeStyle = particle.color; + } context.moveTo(x, particle.y); context.lineTo(x2, y2); context.stroke(); } } - updateParticles() { + function updateParticles() { const width = window.innerWidth; const height = window.innerHeight; let particle; - this.waveAngle += 0.01; - for (let i = 0; i < this.particles.length; i++) { - particle = this.particles[i]; - if (!this.streamingConfetti && particle.y < -15) { + waveAngle += 0.01; + for (let i = 0; i < particles.length; i++) { + particle = particles[i]; + if (!streamingConfetti && particle.y < -15) { particle.y = height + 100; } else { particle.tiltAngle += particle.tiltAngleIncrement; - particle.x += Math.sin(this.waveAngle) - 0.5; - particle.y += (Math.cos(this.waveAngle) + particle.diameter + this.confetti.speed) * 0.5; + particle.x += Math.sin(waveAngle) - 0.5; + particle.y += (Math.cos(waveAngle) + particle.diameter + confetti.speed) * 0.5; particle.tilt = Math.sin(particle.tiltAngle) * 15; } if (particle.x > width + 20 || particle.x < -20 || particle.y > height) { - if (this.streamingConfetti && this.particles.length <= this.confetti.maxCount) { - this.resetParticle(particle, width, height); + if (streamingConfetti && particles.length <= confetti.maxCount) { + resetParticle(particle, width, height); } else { - this.particles.splice(i, 1); + particles.splice(i, 1); i--; } } } } +})(); - convertToHex(content) { - const contentBodyToHexArray = []; - let hex; +export function convertToHex(content) { + const contentBodyToHexArray = []; + let hex; + if (content.body) { for (let i = 0; i < content.body.length; i++) { hex = content.body.codePointAt(i).toString(16); contentBodyToHexArray.push(hex); } - return contentBodyToHexArray; - } - - isChatEffectsDisabled() { - console.log('return value', SettingsStore.getValue('dontShowChatEffects')); - return SettingsStore.getValue('dontShowChatEffects'); - } - - isConfettiEmoji(content) { - const hexArray = this.convertToHex(content); - return !!(hexArray.includes('1f389') || hexArray.includes('1f38a')); - } - - animateConfetti(userId, message) { - // const shortendUserId = userId.slice(1).split(":").slice(0, 1); - console.log('in animate confetti method'); - if (!this.isChatEffectsDisabled()) { - this.confetti.start(3000); - } - if (!message) { - return ('*' + userId + ' throws confetti '); - } - } - - render() { - return ( ); } + return contentBodyToHexArray; +} + +export function isConfettiEmoji(content) { + const hexArray = convertToHex(content); + return !!(hexArray.includes('1f389') || hexArray.includes('1f38a')); +} + +export function animateConfetti(roomWidth) { + confetti.start(roomWidth, 3000); +} +export function forceStopConfetti() { + console.log('confetti should stop'); + confetti.remove(); } From 03b2a529ef681e0e0777af57418f74ebba458954 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Mon, 24 Aug 2020 11:29:46 +0200 Subject: [PATCH 07/66] remove unused var --- src/components/views/elements/Confetti.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js index df2b004ce0..371c26a4b5 100644 --- a/src/components/views/elements/Confetti.js +++ b/src/components/views/elements/Confetti.js @@ -34,7 +34,7 @@ const confetti = { "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,", "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"]; let streamingConfetti = false; - let animationTimer = null; + // let animationTimer = null; let lastFrameTime = Date.now(); let particles = []; let waveAngle = 0; @@ -55,7 +55,7 @@ const confetti = { function runAnimation() { if (particles.length === 0) { context.clearRect(0, 0, window.innerWidth, window.innerHeight); - animationTimer = null; + //animationTimer = null; } else { const now = Date.now(); const delta = now - lastFrameTime; @@ -65,7 +65,7 @@ const confetti = { drawParticles(context); lastFrameTime = now - (delta % confetti.frameInterval); } - animationTimer = requestAnimationFrame(runAnimation); + requestAnimationFrame(runAnimation); } } From 2a8b1e0ccd58628214a496b0b25f18ad96755997 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Mon, 24 Aug 2020 11:55:23 +0200 Subject: [PATCH 08/66] remove space before function parentheses and maximum allowed line --- src/components/views/elements/Confetti.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js index 371c26a4b5..7d4faa3a17 100644 --- a/src/components/views/elements/Confetti.js +++ b/src/components/views/elements/Confetti.js @@ -72,13 +72,13 @@ const confetti = { function startConfetti(roomWidth, timeout) { const width = roomWidth; const height = window.innerHeight; - window.requestAnimationFrame = (function () { + window.requestAnimationFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || - function (callback) { + function(callback) { return window.setTimeout(callback, confetti.frameInterval); }; })(); @@ -86,7 +86,8 @@ const confetti = { if (canvas === null) { canvas = document.createElement("canvas"); canvas.setAttribute("id", "confetti-canvas"); - canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0; right:0"); + canvas.setAttribute("style", + "display:block;z-index:999999;pointer-events:none;position:fixed;top:0; right:0"); document.body.prepend(canvas); canvas.width = width; canvas.height = height; From b79cf1e7ad00cd06ae0b38c8b37612877ec59481 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Mon, 24 Aug 2020 13:59:11 +0200 Subject: [PATCH 09/66] updated translated string --- src/SlashCommands.tsx | 2 +- src/i18n/strings/de_DE.json | 1 - src/i18n/strings/en_EN.json | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 8322512b73..ba0aea73f0 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -1039,7 +1039,7 @@ export const Commands = [ const userName = userId.slice(1).split(":").slice(0, 1); const isChatEffectsDisabled = SettingsStore.getValue('dontShowChatEffects'); if (!args || isChatEffectsDisabled) { - args = '*' + userName + _td(' sends confetti'); + args = _t("* %(userName)s sends confetti", {userName}); } if (!isChatEffectsDisabled) { dis.dispatch({action: 'confetti'}); diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 5e5639942b..46ce139e6e 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -2364,5 +2364,4 @@ "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot", "Don't show chat effects": "Chat Effekte nicht zeigen", "Sends the given message with confetti": "Sendet die Nachricht mit Konfetti", - " sends confetti": " sendet Konfetti" } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index efd68d06a6..78a2d51c56 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -214,7 +214,7 @@ "Opens chat with the given user": "Opens chat with the given user", "Sends a message to the given user": "Sends a message to the given user", "Sends the given message with confetti": "Sends the given message with confetti", - " sends confetti": " sends confetti", + "* %(userName)s sends confetti": "* %(userName)s sends confetti", "Displays action": "Displays action", "Reason": "Reason", "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", From 5b7ccb5a7837e134d28795b7cb8ddc68716ca7c2 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Mon, 24 Aug 2020 14:04:20 +0200 Subject: [PATCH 10/66] fix indentation spaces and readability line spaces --- src/components/structures/RoomView.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index e48063530d..240d300751 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -68,6 +68,7 @@ if (DEBUG) { // using bind means that we get to keep useful line numbers in the console debuglog = console.log.bind(console); } + export default createReactClass({ displayName: 'RoomView', propTypes: { @@ -624,6 +625,7 @@ export default createReactClass({ ev.stopPropagation(); ev.preventDefault(); } + }, onAction: function(payload) { switch (payload.action) { @@ -632,7 +634,7 @@ export default createReactClass({ this._checkIfAlone(this.state.room); break; case 'confetti': - animateConfetti(this._roomView.current.offsetWidth); + animateConfetti(this._roomView.current.offsetWidth); break; case 'post_sticker_message': this.injectSticker( @@ -756,12 +758,12 @@ export default createReactClass({ }, onEventDecrypted(ev) { if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; - this.handleConfetti(); + this.handleConfetti(); }, handleConfetti() { if (this.context.isInitialSyncComplete()) { - dis.dispatch({action: 'confetti'}); - } + dis.dispatch({action: 'confetti'}); + } }, onRoomName: function(room) { From f1c7139711f87dd818f9143fc6ec032e9ce41509 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Mon, 24 Aug 2020 14:25:06 +0200 Subject: [PATCH 11/66] remove not needed comma --- src/i18n/strings/de_DE.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 46ce139e6e..b5a69d7e72 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -2363,5 +2363,5 @@ "We’re excited to announce Riot is now Element!": "Wir freuen uns bekanntzugeben: Riot ist jetzt Element!", "Learn more at element.io/previously-riot": "Erfahre mehr unter element.io/previously-riot", "Don't show chat effects": "Chat Effekte nicht zeigen", - "Sends the given message with confetti": "Sendet die Nachricht mit Konfetti", + "Sends the given message with confetti": "Sendet die Nachricht mit Konfetti" } From eef654e0e3189a8fcba03c8463d62ecbe2a3e745 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Tue, 25 Aug 2020 11:09:10 +0200 Subject: [PATCH 12/66] fix indentation and remove console.log --- src/components/views/elements/Confetti.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js index 7d4faa3a17..b0f88dedb7 100644 --- a/src/components/views/elements/Confetti.js +++ b/src/components/views/elements/Confetti.js @@ -192,9 +192,8 @@ export function isConfettiEmoji(content) { } export function animateConfetti(roomWidth) { - confetti.start(roomWidth, 3000); + confetti.start(roomWidth, 3000); } export function forceStopConfetti() { - console.log('confetti should stop'); confetti.remove(); } From d41ffb1b4be9eeff5330c9e3ca5891cf22bb7f46 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Tue, 25 Aug 2020 11:09:57 +0200 Subject: [PATCH 13/66] pass ev to handleConfetti in order to check content before dispatch --- src/components/structures/RoomView.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 240d300751..b24d6efa2a 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -57,7 +57,7 @@ import MatrixClientContext from "../../contexts/MatrixClientContext"; import { shieldStatusForRoom } from '../../utils/ShieldUtils'; import {Action} from "../../dispatcher/actions"; import {SettingLevel} from "../../settings/SettingLevel"; -import {animateConfetti, forceStopConfetti} from "../views/elements/Confetti"; +import {animateConfetti, forceStopConfetti, isConfettiEmoji} from "../views/elements/Confetti"; const DEBUG = false; let debuglog = function() {}; @@ -758,11 +758,13 @@ export default createReactClass({ }, onEventDecrypted(ev) { if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; - this.handleConfetti(); + this.handleConfetti(ev); }, - handleConfetti() { + handleConfetti(ev) { if (this.context.isInitialSyncComplete()) { - dis.dispatch({action: 'confetti'}); + if (isConfettiEmoji(ev.getContent())) { + dis.dispatch({action: 'confetti'}); + } } }, From 43f266bfe333c2b6c5ece0be86ea5089e5e11c80 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Tue, 25 Aug 2020 11:11:20 +0200 Subject: [PATCH 14/66] remove unused import fix if condition trying (pass the dispatcher to sendHtmlMessage) --- src/SlashCommands.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index ba0aea73f0..6b321ce092 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -44,7 +44,6 @@ import { ensureDMExists } from "./createRoom"; import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload"; import { Action } from "./dispatcher/actions"; import { EffectiveMembership, getEffectiveMembership } from "./utils/membership"; -import {func} from "prop-types"; import SettingsStore from "./settings/SettingsStore"; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 @@ -1038,13 +1037,11 @@ export const Commands = [ const userId = cli.getUserId(); const userName = userId.slice(1).split(":").slice(0, 1); const isChatEffectsDisabled = SettingsStore.getValue('dontShowChatEffects'); - if (!args || isChatEffectsDisabled) { + if ((!args) || (!args && isChatEffectsDisabled)) { args = _t("* %(userName)s sends confetti", {userName}); } - if (!isChatEffectsDisabled) { - dis.dispatch({action: 'confetti'}); - } - cli.sendHtmlMessage(roomId, args); + cli.sendHtmlMessage(roomId, args, + dis.dispatch({action: 'confetti'})); })()); }, category: CommandCategories.actions, From cc71531493df1e5e095b7f173909cbb4606a4f16 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Tue, 25 Aug 2020 13:36:04 +0200 Subject: [PATCH 15/66] reverted German language translations --- src/i18n/strings/de_DE.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 2cea1519df..3d5ba3722e 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -2409,6 +2409,4 @@ "If you cancel now, you may lose encrypted messages & data if you lose access to your logins.": "Wenn du jetzt abbrichst, kannst du verschlüsselte Nachrichten und Daten verlieren, wenn du den Zugriff auf deine Logins verlierst.", "You can also set up Secure Backup & manage your keys in Settings.": "Du kannst auch in den Einstellungen eine Sicherung erstellen & deine Schlüssel verwalten.", "Set up Secure backup": "Sicheres Backup einrichten" - "Don't show chat effects": "Chat Effekte nicht zeigen", - "Sends the given message with confetti": "Sendet die Nachricht mit Konfetti" } From 4527755f7e2df6f3b6622f1cd740469df608d587 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Tue, 25 Aug 2020 16:18:01 +0200 Subject: [PATCH 16/66] updated translation --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 029551eb34..223e063762 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -210,7 +210,7 @@ "Opens chat with the given user": "Opens chat with the given user", "Sends a message to the given user": "Sends a message to the given user", "Sends the given message with confetti": "Sends the given message with confetti", - "* %(userName)s sends confetti": "* %(userName)s sends confetti", + "sends confetti": "sends confetti", "Displays action": "Displays action", "Reason": "Reason", "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", From 5753c964317ab20b1682874416d00cdf9e6c5820 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Tue, 25 Aug 2020 16:39:57 +0200 Subject: [PATCH 17/66] a workaround to make ocnfetti work on recipient side. changed the implementation of on.Event.decrypted function --- src/SlashCommands.tsx | 13 +++++++------ src/components/structures/RoomView.js | 13 ++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index abd4f5449b..03aec46e46 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -1029,15 +1029,16 @@ export const Commands = [ args: '', runFn: function(roomId, args) { return success((async () => { - const cli = MatrixClientPeg.get(); - const userId = cli.getUserId(); - const userName = userId.slice(1).split(":").slice(0, 1); const isChatEffectsDisabled = SettingsStore.getValue('dontShowChatEffects'); if ((!args) || (!args && isChatEffectsDisabled)) { - args = _t("* %(userName)s sends confetti", {userName}); + args = _t("sends confetti"); + MatrixClientPeg.get().sendEmoteMessage(roomId, args); + } else { + MatrixClientPeg.get().sendHtmlMessage(roomId, args); + } + if (!isChatEffectsDisabled) { + dis.dispatch({action: 'confetti'}); } - cli.sendHtmlMessage(roomId, args, - dis.dispatch({action: 'confetti'})); })()); }, category: CommandCategories.actions, diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index dfc92526c7..d5ccbf1c8c 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -511,7 +511,6 @@ export default createReactClass({ this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged); - this.context.removeListener("Event.decrypted", this.onEventDecrypted); } window.removeEventListener('beforeunload', this.onPageUnload); @@ -753,16 +752,16 @@ export default createReactClass({ } } if (!SettingsStore.getValue('dontShowChatEffects')) { - this.context.on('Event.decrypted', this.onEventDecrypted); + this.context.on("Event.decrypted", (ev) => { + if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; + this.handleConfetti(ev); + }); } }, - onEventDecrypted(ev) { - if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; - this.handleConfetti(ev); - }, handleConfetti(ev) { if (this.context.isInitialSyncComplete()) { - if (isConfettiEmoji(ev.getContent())) { + const messageBody = _t('sends confetti'); + if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) { dis.dispatch({action: 'confetti'}); } } From 95051a42b1f2755f52a980ef4521edc88ab728b0 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Wed, 26 Aug 2020 18:56:23 +0200 Subject: [PATCH 18/66] checking for unreadMessages before sending confetti throwing the confetti on the sender's side change sendHtmlMessage to sendTextMessage in slashCommands --- src/SlashCommands.tsx | 2 +- src/components/structures/RoomView.js | 17 ++++++++++------- .../views/rooms/SendMessageComposer.js | 7 +++++++ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 03aec46e46..28eaa8123b 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -1034,7 +1034,7 @@ export const Commands = [ args = _t("sends confetti"); MatrixClientPeg.get().sendEmoteMessage(roomId, args); } else { - MatrixClientPeg.get().sendHtmlMessage(roomId, args); + MatrixClientPeg.get().sendTextMessage(roomId, args); } if (!isChatEffectsDisabled) { dis.dispatch({action: 'confetti'}); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index d5ccbf1c8c..92f43c75ca 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -189,6 +189,7 @@ export default createReactClass({ this.context.on("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.on("userTrustStatusChanged", this.onUserVerificationChanged); this.context.on("crossSigning.keysChanged", this.onCrossSigningKeysChanged); + this.context.on("Event.decrypted", this.onEventDecrypted); // Start listening for RoomViewStore updates this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate); this._rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this._onRightPanelStoreUpdate); @@ -511,6 +512,7 @@ export default createReactClass({ this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged); + this.context.removeListener("Event.decrypted", this.onEventDecrypted); } window.removeEventListener('beforeunload', this.onPageUnload); @@ -751,15 +753,16 @@ export default createReactClass({ }); } } - if (!SettingsStore.getValue('dontShowChatEffects')) { - this.context.on("Event.decrypted", (ev) => { - if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; - this.handleConfetti(ev); - }); - } + }, + onEventDecrypted(ev) { + if (!SettingsStore.getValue('dontShowChatEffects')) { + if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || + this.state.room.getUnreadNotificationCount() === 0) return; + this.handleConfetti(ev); + } }, handleConfetti(ev) { - if (this.context.isInitialSyncComplete()) { + if (this.state.matrixClientIsReady) { const messageBody = _t('sends confetti'); if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) { dis.dispatch({action: 'confetti'}); diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 6a7b2fc753..0b873a9bab 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -44,6 +44,8 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import RateLimitedFunc from '../../../ratelimitedfunc'; import {Action} from "../../../dispatcher/actions"; +import {isConfettiEmoji} from "../elements/Confetti"; +import SettingsStore from "../../../settings/SettingsStore"; function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) { const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent); @@ -313,6 +315,11 @@ export default class SendMessageComposer extends React.Component { }); } dis.dispatch({action: "message_sent"}); + if (!SettingsStore.getValue('dontShowChatEffects')) { + if (isConfettiEmoji(content)) { + dis.dispatch({action: 'confetti'}); + } + } } this.sendHistoryManager.save(this.model); From 43ff97c1789be080b8ec69e3045d7a262e3dfd31 Mon Sep 17 00:00:00 2001 From: Daniel Maslowski Date: Wed, 9 Sep 2020 20:35:26 +0200 Subject: [PATCH 19/66] Add support for giving reason when redacting Signed-off-by: Daniel Maslowski --- src/components/views/context_menus/MessageContextMenu.js | 4 +++- src/components/views/dialogs/ConfirmRedactDialog.js | 8 +++++--- src/i18n/strings/en_EN.json | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index d760c8defa..b6d27e45f9 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -145,7 +145,7 @@ export default class MessageContextMenu extends React.Component { onRedactClick = () => { const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog"); Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, { - onFinished: async (proceed) => { + onFinished: async (proceed, reason) => { if (!proceed) return; const cli = MatrixClientPeg.get(); @@ -153,6 +153,8 @@ export default class MessageContextMenu extends React.Component { await cli.redactEvent( this.props.mxEvent.getRoomId(), this.props.mxEvent.getId(), + undefined, + reason ? { reason } : {}, ); } catch (e) { const code = e.errcode || e.statusCode; diff --git a/src/components/views/dialogs/ConfirmRedactDialog.js b/src/components/views/dialogs/ConfirmRedactDialog.js index 3106df1d5b..2216f9a93a 100644 --- a/src/components/views/dialogs/ConfirmRedactDialog.js +++ b/src/components/views/dialogs/ConfirmRedactDialog.js @@ -23,15 +23,17 @@ import { _t } from '../../../languageHandler'; */ export default class ConfirmRedactDialog extends React.Component { render() { - const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog'); + const TextInputDialog = sdk.getComponent('views.dialogs.TextInputDialog'); return ( - - + ); } } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 65374ea3ec..ecc4bd2f4c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1624,6 +1624,7 @@ "Removing…": "Removing…", "Confirm Removal": "Confirm Removal", "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.", + "Reason (optional)": "Reason (optional)", "Clear all data in this session?": "Clear all data in this session?", "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.", "Clear all data": "Clear all data", From 6e97baf09f80364d157233a8567c2c1bc106f302 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 12:53:17 +0200 Subject: [PATCH 20/66] Added license --- src/components/views/elements/Confetti.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/components/views/elements/Confetti.js b/src/components/views/elements/Confetti.js index b0f88dedb7..bef67cddcc 100644 --- a/src/components/views/elements/Confetti.js +++ b/src/components/views/elements/Confetti.js @@ -1,3 +1,23 @@ +/* +MIT License +Copyright (c) 2018 MathuSum Mut +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + const confetti = { //set max confetti count maxCount: 150, From 3358ed27921d137a2880f00dc0a1c603126b9bca Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 12:57:57 +0200 Subject: [PATCH 21/66] Use arrow functions --- src/components/structures/RoomView.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 92f43c75ca..53a964fbb8 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -754,14 +754,14 @@ export default createReactClass({ } } }, - onEventDecrypted(ev) { - if (!SettingsStore.getValue('dontShowChatEffects')) { - if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || - this.state.room.getUnreadNotificationCount() === 0) return; - this.handleConfetti(ev); - } + onEventDecrypted: (ev) => { + if (!SettingsStore.getValue('dontShowChatEffects')) { + if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || + this.state.room.getUnreadNotificationCount() === 0) return; + this.handleConfetti(ev); + } }, - handleConfetti(ev) { + handleConfetti: (ev) => { if (this.state.matrixClientIsReady) { const messageBody = _t('sends confetti'); if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) { From 4106f70218ef41e6101752e388b54b481cbe0576 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 13:24:22 +0200 Subject: [PATCH 22/66] Fixed merge error --- src/settings/Settings.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 99be728acc..b9ad834c83 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -626,6 +626,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td("Don't show chat effects"), default: false, + }, "Widgets.pinned": { supportedLevels: LEVELS_ROOM_OR_ACCOUNT, default: {}, From 929cc48cef28431cc059055240396ec7a2d173bb Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 13:30:52 +0200 Subject: [PATCH 23/66] Fixed eslint error --- src/components/structures/RoomView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 709864bff6..7094a8de1b 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -247,7 +247,7 @@ export default class RoomView extends React.Component { this.context.on("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.on("userTrustStatusChanged", this.onUserVerificationChanged); this.context.on("crossSigning.keysChanged", this.onCrossSigningKeysChanged); - this.context.on("Event.decrypted", this.onEventDecrypted); + this.context.on("Event.decrypted", this.onEventDecrypted); this.context.on("event", this.onEvent); // Start listening for RoomViewStore updates this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate); From 3e8e817a3d51b11c6bffefcff2edbef0dd053e6f Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 13:35:18 +0200 Subject: [PATCH 24/66] Fixed merge error --- src/components/structures/RoomView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 7094a8de1b..f3ec8b8104 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -693,7 +693,7 @@ export default class RoomView extends React.Component { this.checkIfAlone(this.state.room); break; case 'confetti': - animateConfetti(this._roomView.current.offsetWidth); + animateConfetti(this.roomView.current.offsetWidth); break; case 'post_sticker_message': this.injectSticker( From 41160ff08e827e63851a3823be442d9395771ca6 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 13:54:09 +0200 Subject: [PATCH 25/66] Render confetti the react way --- src/components/structures/RoomView.tsx | 8 +++- src/components/views/elements/Confetti.js | 30 ++++---------- .../views/elements/ConfettiOverlay.tsx | 41 +++++++++++++++++++ 3 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 src/components/views/elements/ConfettiOverlay.tsx diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index f3ec8b8104..0905005cf7 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -73,6 +73,7 @@ import TintableSvg from "../views/elements/TintableSvg"; import {XOR} from "../../@types/common"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import { CallState, CallType, MatrixCall } from "matrix-js-sdk/lib/webrtc/call"; +import ConfettiOverlay from "../views/elements/ConfettiOverlay"; const DEBUG = false; let debuglog = function(msg: string) {}; @@ -693,7 +694,7 @@ export default class RoomView extends React.Component { this.checkIfAlone(this.state.room); break; case 'confetti': - animateConfetti(this.roomView.current.offsetWidth); + //TODO: animateConfetti(this.roomView.current.offsetWidth); break; case 'post_sticker_message': this.injectSticker( @@ -853,7 +854,7 @@ export default class RoomView extends React.Component { this.calculateRecommendedVersion(room); this.updateE2EStatus(room); this.updatePermissions(room); - forceStopConfetti(); + //TODO: forceStopConfetti(); }; private async calculateRecommendedVersion(room: Room) { @@ -2072,6 +2073,9 @@ export default class RoomView extends React.Component { return (
+ {this.roomView.current && + + } { + const resize = () => { + const canvas = canvasRef.current; + canvas.height = window.innerHeight; + }; + const canvas = canvasRef.current; + canvas.width = roomWidth; + canvas.height = window.innerHeight; + window.addEventListener("resize", resize, true); + animateConfetti(canvas, roomWidth); + return () => { + window.removeEventListener("resize", resize); + forceStopConfetti(); + }; + }, []); + // on roomWidth change + + useEffect(() => { + const canvas = canvasRef.current; + canvas.width = roomWidth; + }, [roomWidth]); + return ( + + ) +} \ No newline at end of file From 607e33febaa4a6142776fe0126f850d782cca143 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 21:25:01 +0200 Subject: [PATCH 26/66] Extensibility, TypeScript and lazy loading --- src/SlashCommands.tsx | 10 +- src/components/structures/RoomView.tsx | 38 +--- src/components/views/elements/Confetti.js | 207 ------------------ .../views/elements/ConfettiOverlay.tsx | 41 ---- .../views/elements/effects/EffectsOverlay.tsx | 77 +++++++ .../views/elements/effects/ICanvasEffect.ts | 5 + .../views/elements/effects/confetti/index.ts | 197 +++++++++++++++++ .../views/rooms/SendMessageComposer.js | 8 +- .../tabs/user/PreferencesUserSettingsTab.js | 2 +- src/i18n/strings/en_EN.json | 2 +- src/settings/Settings.ts | 6 +- 11 files changed, 296 insertions(+), 297 deletions(-) delete mode 100644 src/components/views/elements/Confetti.js delete mode 100644 src/components/views/elements/ConfettiOverlay.tsx create mode 100644 src/components/views/elements/effects/EffectsOverlay.tsx create mode 100644 src/components/views/elements/effects/ICanvasEffect.ts create mode 100644 src/components/views/elements/effects/confetti/index.ts diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 87dc1ccdfd..316249d74d 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -77,6 +77,7 @@ export const CommandCategories = { "actions": _td("Actions"), "admin": _td("Admin"), "advanced": _td("Advanced"), + "effects": _td("Effects"), "other": _td("Other"), }; @@ -1045,19 +1046,16 @@ export const Commands = [ args: '', runFn: function(roomId, args) { return success((async () => { - const isChatEffectsDisabled = SettingsStore.getValue('dontShowChatEffects'); - if ((!args) || (!args && isChatEffectsDisabled)) { + if (!args) { args = _t("sends confetti"); MatrixClientPeg.get().sendEmoteMessage(roomId, args); } else { MatrixClientPeg.get().sendTextMessage(roomId, args); } - if (!isChatEffectsDisabled) { - dis.dispatch({action: 'confetti'}); - } + dis.dispatch({action: 'effects.confetti'}); })()); }, - category: CommandCategories.actions, + category: CommandCategories.effects, }), // Command definitions for autocompletion ONLY: diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 0905005cf7..1b47386789 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -56,7 +56,6 @@ import MatrixClientContext from "../../contexts/MatrixClientContext"; import {E2EStatus, shieldStatusForRoom} from '../../utils/ShieldUtils'; import {Action} from "../../dispatcher/actions"; import {SettingLevel} from "../../settings/SettingLevel"; -import {animateConfetti, forceStopConfetti, isConfettiEmoji} from "../views/elements/Confetti"; import {RightPanelPhases} from "../../stores/RightPanelStorePhases"; import {IMatrixClientCreds} from "../../MatrixClientPeg"; import ScrollPanel from "./ScrollPanel"; @@ -73,7 +72,7 @@ import TintableSvg from "../views/elements/TintableSvg"; import {XOR} from "../../@types/common"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import { CallState, CallType, MatrixCall } from "matrix-js-sdk/lib/webrtc/call"; -import ConfettiOverlay from "../views/elements/ConfettiOverlay"; +import EffectsOverlay from "../views/elements/effects/EffectsOverlay"; const DEBUG = false; let debuglog = function(msg: string) {}; @@ -248,8 +247,6 @@ export default class RoomView extends React.Component { this.context.on("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.on("userTrustStatusChanged", this.onUserVerificationChanged); this.context.on("crossSigning.keysChanged", this.onCrossSigningKeysChanged); - this.context.on("Event.decrypted", this.onEventDecrypted); - this.context.on("event", this.onEvent); // Start listening for RoomViewStore updates this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate); this.rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelStoreUpdate); @@ -570,8 +567,6 @@ export default class RoomView extends React.Component { this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged); - this.context.removeListener("Event.decrypted", this.onEventDecrypted); - this.context.removeListener("event", this.onEvent); } window.removeEventListener('beforeunload', this.onPageUnload); @@ -693,9 +688,6 @@ export default class RoomView extends React.Component { case 'message_sent': this.checkIfAlone(this.state.room); break; - case 'confetti': - //TODO: animateConfetti(this.roomView.current.offsetWidth); - break; case 'post_sticker_message': this.injectSticker( payload.data.content.url, @@ -804,28 +796,6 @@ export default class RoomView extends React.Component { } }; - private onEventDecrypted = (ev) => { - if (!SettingsStore.getValue('dontShowChatEffects')) { - if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || - this.state.room.getUnreadNotificationCount() === 0) return; - this.handleConfetti(ev); - } - }; - - private onEvent = (ev) => { - if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; - this.handleConfetti(ev); - }; - - private handleConfetti = (ev) => { - if (this.state.matrixClientIsReady) { - const messageBody = _t('sends confetti'); - if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) { - dis.dispatch({action: 'confetti'}); - } - } - }; - private onRoomName = (room: Room) => { if (this.state.room && room.roomId == this.state.room.roomId) { this.forceUpdate(); @@ -2070,11 +2040,13 @@ export default class RoomView extends React.Component { mx_RoomView_inCall: Boolean(activeCall), }); + const showChatEffects = SettingsStore.getValue('showChatEffects'); + return (
- {this.roomView.current && - + {showChatEffects && this.roomView.current && + } confetti.frameInterval) { - context.clearRect(0, 0, window.innerWidth, window.innerHeight); - updateParticles(); - drawParticles(context); - lastFrameTime = now - (delta % confetti.frameInterval); - } - requestAnimationFrame(runAnimation); - } - } - - function startConfetti(canvas, roomWidth, timeout) { - window.requestAnimationFrame = (function() { - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, confetti.frameInterval); - }; - })(); - if (context === null) { - context = canvas.getContext("2d"); - } - const count = confetti.maxCount; - while (particles.length < count) { - particles.push(resetParticle({}, canvas.width, canvas.height)); - } - streamingConfetti = true; - runAnimation(); - if (timeout) { - window.setTimeout(stopConfetti, timeout); - } - } - - function stopConfetti() { - streamingConfetti = false; - } - - function removeConfetti() { - stop(); - particles = []; - } - - function isConfettiRunning() { - return streamingConfetti; - } - - function drawParticles(context) { - let particle; - let x; let x2; let y2; - for (let i = 0; i < particles.length; i++) { - particle = particles[i]; - context.beginPath(); - context.lineWidth = particle.diameter; - x2 = particle.x + particle.tilt; - x = x2 + particle.diameter / 2; - y2 = particle.y + particle.tilt + particle.diameter / 2; - if (confetti.gradient) { - const gradient = context.createLinearGradient(x, particle.y, x2, y2); - gradient.addColorStop("0", particle.color); - gradient.addColorStop("1.0", particle.color2); - context.strokeStyle = gradient; - } else { - context.strokeStyle = particle.color; - } - context.moveTo(x, particle.y); - context.lineTo(x2, y2); - context.stroke(); - } - } - - function updateParticles() { - const width = window.innerWidth; - const height = window.innerHeight; - let particle; - waveAngle += 0.01; - for (let i = 0; i < particles.length; i++) { - particle = particles[i]; - if (!streamingConfetti && particle.y < -15) { - particle.y = height + 100; - } else { - particle.tiltAngle += particle.tiltAngleIncrement; - particle.x += Math.sin(waveAngle) - 0.5; - particle.y += (Math.cos(waveAngle) + particle.diameter + confetti.speed) * 0.5; - particle.tilt = Math.sin(particle.tiltAngle) * 15; - } - if (particle.x > width + 20 || particle.x < -20 || particle.y > height) { - if (streamingConfetti && particles.length <= confetti.maxCount) { - resetParticle(particle, width, height); - } else { - particles.splice(i, 1); - i--; - } - } - } - } -})(); - -export function convertToHex(content) { - const contentBodyToHexArray = []; - let hex; - if (content.body) { - for (let i = 0; i < content.body.length; i++) { - hex = content.body.codePointAt(i).toString(16); - contentBodyToHexArray.push(hex); - } - } - return contentBodyToHexArray; -} - -export function isConfettiEmoji(content) { - const hexArray = convertToHex(content); - return !!(hexArray.includes('1f389') || hexArray.includes('1f38a')); -} - -export function animateConfetti(canvas, roomWidth) { - confetti.start(canvas, roomWidth, 3000); -} -export function forceStopConfetti() { - confetti.remove(); -} diff --git a/src/components/views/elements/ConfettiOverlay.tsx b/src/components/views/elements/ConfettiOverlay.tsx deleted file mode 100644 index 63d38d834c..0000000000 --- a/src/components/views/elements/ConfettiOverlay.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React, {useEffect, useRef} from 'react'; -import {animateConfetti, forceStopConfetti} from './Confetti.js'; - -export default function ConfettiOverlay({roomWidth}) { - const canvasRef = useRef(null); - // on mount - useEffect(() => { - const resize = () => { - const canvas = canvasRef.current; - canvas.height = window.innerHeight; - }; - const canvas = canvasRef.current; - canvas.width = roomWidth; - canvas.height = window.innerHeight; - window.addEventListener("resize", resize, true); - animateConfetti(canvas, roomWidth); - return () => { - window.removeEventListener("resize", resize); - forceStopConfetti(); - }; - }, []); - // on roomWidth change - - useEffect(() => { - const canvas = canvasRef.current; - canvas.width = roomWidth; - }, [roomWidth]); - return ( - - ) -} \ No newline at end of file diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx new file mode 100644 index 0000000000..1f8e7a97ad --- /dev/null +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -0,0 +1,77 @@ +import React, {FunctionComponent, useEffect, useRef} from 'react'; +import dis from '../../../../dispatcher/dispatcher'; +import ICanvasEffect from './ICanvasEffect.js'; + +type EffectsOverlayProps = { + roomWidth: number; +} + +const EffectsOverlay: FunctionComponent = ({roomWidth}) => { + const canvasRef = useRef(null); + const effectsRef = useRef>(new Map()); + + const resize = () => { + canvasRef.current.height = window.innerHeight; + }; + + const lazyLoadEffectModule = async (name: string): Promise => { + if(!name) return null; + let effect = effectsRef.current[name] ?? null; + if(effect === null) { + try { + var { default: Effect } = await import(`./${name}`); + effect = new Effect(); + effectsRef.current[name] = effect; + } catch (err) { + console.warn('Unable to load effect module at \'./${name}\'.', err) + } + } + return effect; + } + + const onAction = (payload: { action: string }) => { + const actionPrefix = 'effects.'; + if(payload.action.indexOf(actionPrefix) === 0) { + const effect = payload.action.substr(actionPrefix.length); + lazyLoadEffectModule(effect).then((module) => module?.start(canvasRef.current)); + } + }; + + // on mount + useEffect(() => { + const dispatcherRef = dis.register(onAction); + const canvas = canvasRef.current; + canvas.width = roomWidth; + canvas.height = window.innerHeight; + window.addEventListener('resize', resize, true); + + return () => { + dis.unregister(dispatcherRef); + window.removeEventListener('resize', resize); + for(const effect in effectsRef.current) { + effectsRef.current[effect]?.stop(); + } + }; + }, []); + + // on roomWidth change + useEffect(() => { + canvasRef.current.width = roomWidth; + }, [roomWidth]); + + return ( + + ) +} + +export default EffectsOverlay; \ No newline at end of file diff --git a/src/components/views/elements/effects/ICanvasEffect.ts b/src/components/views/elements/effects/ICanvasEffect.ts new file mode 100644 index 0000000000..c463235880 --- /dev/null +++ b/src/components/views/elements/effects/ICanvasEffect.ts @@ -0,0 +1,5 @@ +export default interface ICanvasEffect { + start: (canvas: HTMLCanvasElement, timeout?: number) => Promise, + stop: () => Promise, + isRunning: boolean +} \ No newline at end of file diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts new file mode 100644 index 0000000000..dd4e869078 --- /dev/null +++ b/src/components/views/elements/effects/confetti/index.ts @@ -0,0 +1,197 @@ +import ICanvasEffect from '../ICanvasEffect' + +declare global { + interface Window { + mozRequestAnimationFrame: any; + oRequestAnimationFrame: any; + msRequestAnimationFrame: any; + } +} + +export type ConfettiOptions = { + maxCount: number, + speed: number, + frameInterval: number, + alpha: number, + gradient: boolean, +} + +type ConfettiParticle = { + color: string, + color2: string, + x: number, + y: number, + diameter: number, + tilt: number, + tiltAngleIncrement: number, + tiltAngle: number, +} + +const DefaultOptions: ConfettiOptions = { + //set max confetti count + maxCount: 150, + //syarn addet the particle animation speed + speed: 3, + //the confetti animation frame interval in milliseconds + frameInterval: 15, + //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) + alpha: 1.0, + //use gradient instead of solid particle color + gradient: false, +}; + +export default class Confetti implements ICanvasEffect { + private readonly options: ConfettiOptions; + + constructor(options: ConfettiOptions = DefaultOptions) { + this.options = options; + } + + private context: CanvasRenderingContext2D | null; + private supportsAnimationFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame; + private colors = ['rgba(30,144,255,', 'rgba(107,142,35,', 'rgba(255,215,0,', + 'rgba(255,192,203,', 'rgba(106,90,205,', 'rgba(173,216,230,', + 'rgba(238,130,238,', 'rgba(152,251,152,', 'rgba(70,130,180,', + 'rgba(244,164,96,', 'rgba(210,105,30,', 'rgba(220,20,60,']; + + private lastFrameTime = Date.now(); + private particles: Array = []; + private waveAngle = 0; + + public isRunning: boolean; + + public start = async (canvas: HTMLCanvasElement, timeout?: number) => { + if(!canvas) { + return; + } + window.requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + return window.setTimeout(callback, this.options.frameInterval); + }; + })(); + if (this.context === null) { + this.context = canvas.getContext('2d'); + } + const count = this.options.maxCount; + while (this.particles.length < count) { + this.particles.push(this.resetParticle({} as ConfettiParticle, canvas.width, canvas.height)); + } + this.isRunning = true; + this.runAnimation(); + if (timeout) { + window.setTimeout(this.stop, timeout || 3000); + } + } + + public stop = async () => { + this.isRunning = false; + this.particles = []; + } + + private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => { + particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); + particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); + particle.x = Math.random() * width; + particle.y = Math.random() * height - height; + particle.diameter = Math.random() * 10 + 5; + particle.tilt = Math.random() * 10 - 10; + particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05; + particle.tiltAngle = Math.random() * Math.PI; + return particle; + } + + private runAnimation = (): void => { + if (this.particles.length === 0) { + this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height); + //animationTimer = null; + } else { + const now = Date.now(); + const delta = now - this.lastFrameTime; + if (!this.supportsAnimationFrame || delta > this.options.frameInterval) { + this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height); + this.updateParticles(); + this.drawParticles(this.context); + this.lastFrameTime = now - (delta % this.options.frameInterval); + } + requestAnimationFrame(this.runAnimation); + } + } + + + private drawParticles = (context: CanvasRenderingContext2D): void => { + let particle; + let x; let x2; let y2; + for (let i = 0; i < this.particles.length; i++) { + particle = this.particles[i]; + this.context.beginPath(); + context.lineWidth = particle.diameter; + x2 = particle.x + particle.tilt; + x = x2 + particle.diameter / 2; + y2 = particle.y + particle.tilt + particle.diameter / 2; + if (this.options.gradient) { + const gradient = context.createLinearGradient(x, particle.y, x2, y2); + gradient.addColorStop(0, particle.color); + gradient.addColorStop(1.0, particle.color2); + context.strokeStyle = gradient; + } else { + context.strokeStyle = particle.color; + } + context.moveTo(x, particle.y); + context.lineTo(x2, y2); + context.stroke(); + } + } + + private updateParticles = () => { + const width = this.context.canvas.width; + const height = this.context.canvas.height; + let particle: ConfettiParticle; + this.waveAngle += 0.01; + for (let i = 0; i < this.particles.length; i++) { + particle = this.particles[i]; + if (!this.isRunning && particle.y < -15) { + particle.y = height + 100; + } else { + particle.tiltAngle += particle.tiltAngleIncrement; + particle.x += Math.sin(this.waveAngle) - 0.5; + particle.y += (Math.cos(this.waveAngle) + particle.diameter + this.options.speed) * 0.5; + particle.tilt = Math.sin(particle.tiltAngle) * 15; + } + if (particle.x > width + 20 || particle.x < -20 || particle.y > height) { + if (this.isRunning && this.particles.length <= this.options.maxCount) { + this.resetParticle(particle, width, height); + } else { + this.particles.splice(i, 1); + i--; + } + } + } + } +} + +const convertToHex = (data: string): Array => { + const contentBodyToHexArray = []; + if (!data) return contentBodyToHexArray; + let hex; + if (data) { + for (let i = 0; i < data.length; i++) { + hex = data.codePointAt(i).toString(16); + contentBodyToHexArray.push(hex); + } + } + return contentBodyToHexArray; +} + +export const isConfettiEmoji = (content: { msgtype: string, body: string }): boolean => { + const hexArray = convertToHex(content.body); + return !!(hexArray.includes('1f389') || hexArray.includes('1f38a')); +} diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index d148d38b23..4fbea9d043 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -42,7 +42,7 @@ import {Key} from "../../../Keyboard"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import RateLimitedFunc from '../../../ratelimitedfunc'; import {Action} from "../../../dispatcher/actions"; -import {isConfettiEmoji} from "../elements/Confetti"; +import {isConfettiEmoji} from "../elements/effects/confetti"; import SettingsStore from "../../../settings/SettingsStore"; function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) { @@ -318,10 +318,8 @@ export default class SendMessageComposer extends React.Component { }); } dis.dispatch({action: "message_sent"}); - if (!SettingsStore.getValue('dontShowChatEffects')) { - if (isConfettiEmoji(content)) { - dis.dispatch({action: 'confetti'}); - } + if (isConfettiEmoji(content)) { + dis.dispatch({action: 'effects.confetti'}); } } diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index 95d0f4be46..078d4dd2c7 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -49,7 +49,7 @@ export default class PreferencesUserSettingsTab extends React.Component { 'showAvatarChanges', 'showDisplaynameChanges', 'showImages', - 'dontShowChatEffects', + 'showChatEffects', 'Pill.shouldShowPillAvatar', ]; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 4fc7a3ad25..c3943eb764 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -510,7 +510,7 @@ "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", - "Don't show chat effects": "Don't show chat effects", + "Show chat effects": "Show chat effects", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading logs": "Uploading logs", diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index b9ad834c83..ab4665c401 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -622,10 +622,10 @@ export const SETTINGS: {[setting: string]: ISetting} = { displayName: _td("Enable experimental, compact IRC style layout"), default: false, }, - "dontShowChatEffects": { + "showChatEffects": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, - displayName: _td("Don't show chat effects"), - default: false, + displayName: _td("Show chat effects"), + default: true, }, "Widgets.pinned": { supportedLevels: LEVELS_ROOM_OR_ACCOUNT, From 6d98335368b82ed6c7d5539bacfbd2f424fd8be7 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 21:28:22 +0200 Subject: [PATCH 27/66] Removed old todo --- src/components/structures/RoomView.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 1b47386789..2c3aa4793a 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -824,7 +824,6 @@ export default class RoomView extends React.Component { this.calculateRecommendedVersion(room); this.updateE2EStatus(room); this.updatePermissions(room); - //TODO: forceStopConfetti(); }; private async calculateRecommendedVersion(room: Room) { From 48633f76ab92c5238a4f2e4c99f73d2598129f64 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Mon, 19 Oct 2020 23:10:43 +0200 Subject: [PATCH 28/66] added event handling back --- src/SlashCommands.tsx | 2 +- src/components/structures/RoomView.tsx | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 316249d74d..68be5de0a0 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -1047,7 +1047,7 @@ export const Commands = [ runFn: function(roomId, args) { return success((async () => { if (!args) { - args = _t("sends confetti"); + args = "sends confetti"; MatrixClientPeg.get().sendEmoteMessage(roomId, args); } else { MatrixClientPeg.get().sendTextMessage(roomId, args); diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 2c3aa4793a..f975a7cc6e 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -73,6 +73,7 @@ import {XOR} from "../../@types/common"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import { CallState, CallType, MatrixCall } from "matrix-js-sdk/lib/webrtc/call"; import EffectsOverlay from "../views/elements/effects/EffectsOverlay"; +import { isConfettiEmoji } from '../views/elements/effects/confetti'; const DEBUG = false; let debuglog = function(msg: string) {}; @@ -247,6 +248,8 @@ export default class RoomView extends React.Component { this.context.on("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.on("userTrustStatusChanged", this.onUserVerificationChanged); this.context.on("crossSigning.keysChanged", this.onCrossSigningKeysChanged); + this.context.on("Event.decrypted", this.onEventDecrypted); + this.context.on("event", this.onEvent); // Start listening for RoomViewStore updates this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate); this.rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelStoreUpdate); @@ -567,6 +570,8 @@ export default class RoomView extends React.Component { this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged); + this.context.removeListener("Event.decrypted", this.onEventDecrypted); + this.context.removeListener("event", this.onEvent); } window.removeEventListener('beforeunload', this.onPageUnload); @@ -795,6 +800,26 @@ export default class RoomView extends React.Component { } } }; + + private onEventDecrypted = (ev) => { + if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || + this.state.room.getUnreadNotificationCount() === 0) return; + this.handleConfetti(ev); + }; + + private onEvent = (ev) => { + if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; + this.handleConfetti(ev); + }; + + private handleConfetti = (ev) => { + if (this.state.matrixClientIsReady) { + const messageBody = 'sends confetti'; + if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) { + dis.dispatch({action: 'effects.confetti'}); + } + } + }; private onRoomName = (room: Room) => { if (this.state.room && room.roomId == this.state.room.roomId) { From 1c6d28b861c8b6bee20c29056733ef08cc3dfa76 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Wed, 21 Oct 2020 13:37:36 +0200 Subject: [PATCH 29/66] refactoring roomView / slashCommands / SendMessageComposer with the new effects configurations and fix confetti animation timeout --- src/SlashCommands.tsx | 39 +++++++++++-------- src/components/structures/RoomView.tsx | 18 +++++---- .../views/elements/effects/ICanvasEffect.ts | 4 +- .../views/elements/effects/confetti/index.ts | 26 ++----------- .../views/elements/effects/effectUtilities.ts | 3 ++ .../views/elements/effects/index.ts | 11 ++++++ .../views/rooms/SendMessageComposer.js | 10 +++-- 7 files changed, 59 insertions(+), 52 deletions(-) create mode 100644 src/components/views/elements/effects/effectUtilities.ts create mode 100644 src/components/views/elements/effects/index.ts diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 68be5de0a0..3f51614028 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -46,6 +46,7 @@ import { EffectiveMembership, getEffectiveMembership, leaveRoomBehaviour } from import SdkConfig from "./SdkConfig"; import SettingsStore from "./settings/SettingsStore"; import {UIFeature} from "./settings/UIFeature"; +import effects from "./components/views/elements/effects" // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -1040,22 +1041,28 @@ export const Commands = [ }, category: CommandCategories.actions, }), - new Command({ - command: "confetti", - description: _td("Sends the given message with confetti"), - args: '', - runFn: function(roomId, args) { - return success((async () => { - if (!args) { - args = "sends confetti"; - MatrixClientPeg.get().sendEmoteMessage(roomId, args); - } else { - MatrixClientPeg.get().sendTextMessage(roomId, args); - } - dis.dispatch({action: 'effects.confetti'}); - })()); - }, - category: CommandCategories.effects, + ...effects.map((effect) => { + return new Command({ + command: effect.command, + description: effect.description(), + args: '', + runFn: function(roomId, args) { + return success((async () => { + if (!args) { + args = effect.fallbackMessage(); + MatrixClientPeg.get().sendEmoteMessage(roomId, args); + } else { + const content = { + msgtype: effect.msgType, + body: args, + }; + MatrixClientPeg.get().sendMessage(roomId, content); + } + dis.dispatch({action: `effects.${effect.command}`}); + })()); + }, + category: CommandCategories.effects, + }) }), // Command definitions for autocompletion ONLY: diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index f975a7cc6e..0b7aa08288 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -73,7 +73,8 @@ import {XOR} from "../../@types/common"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import { CallState, CallType, MatrixCall } from "matrix-js-sdk/lib/webrtc/call"; import EffectsOverlay from "../views/elements/effects/EffectsOverlay"; -import { isConfettiEmoji } from '../views/elements/effects/confetti'; +import {containsEmoji} from '../views/elements/effects/effectUtilities'; +import effects from '../views/elements/effects' const DEBUG = false; let debuglog = function(msg: string) {}; @@ -800,10 +801,9 @@ export default class RoomView extends React.Component { } } }; - + private onEventDecrypted = (ev) => { - if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || - this.state.room.getUnreadNotificationCount() === 0) return; + if (ev.isDecryptionFailure()) return; this.handleConfetti(ev); }; @@ -813,11 +813,13 @@ export default class RoomView extends React.Component { }; private handleConfetti = (ev) => { + if (this.state.room.getUnreadNotificationCount() === 0) return; if (this.state.matrixClientIsReady) { - const messageBody = 'sends confetti'; - if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) { - dis.dispatch({action: 'effects.confetti'}); - } + effects.map(effect => { + if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) { + dis.dispatch({action: `effects.${effect.command}`}); + } + }) } }; diff --git a/src/components/views/elements/effects/ICanvasEffect.ts b/src/components/views/elements/effects/ICanvasEffect.ts index c463235880..a8b9a83514 100644 --- a/src/components/views/elements/effects/ICanvasEffect.ts +++ b/src/components/views/elements/effects/ICanvasEffect.ts @@ -1,5 +1,5 @@ export default interface ICanvasEffect { - start: (canvas: HTMLCanvasElement, timeout?: number) => Promise, + start: (canvas: HTMLCanvasElement, timeout: number) => Promise, stop: () => Promise, isRunning: boolean -} \ No newline at end of file +} diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index dd4e869078..c5874311c5 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -47,7 +47,7 @@ export default class Confetti implements ICanvasEffect { this.options = options; } - private context: CanvasRenderingContext2D | null; + private context: CanvasRenderingContext2D | null = null; private supportsAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || @@ -64,7 +64,7 @@ export default class Confetti implements ICanvasEffect { public isRunning: boolean; - public start = async (canvas: HTMLCanvasElement, timeout?: number) => { + public start = async (canvas: HTMLCanvasElement, timeout = 3000) => { if(!canvas) { return; } @@ -88,13 +88,13 @@ export default class Confetti implements ICanvasEffect { this.isRunning = true; this.runAnimation(); if (timeout) { - window.setTimeout(this.stop, timeout || 3000); + window.setTimeout(this.stop, timeout); } } public stop = async () => { this.isRunning = false; - this.particles = []; + // this.particles = []; } private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => { @@ -177,21 +177,3 @@ export default class Confetti implements ICanvasEffect { } } } - -const convertToHex = (data: string): Array => { - const contentBodyToHexArray = []; - if (!data) return contentBodyToHexArray; - let hex; - if (data) { - for (let i = 0; i < data.length; i++) { - hex = data.codePointAt(i).toString(16); - contentBodyToHexArray.push(hex); - } - } - return contentBodyToHexArray; -} - -export const isConfettiEmoji = (content: { msgtype: string, body: string }): boolean => { - const hexArray = convertToHex(content.body); - return !!(hexArray.includes('1f389') || hexArray.includes('1f38a')); -} diff --git a/src/components/views/elements/effects/effectUtilities.ts b/src/components/views/elements/effects/effectUtilities.ts new file mode 100644 index 0000000000..927b445a61 --- /dev/null +++ b/src/components/views/elements/effects/effectUtilities.ts @@ -0,0 +1,3 @@ +export const containsEmoji = (content: { msgtype: string, body: string }, emojis: Array): boolean => { + return emojis.some((emoji) => content.body.includes(emoji)); +} diff --git a/src/components/views/elements/effects/index.ts b/src/components/views/elements/effects/index.ts new file mode 100644 index 0000000000..d4c12fa7ce --- /dev/null +++ b/src/components/views/elements/effects/index.ts @@ -0,0 +1,11 @@ +import {_t, _td} from "../../../../languageHandler"; + +export default [ + { + emojis: ['🎊', '🎉'], + msgType: 'nic.custom.confetti', + command: 'confetti', + description: () => _td("Sends the given message with confetti"), + fallbackMessage: () => _t("sends confetti") + " 🎉", + }, +] diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 4fbea9d043..94ad934067 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -42,8 +42,8 @@ import {Key} from "../../../Keyboard"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import RateLimitedFunc from '../../../ratelimitedfunc'; import {Action} from "../../../dispatcher/actions"; -import {isConfettiEmoji} from "../elements/effects/confetti"; -import SettingsStore from "../../../settings/SettingsStore"; +import {containsEmoji} from "../elements/effects/effectUtilities"; +import effects from '../elements/effects'; function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) { const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent); @@ -318,9 +318,11 @@ export default class SendMessageComposer extends React.Component { }); } dis.dispatch({action: "message_sent"}); - if (isConfettiEmoji(content)) { - dis.dispatch({action: 'effects.confetti'}); + effects.map( (effect) => { + if (containsEmoji(content, effect.emojis)) { + dis.dispatch({action: `effects.${effect.command}`}); } + }); } this.sendHistoryManager.save(this.model, replyToEvent); From d4ec1dd7750f5a901eaf079a7b5fd8153701aed3 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 13:56:58 +0200 Subject: [PATCH 30/66] Refactoring --- src/components/structures/RoomView.tsx | 2 +- .../views/elements/effects/confetti/index.ts | 1 - src/components/views/elements/effects/index.ts | 16 ++++++++++++++-- .../views/rooms/SendMessageComposer.js | 6 +++--- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 0b7aa08288..c84a3bf783 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -815,7 +815,7 @@ export default class RoomView extends React.Component { private handleConfetti = (ev) => { if (this.state.room.getUnreadNotificationCount() === 0) return; if (this.state.matrixClientIsReady) { - effects.map(effect => { + effects.forEach(effect => { if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) { dis.dispatch({action: `effects.${effect.command}`}); } diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index c5874311c5..e8a139387b 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -94,7 +94,6 @@ export default class Confetti implements ICanvasEffect { public stop = async () => { this.isRunning = false; - // this.particles = []; } private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => { diff --git a/src/components/views/elements/effects/index.ts b/src/components/views/elements/effects/index.ts index d4c12fa7ce..6311135c1e 100644 --- a/src/components/views/elements/effects/index.ts +++ b/src/components/views/elements/effects/index.ts @@ -1,6 +1,14 @@ import {_t, _td} from "../../../../languageHandler"; -export default [ +type Effect = { + emojis: Array; + msgType: string; + command: string; + description: () => string; + fallbackMessage: () => string; +} + +const effects: Array = [ { emojis: ['🎊', '🎉'], msgType: 'nic.custom.confetti', @@ -8,4 +16,8 @@ export default [ description: () => _td("Sends the given message with confetti"), fallbackMessage: () => _t("sends confetti") + " 🎉", }, -] +]; + +export default effects; + + diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 94ad934067..a413a9917c 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -318,10 +318,10 @@ export default class SendMessageComposer extends React.Component { }); } dis.dispatch({action: "message_sent"}); - effects.map( (effect) => { + effects.forEach((effect) => { if (containsEmoji(content, effect.emojis)) { - dis.dispatch({action: `effects.${effect.command}`}); - } + dis.dispatch({action: `effects.${effect.command}`}); + } }); } From 047c29731b319bdf48027ae1e2966100763530f4 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 14:15:27 +0200 Subject: [PATCH 31/66] Added missing translation --- src/components/views/elements/effects/ICanvasEffect.ts | 2 +- src/i18n/strings/en_EN.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/effects/ICanvasEffect.ts b/src/components/views/elements/effects/ICanvasEffect.ts index a8b9a83514..71210d7ec3 100644 --- a/src/components/views/elements/effects/ICanvasEffect.ts +++ b/src/components/views/elements/effects/ICanvasEffect.ts @@ -1,5 +1,5 @@ export default interface ICanvasEffect { - start: (canvas: HTMLCanvasElement, timeout: number) => Promise, + start: (canvas: HTMLCanvasElement, timeout?: number) => Promise, stop: () => Promise, isRunning: boolean } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c3943eb764..974658aa3d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -148,6 +148,7 @@ "Messages": "Messages", "Actions": "Actions", "Advanced": "Advanced", + "Effects": "Effects", "Other": "Other", "Command error": "Command error", "Usage": "Usage", @@ -211,8 +212,6 @@ "Send a bug report with logs": "Send a bug report with logs", "Opens chat with the given user": "Opens chat with the given user", "Sends a message to the given user": "Sends a message to the given user", - "Sends the given message with confetti": "Sends the given message with confetti", - "sends confetti": "sends confetti", "Displays action": "Displays action", "Reason": "Reason", "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", @@ -1580,6 +1579,8 @@ "Sign in with single sign-on": "Sign in with single sign-on", "And %(count)s more...|other": "And %(count)s more...", "Home": "Home", + "Sends the given message with confetti": "Sends the given message with confetti", + "sends confetti": "sends confetti", "Enter a server name": "Enter a server name", "Looks good": "Looks good", "Can't find this server or its room list": "Can't find this server or its room list", From c7d535d2d3d07ae6490365cb242d1f1e1f1d25a3 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 14:29:25 +0200 Subject: [PATCH 32/66] Fixed some formatting issues --- .../views/elements/effects/EffectsOverlay.tsx | 18 +++++++++--------- .../views/elements/effects/confetti/index.ts | 6 +++--- src/components/views/elements/effects/index.ts | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 1f8e7a97ad..437d1f127f 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -1,4 +1,4 @@ -import React, {FunctionComponent, useEffect, useRef} from 'react'; +import React, { FunctionComponent, useEffect, useRef } from 'react'; import dis from '../../../../dispatcher/dispatcher'; import ICanvasEffect from './ICanvasEffect.js'; @@ -6,7 +6,7 @@ type EffectsOverlayProps = { roomWidth: number; } -const EffectsOverlay: FunctionComponent = ({roomWidth}) => { +const EffectsOverlay: FunctionComponent = ({ roomWidth }) => { const canvasRef = useRef(null); const effectsRef = useRef>(new Map()); @@ -15,9 +15,9 @@ const EffectsOverlay: FunctionComponent = ({roomWidth}) => }; const lazyLoadEffectModule = async (name: string): Promise => { - if(!name) return null; + if (!name) return null; let effect = effectsRef.current[name] ?? null; - if(effect === null) { + if (effect === null) { try { var { default: Effect } = await import(`./${name}`); effect = new Effect(); @@ -31,7 +31,7 @@ const EffectsOverlay: FunctionComponent = ({roomWidth}) => const onAction = (payload: { action: string }) => { const actionPrefix = 'effects.'; - if(payload.action.indexOf(actionPrefix) === 0) { + if (payload.action.indexOf(actionPrefix) === 0) { const effect = payload.action.substr(actionPrefix.length); lazyLoadEffectModule(effect).then((module) => module?.start(canvasRef.current)); } @@ -44,11 +44,11 @@ const EffectsOverlay: FunctionComponent = ({roomWidth}) => canvas.width = roomWidth; canvas.height = window.innerHeight; window.addEventListener('resize', resize, true); - - return () => { + + return () => { dis.unregister(dispatcherRef); window.removeEventListener('resize', resize); - for(const effect in effectsRef.current) { + for (const effect in effectsRef.current) { effectsRef.current[effect]?.stop(); } }; @@ -58,7 +58,7 @@ const EffectsOverlay: FunctionComponent = ({roomWidth}) => useEffect(() => { canvasRef.current.width = roomWidth; }, [roomWidth]); - + return ( { - if(!canvas) { + if (!canvas) { return; } - window.requestAnimationFrame = (function () { + window.requestAnimationFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || - function (callback) { + function(callback) { return window.setTimeout(callback, this.options.frameInterval); }; })(); diff --git a/src/components/views/elements/effects/index.ts b/src/components/views/elements/effects/index.ts index 6311135c1e..8a95b1c9d0 100644 --- a/src/components/views/elements/effects/index.ts +++ b/src/components/views/elements/effects/index.ts @@ -1,4 +1,4 @@ -import {_t, _td} from "../../../../languageHandler"; +import { _t, _td } from "../../../../languageHandler"; type Effect = { emojis: Array; From 8728e12242d0a751407e9a2c08c95ecaa5844dfd Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 14:43:09 +0200 Subject: [PATCH 33/66] Some code optimizations --- .../views/elements/effects/EffectsOverlay.tsx | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 437d1f127f..4b40f7cbb1 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -10,16 +10,12 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = const canvasRef = useRef(null); const effectsRef = useRef>(new Map()); - const resize = () => { - canvasRef.current.height = window.innerHeight; - }; - const lazyLoadEffectModule = async (name: string): Promise => { if (!name) return null; let effect = effectsRef.current[name] ?? null; if (effect === null) { try { - var { default: Effect } = await import(`./${name}`); + const { default: Effect } = await import(`./${name}`); effect = new Effect(); effectsRef.current[name] = effect; } catch (err) { @@ -27,41 +23,41 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = } } return effect; - } - - const onAction = (payload: { action: string }) => { - const actionPrefix = 'effects.'; - if (payload.action.indexOf(actionPrefix) === 0) { - const effect = payload.action.substr(actionPrefix.length); - lazyLoadEffectModule(effect).then((module) => module?.start(canvasRef.current)); - } }; - // on mount useEffect(() => { + const resize = () => { + canvasRef.current.height = window.innerHeight; + }; + const onAction = (payload: { action: string }) => { + const actionPrefix = 'effects.'; + if (payload.action.indexOf(actionPrefix) === 0) { + const effect = payload.action.substr(actionPrefix.length); + lazyLoadEffectModule(effect).then((module) => module?.start(canvasRef.current)); + } + } const dispatcherRef = dis.register(onAction); const canvas = canvasRef.current; - canvas.width = roomWidth; canvas.height = window.innerHeight; window.addEventListener('resize', resize, true); return () => { dis.unregister(dispatcherRef); window.removeEventListener('resize', resize); - for (const effect in effectsRef.current) { - effectsRef.current[effect]?.stop(); + const currentEffects = effectsRef.current; + for (const effect in currentEffects) { + const effectModule: ICanvasEffect = currentEffects[effect]; + if(effectModule && effectModule.isRunning) { + effectModule.stop(); + } } }; }, []); - // on roomWidth change - useEffect(() => { - canvasRef.current.width = roomWidth; - }, [roomWidth]); - return ( = ({ roomWidth }) = ) } -export default EffectsOverlay; \ No newline at end of file +export default EffectsOverlay; From 88475617955c72a538269259a448f54f8e5e27fd Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 14:48:11 +0200 Subject: [PATCH 34/66] Added additional module exports --- src/components/views/elements/effects/EffectsOverlay.tsx | 2 +- src/components/views/elements/effects/confetti/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 4b40f7cbb1..0ff2b228ad 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -2,7 +2,7 @@ import React, { FunctionComponent, useEffect, useRef } from 'react'; import dis from '../../../../dispatcher/dispatcher'; import ICanvasEffect from './ICanvasEffect.js'; -type EffectsOverlayProps = { +export type EffectsOverlayProps = { roomWidth: number; } diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 3cb7db5ec4..e45961006b 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -27,7 +27,7 @@ type ConfettiParticle = { tiltAngle: number, } -const DefaultOptions: ConfettiOptions = { +export const DefaultOptions: ConfettiOptions = { //set max confetti count maxCount: 150, //syarn addet the particle animation speed From 6f4c5d1b080f8ddcbd6053f6778f52d6f15f2125 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 14:56:04 +0200 Subject: [PATCH 35/66] fixed build error --- src/components/views/elements/effects/EffectsOverlay.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 0ff2b228ad..803fd18042 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -43,11 +43,12 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = return () => { dis.unregister(dispatcherRef); - window.removeEventListener('resize', resize); - const currentEffects = effectsRef.current; + window.removeEventListener('resize', resize); + // eslint-disable-next-line react-hooks/exhaustive-deps + const currentEffects = effectsRef.current; // this is not a react node ref, warning can be safely ignored for (const effect in currentEffects) { const effectModule: ICanvasEffect = currentEffects[effect]; - if(effectModule && effectModule.isRunning) { + if (effectModule && effectModule.isRunning) { effectModule.stop(); } } From 906686b640ff22812f3cb628bd3f8267af6c8144 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 15:06:05 +0200 Subject: [PATCH 36/66] Fixed more linter warnings --- src/components/views/elements/effects/confetti/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index e45961006b..4537683030 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -68,13 +68,13 @@ export default class Confetti implements ICanvasEffect { if (!canvas) { return; } - window.requestAnimationFrame = (function() { + window.requestAnimationFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || - function(callback) { + function (callback) { return window.setTimeout(callback, this.options.frameInterval); }; })(); From 2f83771eab27a2e6441a34224c9acbd120c6c5b8 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 15:15:26 +0200 Subject: [PATCH 37/66] Fixed more eslint errors --- src/components/views/elements/effects/EffectsOverlay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 803fd18042..5ec3566f18 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -43,7 +43,7 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = return () => { dis.unregister(dispatcherRef); - window.removeEventListener('resize', resize); + window.removeEventListener('resize', resize); // eslint-disable-next-line react-hooks/exhaustive-deps const currentEffects = effectsRef.current; // this is not a react node ref, warning can be safely ignored for (const effect in currentEffects) { From 335774b6ff3bd558f11eea9ca58e2f1d7f73c262 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 16:03:22 +0200 Subject: [PATCH 38/66] Fixed more linter errors --- src/components/views/elements/effects/confetti/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 4537683030..7428651490 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -1,4 +1,4 @@ -import ICanvasEffect from '../ICanvasEffect' +import ICanvasEffect from '../ICanvasEffect'; declare global { interface Window { @@ -68,13 +68,13 @@ export default class Confetti implements ICanvasEffect { if (!canvas) { return; } - window.requestAnimationFrame = (function () { + window.requestAnimationFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || - function (callback) { + function(callback) { return window.setTimeout(callback, this.options.frameInterval); }; })(); From fbe2d7e0f86ceae6511b6dc553a70d428b773290 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 16:15:15 +0200 Subject: [PATCH 39/66] Optimized naming --- src/components/structures/RoomView.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 817b2d2cea..1a18ece008 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -822,15 +822,15 @@ export default class RoomView extends React.Component { private onEventDecrypted = (ev) => { if (ev.isDecryptionFailure()) return; - this.handleConfetti(ev); + this.handleEffects(ev); }; private onEvent = (ev) => { if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; - this.handleConfetti(ev); + this.handleEffects(ev); }; - private handleConfetti = (ev) => { + private handleEffects = (ev) => { if (this.state.room.getUnreadNotificationCount() === 0) return; if (this.state.matrixClientIsReady) { effects.forEach(effect => { From cb79e38377165b4cb233caa804fadc9272e5e2a8 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 17:04:01 +0200 Subject: [PATCH 40/66] Better initialization and check if canvas gets unmounted --- .../views/elements/effects/confetti/index.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 7428651490..b613c32043 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -78,9 +78,8 @@ export default class Confetti implements ICanvasEffect { return window.setTimeout(callback, this.options.frameInterval); }; })(); - if (this.context === null) { - this.context = canvas.getContext('2d'); - } + this.context = canvas.getContext('2d'); + this.particles = []; const count = this.options.maxCount; while (this.particles.length < count) { this.particles.push(this.resetParticle({} as ConfettiParticle, canvas.width, canvas.height)); @@ -109,9 +108,11 @@ export default class Confetti implements ICanvasEffect { } private runAnimation = (): void => { + if (!this.context || !this.context.canvas) { + return; + } if (this.particles.length === 0) { this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height); - //animationTimer = null; } else { const now = Date.now(); const delta = now - this.lastFrameTime; @@ -127,6 +128,9 @@ export default class Confetti implements ICanvasEffect { private drawParticles = (context: CanvasRenderingContext2D): void => { + if (!this.context || !this.context.canvas) { + return; + } let particle; let x; let x2; let y2; for (let i = 0; i < this.particles.length; i++) { @@ -151,6 +155,9 @@ export default class Confetti implements ICanvasEffect { } private updateParticles = () => { + if (!this.context || !this.context.canvas) { + return; + } const width = this.context.canvas.width; const height = this.context.canvas.height; let particle: ConfettiParticle; From 3ea4560019f111c5ce73382644d6d6aae9fb753a Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 17:58:54 +0200 Subject: [PATCH 41/66] Moved effect options to configuration --- .../views/elements/effects/EffectsOverlay.tsx | 12 +++++---- .../views/elements/effects/ICanvasEffect.ts | 4 +++ .../views/elements/effects/confetti/index.ts | 4 +-- .../views/elements/effects/index.ts | 25 ++++++++++++++++++- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/effects/EffectsOverlay.tsx index 5ec3566f18..b2ecec8753 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/effects/EffectsOverlay.tsx @@ -1,6 +1,7 @@ import React, { FunctionComponent, useEffect, useRef } from 'react'; import dis from '../../../../dispatcher/dispatcher'; -import ICanvasEffect from './ICanvasEffect.js'; +import ICanvasEffect, { ICanvasEffectConstructable } from './ICanvasEffect.js'; +import effects from './index' export type EffectsOverlayProps = { roomWidth: number; @@ -8,15 +9,16 @@ export type EffectsOverlayProps = { const EffectsOverlay: FunctionComponent = ({ roomWidth }) => { const canvasRef = useRef(null); - const effectsRef = useRef>(new Map()); + const effectsRef = useRef>(new Map()); const lazyLoadEffectModule = async (name: string): Promise => { if (!name) return null; - let effect = effectsRef.current[name] ?? null; + let effect: ICanvasEffect | null = effectsRef.current[name] || null; if (effect === null) { + const options = effects.find((e) => e.command === name)?.options try { - const { default: Effect } = await import(`./${name}`); - effect = new Effect(); + const { default: Effect }: { default: ICanvasEffectConstructable } = await import(`./${name}`); + effect = new Effect(options); effectsRef.current[name] = effect; } catch (err) { console.warn('Unable to load effect module at \'./${name}\'.', err) diff --git a/src/components/views/elements/effects/ICanvasEffect.ts b/src/components/views/elements/effects/ICanvasEffect.ts index 71210d7ec3..c2a3046c8f 100644 --- a/src/components/views/elements/effects/ICanvasEffect.ts +++ b/src/components/views/elements/effects/ICanvasEffect.ts @@ -1,3 +1,7 @@ +export interface ICanvasEffectConstructable { + new(options?: { [key: string]: any }): ICanvasEffect +} + export default interface ICanvasEffect { start: (canvas: HTMLCanvasElement, timeout?: number) => Promise, stop: () => Promise, diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index b613c32043..07b6f1632a 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -43,8 +43,8 @@ export const DefaultOptions: ConfettiOptions = { export default class Confetti implements ICanvasEffect { private readonly options: ConfettiOptions; - constructor(options: ConfettiOptions = DefaultOptions) { - this.options = options; + constructor(options: { [key: string]: any }) { + this.options = {...DefaultOptions, ...options}; } private context: CanvasRenderingContext2D | null = null; diff --git a/src/components/views/elements/effects/index.ts b/src/components/views/elements/effects/index.ts index 8a95b1c9d0..3986d6e841 100644 --- a/src/components/views/elements/effects/index.ts +++ b/src/components/views/elements/effects/index.ts @@ -1,11 +1,22 @@ import { _t, _td } from "../../../../languageHandler"; -type Effect = { +export type Effect = { emojis: Array; msgType: string; command: string; description: () => string; fallbackMessage: () => string; + options: { + [key: string]: any + } +} + +type ConfettiOptions = { + maxCount: number, + speed: number, + frameInterval: number, + alpha: number, + gradient: boolean, } const effects: Array = [ @@ -15,6 +26,18 @@ const effects: Array = [ command: 'confetti', description: () => _td("Sends the given message with confetti"), fallbackMessage: () => _t("sends confetti") + " 🎉", + options: { + //set max confetti count + maxCount: 150, + //syarn addet the particle animation speed + speed: 3, + //the confetti animation frame interval in milliseconds + frameInterval: 15, + //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) + alpha: 1.0, + //use gradient instead of solid particle color + gradient: false, + } as ConfettiOptions, }, ]; From 1c556c97d3a23d95389ef3f1ce7be642a1885195 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 21 Oct 2020 19:06:10 +0200 Subject: [PATCH 42/66] Added some code documentation --- .../views/elements/effects/ICanvasEffect.ts | 20 +++++++ .../views/elements/effects/confetti/index.ts | 20 +++++-- .../views/elements/effects/effectUtilities.ts | 5 ++ .../views/elements/effects/index.ts | 53 ++++++++++++++----- 4 files changed, 81 insertions(+), 17 deletions(-) diff --git a/src/components/views/elements/effects/ICanvasEffect.ts b/src/components/views/elements/effects/ICanvasEffect.ts index c2a3046c8f..400f42af73 100644 --- a/src/components/views/elements/effects/ICanvasEffect.ts +++ b/src/components/views/elements/effects/ICanvasEffect.ts @@ -1,9 +1,29 @@ +/** + * Defines the constructor of a canvas based room effect + */ export interface ICanvasEffectConstructable { + /** + * @param {{[key:string]:any}} options? Optional animation options + * @returns ICanvasEffect Returns a new instance of the canvas effect + */ new(options?: { [key: string]: any }): ICanvasEffect } +/** + * Defines the interface of a canvas based room effect + */ export default interface ICanvasEffect { + /** + * @param {HTMLCanvasElement} canvas The canvas instance as the render target of the animation + * @param {number} timeout? A timeout that defines the runtime of the animation (defaults to false) + */ start: (canvas: HTMLCanvasElement, timeout?: number) => Promise, + /** + * Stops the current animation + */ stop: () => Promise, + /** + * Returns a value that defines if the animation is currently running + */ isRunning: boolean } diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 07b6f1632a..29f70d1a57 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -9,10 +9,25 @@ declare global { } export type ConfettiOptions = { + /** + * max confetti count + */ maxCount: number, + /** + * particle animation speed + */ speed: number, + /** + * the confetti animation frame interval in milliseconds + */ frameInterval: number, + /** + * the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) + */ alpha: number, + /** + * use gradient instead of solid particle color + */ gradient: boolean, } @@ -28,15 +43,10 @@ type ConfettiParticle = { } export const DefaultOptions: ConfettiOptions = { - //set max confetti count maxCount: 150, - //syarn addet the particle animation speed speed: 3, - //the confetti animation frame interval in milliseconds frameInterval: 15, - //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) alpha: 1.0, - //use gradient instead of solid particle color gradient: false, }; diff --git a/src/components/views/elements/effects/effectUtilities.ts b/src/components/views/elements/effects/effectUtilities.ts index 927b445a61..212c477b39 100644 --- a/src/components/views/elements/effects/effectUtilities.ts +++ b/src/components/views/elements/effects/effectUtilities.ts @@ -1,3 +1,8 @@ +/** + * Checks a message if it contains one of the provided emojis + * @param {Object} content The message + * @param {Array} emojis The list of emojis to check for + */ export const containsEmoji = (content: { msgtype: string, body: string }, emojis: Array): boolean => { return emojis.some((emoji) => content.body.includes(emoji)); } diff --git a/src/components/views/elements/effects/index.ts b/src/components/views/elements/effects/index.ts index 3986d6e841..0f01f2624e 100644 --- a/src/components/views/elements/effects/index.ts +++ b/src/components/views/elements/effects/index.ts @@ -1,25 +1,59 @@ import { _t, _td } from "../../../../languageHandler"; -export type Effect = { +export type Effect = { + /** + * one or more emojis that will trigger this effect + */ emojis: Array; + /** + * the matrix message type that will trigger this effect + */ msgType: string; + /** + * the room command to trigger this effect + */ command: string; + /** + * a function that returns the translated description of the effect + */ description: () => string; + /** + * a function that returns the translated fallback message. this message will be shown if the user did not provide a custom message + */ fallbackMessage: () => string; - options: { - [key: string]: any - } + /** + * animation options + */ + options: TOptions; } type ConfettiOptions = { + /** + * max confetti count + */ maxCount: number, + /** + * particle animation speed + */ speed: number, + /** + * the confetti animation frame interval in milliseconds + */ frameInterval: number, + /** + * the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) + */ alpha: number, + /** + * use gradient instead of solid particle color + */ gradient: boolean, } -const effects: Array = [ +/** + * This configuration defines room effects that can be triggered by custom message types and emojis + */ +const effects: Array> = [ { emojis: ['🎊', '🎉'], msgType: 'nic.custom.confetti', @@ -27,18 +61,13 @@ const effects: Array = [ description: () => _td("Sends the given message with confetti"), fallbackMessage: () => _t("sends confetti") + " 🎉", options: { - //set max confetti count maxCount: 150, - //syarn addet the particle animation speed speed: 3, - //the confetti animation frame interval in milliseconds frameInterval: 15, - //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible) alpha: 1.0, - //use gradient instead of solid particle color gradient: false, - } as ConfettiOptions, - }, + }, + } as Effect, ]; export default effects; From 46eb5cdb1b036f2e696cd97fa9540c8ff18285be Mon Sep 17 00:00:00 2001 From: MaHa-Nordeck Date: Thu, 22 Oct 2020 14:01:16 +0200 Subject: [PATCH 43/66] Minor improvements * Made color generation dependant on gradient usage. * Changed a loop to use for of --- .../views/elements/effects/confetti/index.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 29f70d1a57..309fc9dd9c 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -107,11 +107,15 @@ export default class Confetti implements ICanvasEffect { private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => { particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); - particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); + if(this.options.gradient) { + particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); + } else { + particle.color2 = particle.color; + } particle.x = Math.random() * width; - particle.y = Math.random() * height - height; + particle.y = Math.random() * -height; particle.diameter = Math.random() * 10 + 5; - particle.tilt = Math.random() * 10 - 10; + particle.tilt = Math.random() * -10; particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05; particle.tiltAngle = Math.random() * Math.PI; return particle; @@ -141,10 +145,8 @@ export default class Confetti implements ICanvasEffect { if (!this.context || !this.context.canvas) { return; } - let particle; let x; let x2; let y2; - for (let i = 0; i < this.particles.length; i++) { - particle = this.particles[i]; + for (const particle of this.particles) { this.context.beginPath(); context.lineWidth = particle.diameter; x2 = particle.x + particle.tilt; From 674060ed9373b1a2ca210449e4a4110415ce21ba Mon Sep 17 00:00:00 2001 From: MaHa-Nordeck Date: Thu, 22 Oct 2020 15:28:30 +0200 Subject: [PATCH 44/66] fixed spacing --- src/components/views/elements/effects/confetti/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/components/views/elements/effects/confetti/index.ts index 309fc9dd9c..aee8f54a3a 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/components/views/elements/effects/confetti/index.ts @@ -107,7 +107,7 @@ export default class Confetti implements ICanvasEffect { private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => { particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); - if(this.options.gradient) { + if (this.options.gradient) { particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')'); } else { particle.color2 = particle.color; From 19395f3c3cf6076b097553da7873a045721d0ff9 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Mon, 26 Oct 2020 16:37:45 +0100 Subject: [PATCH 45/66] null checks added --- src/components/structures/RoomView.tsx | 16 ++++++++-------- .../views/elements/effects/effectUtilities.ts | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 1a18ece008..57c9afb17b 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -831,14 +831,14 @@ export default class RoomView extends React.Component { }; private handleEffects = (ev) => { - if (this.state.room.getUnreadNotificationCount() === 0) return; - if (this.state.matrixClientIsReady) { - effects.forEach(effect => { - if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) { - dis.dispatch({action: `effects.${effect.command}`}); - } - }) - } + if (!this.state.room || + !this.state.matrixClientIsReady || + this.state.room.getUnreadNotificationCount() === 0) return; + effects.forEach(effect => { + if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) { + dis.dispatch({action: `effects.${effect.command}`}); + } + }) }; private onRoomName = (room: Room) => { diff --git a/src/components/views/elements/effects/effectUtilities.ts b/src/components/views/elements/effects/effectUtilities.ts index 212c477b39..e94287c745 100644 --- a/src/components/views/elements/effects/effectUtilities.ts +++ b/src/components/views/elements/effects/effectUtilities.ts @@ -4,5 +4,5 @@ * @param {Array} emojis The list of emojis to check for */ export const containsEmoji = (content: { msgtype: string, body: string }, emojis: Array): boolean => { - return emojis.some((emoji) => content.body.includes(emoji)); + return emojis.some((emoji) => content.body && content.body.includes(emoji)); } From 20f3ab029320adb87d9384b4bf8504be59ad1b2f Mon Sep 17 00:00:00 2001 From: su-ex Date: Tue, 3 Nov 2020 20:41:59 +0100 Subject: [PATCH 46/66] Fix inverted settings default value Currently it doesn't matter what's set in the default property once the invertedSettingName property exists --- src/settings/SettingsStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index 7c05e4b500..82f169f498 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -42,7 +42,7 @@ for (const key of Object.keys(SETTINGS)) { if (SETTINGS[key].invertedSettingName) { // Invert now so that the rest of the system will invert it back // to what was intended. - invertedDefaultSettings[key] = !SETTINGS[key].default; + invertedDefaultSettings[SETTINGS[key].invertedSettingName] = !SETTINGS[key].default; } } From ede67684e4d436be9602c3e570079dfa036617af Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Thu, 26 Nov 2020 18:27:35 +0100 Subject: [PATCH 47/66] Removed trailing space --- src/SlashCommands.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index c7d5b1b08c..45c7251c3b 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -1096,7 +1096,7 @@ export const Commands = [ category: CommandCategories.messages, hideCompletionAfterSpace: true, }), - + ...effects.map((effect) => { return new Command({ command: effect.command, From 6ce5d3b044ce5f0cfd448c8f058553a12505913a Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Fri, 27 Nov 2020 14:54:21 +0100 Subject: [PATCH 48/66] refactored effects dir and changed effects exported name --- src/SlashCommands.tsx | 4 +- src/components/structures/RoomView.tsx | 10 ++--- .../elements/{effects => }/EffectsOverlay.tsx | 25 ++++++++++-- .../views/elements/effects/effectUtilities.ts | 8 ---- .../views/rooms/SendMessageComposer.js | 6 +-- .../elements => }/effects/ICanvasEffect.ts | 17 ++++++++ .../elements => }/effects/confetti/index.ts | 40 +++++++++---------- src/effects/effectUtilities.ts | 25 ++++++++++++ .../views/elements => }/effects/index.ts | 23 +++++++++-- 9 files changed, 110 insertions(+), 48 deletions(-) rename src/components/views/elements/{effects => }/EffectsOverlay.tsx (74%) delete mode 100644 src/components/views/elements/effects/effectUtilities.ts rename src/{components/views/elements => }/effects/ICanvasEffect.ts (60%) rename src/{components/views/elements => }/effects/confetti/index.ts (88%) create mode 100644 src/effects/effectUtilities.ts rename src/{components/views/elements => }/effects/index.ts (70%) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 45c7251c3b..e2ae875ac3 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -46,7 +46,7 @@ import { EffectiveMembership, getEffectiveMembership, leaveRoomBehaviour } from import SdkConfig from "./SdkConfig"; import SettingsStore from "./settings/SettingsStore"; import {UIFeature} from "./settings/UIFeature"; -import effects from "./components/views/elements/effects" +import {CHAT_EFFECTS} from "./effects" import CallHandler from "./CallHandler"; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 @@ -1097,7 +1097,7 @@ export const Commands = [ hideCompletionAfterSpace: true, }), - ...effects.map((effect) => { + ...CHAT_EFFECTS.map((effect) => { return new Command({ command: effect.command, description: effect.description(), diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 59f8db5837..8189420a52 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -69,9 +69,9 @@ import AuxPanel from "../views/rooms/AuxPanel"; import RoomHeader from "../views/rooms/RoomHeader"; import {XOR} from "../../@types/common"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; -import EffectsOverlay from "../views/elements/effects/EffectsOverlay"; -import {containsEmoji} from '../views/elements/effects/effectUtilities'; -import effects from '../views/elements/effects' +import EffectsOverlay from "../views/elements/EffectsOverlay"; +import {containsEmoji} from '../../effects/effectUtilities'; +import {CHAT_EFFECTS} from '../../effects' import { CallState, MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import WidgetStore from "../../stores/WidgetStore"; import {UPDATE_EVENT} from "../../stores/AsyncStore"; @@ -802,9 +802,9 @@ export default class RoomView extends React.Component { if (!this.state.room || !this.state.matrixClientIsReady || this.state.room.getUnreadNotificationCount() === 0) return; - effects.forEach(effect => { + CHAT_EFFECTS.forEach(effect => { if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) { - dis.dispatch({action: `effects.${effect.command}`}); + dis.dispatch({action: `CHAT_EFFECTS.${effect.command}`}); } }) }; diff --git a/src/components/views/elements/effects/EffectsOverlay.tsx b/src/components/views/elements/EffectsOverlay.tsx similarity index 74% rename from src/components/views/elements/effects/EffectsOverlay.tsx rename to src/components/views/elements/EffectsOverlay.tsx index b2ecec8753..4c6a3c06ae 100644 --- a/src/components/views/elements/effects/EffectsOverlay.tsx +++ b/src/components/views/elements/EffectsOverlay.tsx @@ -1,7 +1,24 @@ +/* + Copyright 2020 Nurjin Jafar + Copyright 2020 Nordeck IT + Consulting GmbH. + + + 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, { FunctionComponent, useEffect, useRef } from 'react'; -import dis from '../../../../dispatcher/dispatcher'; -import ICanvasEffect, { ICanvasEffectConstructable } from './ICanvasEffect.js'; -import effects from './index' +import dis from '../../../dispatcher/dispatcher'; +import ICanvasEffect, { ICanvasEffectConstructable } from '../../../effects/ICanvasEffect.js'; +import {CHAT_EFFECTS} from '../../../effects' export type EffectsOverlayProps = { roomWidth: number; @@ -15,7 +32,7 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = if (!name) return null; let effect: ICanvasEffect | null = effectsRef.current[name] || null; if (effect === null) { - const options = effects.find((e) => e.command === name)?.options + const options = CHAT_EFFECTS.find((e) => e.command === name)?.options try { const { default: Effect }: { default: ICanvasEffectConstructable } = await import(`./${name}`); effect = new Effect(options); diff --git a/src/components/views/elements/effects/effectUtilities.ts b/src/components/views/elements/effects/effectUtilities.ts deleted file mode 100644 index e94287c745..0000000000 --- a/src/components/views/elements/effects/effectUtilities.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Checks a message if it contains one of the provided emojis - * @param {Object} content The message - * @param {Array} emojis The list of emojis to check for - */ -export const containsEmoji = (content: { msgtype: string, body: string }, emojis: Array): boolean => { - return emojis.some((emoji) => content.body && content.body.includes(emoji)); -} diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 583a3c6368..6a7270c3d6 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -42,8 +42,8 @@ import {Key, isOnlyCtrlOrCmdKeyEvent} from "../../../Keyboard"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import RateLimitedFunc from '../../../ratelimitedfunc'; import {Action} from "../../../dispatcher/actions"; -import {containsEmoji} from "../elements/effects/effectUtilities"; -import effects from '../elements/effects'; +import {containsEmoji} from "../../../effects/effectUtilities"; +import {CHAT_EFFECTS} from '../../../effects'; import SettingsStore from "../../../settings/SettingsStore"; import CountlyAnalytics from "../../../CountlyAnalytics"; @@ -328,7 +328,7 @@ export default class SendMessageComposer extends React.Component { }); } dis.dispatch({action: "message_sent"}); - effects.forEach((effect) => { + CHAT_EFFECTS.forEach((effect) => { if (containsEmoji(content, effect.emojis)) { dis.dispatch({action: `effects.${effect.command}`}); } diff --git a/src/components/views/elements/effects/ICanvasEffect.ts b/src/effects/ICanvasEffect.ts similarity index 60% rename from src/components/views/elements/effects/ICanvasEffect.ts rename to src/effects/ICanvasEffect.ts index 400f42af73..dbbde3dbe7 100644 --- a/src/components/views/elements/effects/ICanvasEffect.ts +++ b/src/effects/ICanvasEffect.ts @@ -1,3 +1,20 @@ +/* + Copyright 2020 Nurjin Jafar + Copyright 2020 Nordeck IT + Consulting GmbH. + + + 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. + */ /** * Defines the constructor of a canvas based room effect */ diff --git a/src/components/views/elements/effects/confetti/index.ts b/src/effects/confetti/index.ts similarity index 88% rename from src/components/views/elements/effects/confetti/index.ts rename to src/effects/confetti/index.ts index aee8f54a3a..646ac30524 100644 --- a/src/components/views/elements/effects/confetti/index.ts +++ b/src/effects/confetti/index.ts @@ -1,12 +1,22 @@ +/* + Copyright 2020 Nurjin Jafar + Copyright 2020 Nordeck IT + Consulting GmbH. + + + 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 ICanvasEffect from '../ICanvasEffect'; -declare global { - interface Window { - mozRequestAnimationFrame: any; - oRequestAnimationFrame: any; - msRequestAnimationFrame: any; - } -} export type ConfettiOptions = { /** @@ -58,11 +68,7 @@ export default class Confetti implements ICanvasEffect { } private context: CanvasRenderingContext2D | null = null; - private supportsAnimationFrame = window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame; + private supportsAnimationFrame = window.requestAnimationFrame; private colors = ['rgba(30,144,255,', 'rgba(107,142,35,', 'rgba(255,215,0,', 'rgba(255,192,203,', 'rgba(106,90,205,', 'rgba(173,216,230,', 'rgba(238,130,238,', 'rgba(152,251,152,', 'rgba(70,130,180,', @@ -78,16 +84,6 @@ export default class Confetti implements ICanvasEffect { if (!canvas) { return; } - window.requestAnimationFrame = (function() { - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, this.options.frameInterval); - }; - })(); this.context = canvas.getContext('2d'); this.particles = []; const count = this.options.maxCount; diff --git a/src/effects/effectUtilities.ts b/src/effects/effectUtilities.ts new file mode 100644 index 0000000000..e708f4864e --- /dev/null +++ b/src/effects/effectUtilities.ts @@ -0,0 +1,25 @@ +/* + Copyright 2020 Nurjin Jafar + Copyright 2020 Nordeck IT + Consulting GmbH. + + + 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. + */ +/** + * Checks a message if it contains one of the provided emojis + * @param {Object} content The message + * @param {Array} emojis The list of emojis to check for + */ +export const containsEmoji = (content: { msgtype: string, body: string }, emojis: Array): boolean => { + return emojis.some((emoji) => content.body && content.body.includes(emoji)); +} diff --git a/src/components/views/elements/effects/index.ts b/src/effects/index.ts similarity index 70% rename from src/components/views/elements/effects/index.ts rename to src/effects/index.ts index 0f01f2624e..067bd6848c 100644 --- a/src/components/views/elements/effects/index.ts +++ b/src/effects/index.ts @@ -1,4 +1,21 @@ -import { _t, _td } from "../../../../languageHandler"; +/* + Copyright 2020 Nurjin Jafar + Copyright 2020 Nordeck IT + Consulting GmbH. + + + 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 { _t, _td } from "../languageHandler"; export type Effect = { /** @@ -53,7 +70,7 @@ type ConfettiOptions = { /** * This configuration defines room effects that can be triggered by custom message types and emojis */ -const effects: Array> = [ +export const CHAT_EFFECTS: Array> = [ { emojis: ['🎊', '🎉'], msgType: 'nic.custom.confetti', @@ -70,6 +87,4 @@ const effects: Array> = [ } as Effect, ]; -export default effects; - From 2f72c9ee55c9f037800a4446ed034e54faa099db Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Fri, 27 Nov 2020 16:47:07 +0100 Subject: [PATCH 49/66] update translation file --- src/i18n/strings/en_EN.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bd8895d4c0..c432eb4b27 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -846,6 +846,8 @@ "When rooms are upgraded": "When rooms are upgraded", "My Ban List": "My Ban List", "This is your list of users/servers you have blocked - don't leave the room!": "This is your list of users/servers you have blocked - don't leave the room!", + "Sends the given message with confetti": "Sends the given message with confetti", + "sends confetti": "sends confetti", "Video Call": "Video Call", "Voice Call": "Voice Call", "Fill Screen": "Fill Screen", @@ -1900,8 +1902,6 @@ "Sign in with single sign-on": "Sign in with single sign-on", "And %(count)s more...|other": "And %(count)s more...", "Home": "Home", - "Sends the given message with confetti": "Sends the given message with confetti", - "sends confetti": "sends confetti", "Enter a server name": "Enter a server name", "Looks good": "Looks good", "Can't find this server or its room list": "Can't find this server or its room list", From 0e53e220d0bbb35b8ba489e4f44b25a11046ff86 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Fri, 27 Nov 2020 17:25:34 +0100 Subject: [PATCH 50/66] Fixed copy paste error --- src/components/structures/RoomView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 8189420a52..618a397697 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -804,7 +804,7 @@ export default class RoomView extends React.Component { this.state.room.getUnreadNotificationCount() === 0) return; CHAT_EFFECTS.forEach(effect => { if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) { - dis.dispatch({action: `CHAT_EFFECTS.${effect.command}`}); + dis.dispatch({action: `effects.${effect.command}`}); } }) }; From 1bf7ff8994356daa8a94c7ec94de064aabbf932c Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Tue, 1 Dec 2020 17:15:02 +0100 Subject: [PATCH 51/66] null check added path to confetti fixed after refactoring --- src/components/views/elements/EffectsOverlay.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/EffectsOverlay.tsx b/src/components/views/elements/EffectsOverlay.tsx index 4c6a3c06ae..0bffaf526f 100644 --- a/src/components/views/elements/EffectsOverlay.tsx +++ b/src/components/views/elements/EffectsOverlay.tsx @@ -34,11 +34,11 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = if (effect === null) { const options = CHAT_EFFECTS.find((e) => e.command === name)?.options try { - const { default: Effect }: { default: ICanvasEffectConstructable } = await import(`./${name}`); + const { default: Effect }: { default: ICanvasEffectConstructable } = await import(`../../../effects/${name}`); effect = new Effect(options); effectsRef.current[name] = effect; } catch (err) { - console.warn('Unable to load effect module at \'./${name}\'.', err) + console.warn('Unable to load effect module at \'../../../effects/${name}\'.', err) } } return effect; @@ -46,7 +46,9 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = useEffect(() => { const resize = () => { - canvasRef.current.height = window.innerHeight; + if (canvasRef.current) { + canvasRef.current.height = window.innerHeight; + } }; const onAction = (payload: { action: string }) => { const actionPrefix = 'effects.'; From 111515e794ad5e8fcc389ca9137ed9e79abd27d3 Mon Sep 17 00:00:00 2001 From: nurjinn jafar Date: Tue, 1 Dec 2020 17:37:28 +0100 Subject: [PATCH 52/66] fixed linter problem --- src/components/views/elements/EffectsOverlay.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/EffectsOverlay.tsx b/src/components/views/elements/EffectsOverlay.tsx index 0bffaf526f..684b647365 100644 --- a/src/components/views/elements/EffectsOverlay.tsx +++ b/src/components/views/elements/EffectsOverlay.tsx @@ -34,7 +34,8 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = if (effect === null) { const options = CHAT_EFFECTS.find((e) => e.command === name)?.options try { - const { default: Effect }: { default: ICanvasEffectConstructable } = await import(`../../../effects/${name}`); + const { default: Effect }: { default: ICanvasEffectConstructable } + = await import(`../../../effects/${name}`); effect = new Effect(options); effectsRef.current[name] = effect; } catch (err) { From ed59b504480f943c3ddc2dbc55c81ab980e714cd Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 2 Dec 2020 14:25:55 +0000 Subject: [PATCH 53/66] Upgrade matrix-js-sdk to 9.3.0-rc.1 --- package.json | 4 ++-- yarn.lock | 27 ++++++++++++++------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index f3b8104663..76723bb12d 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "blueimp-canvas-to-blob": "^3.27.0", "browser-encrypt-attachment": "^0.3.0", "browser-request": "^0.3.3", + "cheerio": "^1.0.0-rc.3", "classnames": "^2.2.6", "commonmark": "^0.29.1", "counterpart": "^0.18.6", @@ -77,10 +78,9 @@ "html-entities": "^1.3.1", "is-ip": "^2.0.0", "katex": "^0.12.0", - "cheerio": "^1.0.0-rc.3", "linkifyjs": "^2.1.9", "lodash": "^4.17.19", - "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", + "matrix-js-sdk": "9.3.0-rc.1", "matrix-widget-api": "^0.1.0-beta.10", "minimist": "^1.2.5", "pako": "^1.0.11", diff --git a/yarn.lock b/yarn.lock index c06494d319..2a49110d58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1256,10 +1256,10 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.11.2": - version "7.11.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" - integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== +"@babel/runtime@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" + integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== dependencies: regenerator-runtime "^0.13.4" @@ -6390,10 +6390,10 @@ log-symbols@^2.0.0, log-symbols@^2.2.0: dependencies: chalk "^2.0.1" -loglevel@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" - integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== +loglevel@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" + integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== lolex@^5.0.0, lolex@^5.1.2: version "5.1.2" @@ -6512,16 +6512,17 @@ mathml-tag-names@^2.0.1: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": - version "9.2.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6661bde6088e6e43f31198e8532432e162aef33c" +matrix-js-sdk@9.3.0-rc.1: + version "9.3.0-rc.1" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-9.3.0-rc.1.tgz#d6ad2d9a5e0c539c6aec9e587a6dd2b5aa8bf2f6" + integrity sha512-H20QLwsgzBIO0Lp75CYBlw4QTOHT98vCESNZrnjIsu8FlFqsXIhdTa5C8BIYsNLex5luufxdp2an5BQtJEuAUQ== dependencies: - "@babel/runtime" "^7.11.2" + "@babel/runtime" "^7.12.5" another-json "^0.2.0" browser-request "^0.3.3" bs58 "^4.0.1" content-type "^1.0.4" - loglevel "^1.7.0" + loglevel "^1.7.1" qs "^6.9.4" request "^2.88.2" unhomoglyph "^1.0.6" From 054dff31d5ec472551e4ea7ed88e6a4e40fb522d Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 2 Dec 2020 14:38:28 +0000 Subject: [PATCH 54/66] Prepare changelog for v3.10.0-rc.1 --- CHANGELOG.md | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5aac4e2974..6fa0612695 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,80 @@ +Changes in [3.10.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.10.0-rc.1) (2020-12-02) +=============================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.9.0...v3.10.0-rc.1) + + * Upgrade to JS SDK 9.3.0-rc.1 + * Translations update from Weblate + [\#5461](https://github.com/matrix-org/matrix-react-sdk/pull/5461) + * Fix VoIP call plinth on dark theme + [\#5460](https://github.com/matrix-org/matrix-react-sdk/pull/5460) + * Add sanity checking around widget pinning + [\#5459](https://github.com/matrix-org/matrix-react-sdk/pull/5459) + * Update i18n for Appearance User Settings + [\#5457](https://github.com/matrix-org/matrix-react-sdk/pull/5457) + * Only show 'answered elsewhere' if we tried to answer too + [\#5455](https://github.com/matrix-org/matrix-react-sdk/pull/5455) + * Fixed Avatar for 3PID invites + [\#5442](https://github.com/matrix-org/matrix-react-sdk/pull/5442) + * Slightly better error if we can't capture user media + [\#5449](https://github.com/matrix-org/matrix-react-sdk/pull/5449) + * Make it possible in-code to hide rooms from the room list + [\#5445](https://github.com/matrix-org/matrix-react-sdk/pull/5445) + * Fix the stickerpicker + [\#5447](https://github.com/matrix-org/matrix-react-sdk/pull/5447) + * Add live password validation to change password dialog + [\#5436](https://github.com/matrix-org/matrix-react-sdk/pull/5436) + * LaTeX rendering in element-web using KaTeX + [\#5244](https://github.com/matrix-org/matrix-react-sdk/pull/5244) + * Add lifecycle customisation point after logout + [\#5448](https://github.com/matrix-org/matrix-react-sdk/pull/5448) + * Simplify UserMenu for Guests as they can't use most of the options + [\#5421](https://github.com/matrix-org/matrix-react-sdk/pull/5421) + * Fix known issues with modal widgets + [\#5444](https://github.com/matrix-org/matrix-react-sdk/pull/5444) + * Fix existing widgets not having approved capabilities for their function + [\#5443](https://github.com/matrix-org/matrix-react-sdk/pull/5443) + * Use the WidgetDriver to run OIDC requests + [\#5440](https://github.com/matrix-org/matrix-react-sdk/pull/5440) + * Add a customisation point for widget permissions and fix amnesia issues + [\#5439](https://github.com/matrix-org/matrix-react-sdk/pull/5439) + * Fix Widget event notification text including spurious space + [\#5441](https://github.com/matrix-org/matrix-react-sdk/pull/5441) + * Move call listener out of MatrixChat + [\#5438](https://github.com/matrix-org/matrix-react-sdk/pull/5438) + * New Look in-Call View + [\#5432](https://github.com/matrix-org/matrix-react-sdk/pull/5432) + * Support arbitrary widgets sticking to the screen + sending stickers + [\#5435](https://github.com/matrix-org/matrix-react-sdk/pull/5435) + * Auth typescripting and validation tweaks + [\#5433](https://github.com/matrix-org/matrix-react-sdk/pull/5433) + * Add new widget API actions for changing rooms and sending/receiving events + [\#5385](https://github.com/matrix-org/matrix-react-sdk/pull/5385) + * Revert room header click behaviour to opening room settings + [\#5434](https://github.com/matrix-org/matrix-react-sdk/pull/5434) + * Add option to send/edit a message with Ctrl + Enter / Command + Enter + [\#5160](https://github.com/matrix-org/matrix-react-sdk/pull/5160) + * Add Analytics instrumentation to the Homepage + [\#5409](https://github.com/matrix-org/matrix-react-sdk/pull/5409) + * Fix encrypted video playback in Chrome-based browsers + [\#5430](https://github.com/matrix-org/matrix-react-sdk/pull/5430) + * Add border-radius for video + [\#5333](https://github.com/matrix-org/matrix-react-sdk/pull/5333) + * Push name to the end, near text, in IRC layout + [\#5166](https://github.com/matrix-org/matrix-react-sdk/pull/5166) + * Disable notifications for the room you have recently been active in + [\#5325](https://github.com/matrix-org/matrix-react-sdk/pull/5325) + * Search through the list of unfiltered rooms rather than the rooms in the + state which are already filtered by the search text + [\#5331](https://github.com/matrix-org/matrix-react-sdk/pull/5331) + * Lighten blockquote colour in dark mode + [\#5353](https://github.com/matrix-org/matrix-react-sdk/pull/5353) + * Specify community description img must be mxc urls + [\#5364](https://github.com/matrix-org/matrix-react-sdk/pull/5364) + * Add keyboard shortcut to close the current conversation + [\#5253](https://github.com/matrix-org/matrix-react-sdk/pull/5253) + * Redirect user home from auth screens if they are already logged in + [\#5423](https://github.com/matrix-org/matrix-react-sdk/pull/5423) + Changes in [3.9.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.9.0) (2020-11-23) =================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.9.0-rc.1...v3.9.0) From db354ff888a44f4560d05580e83edd7a00bb307d Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 2 Dec 2020 14:38:28 +0000 Subject: [PATCH 55/66] v3.10.0-rc.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 76723bb12d..7eff8cf388 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.9.0", + "version": "3.10.0-rc.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From c63c8540f1dda6ce1a4ffed0ba9725af2359eea5 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 4 Dec 2020 11:05:01 +0000 Subject: [PATCH 56/66] Remove old app test script path Now that https://github.com/matrix-org/pipelines/pull/112 has merged, we no longer need to support this old path for launching app-level tests. --- scripts/ci/riot-unit-tests.sh | 1 - 1 file changed, 1 deletion(-) delete mode 120000 scripts/ci/riot-unit-tests.sh diff --git a/scripts/ci/riot-unit-tests.sh b/scripts/ci/riot-unit-tests.sh deleted file mode 120000 index 199dfb58fd..0000000000 --- a/scripts/ci/riot-unit-tests.sh +++ /dev/null @@ -1 +0,0 @@ -app-tests.sh \ No newline at end of file From 58eaccbba823a449ec2eb5cb7736155b0ab917b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Dec 2020 17:35:40 +0000 Subject: [PATCH 57/66] Bump highlight.js from 10.1.2 to 10.4.1 Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 10.1.2 to 10.4.1. - [Release notes](https://github.com/highlightjs/highlight.js/releases) - [Changelog](https://github.com/highlightjs/highlight.js/blob/master/CHANGES.md) - [Commits](https://github.com/highlightjs/highlight.js/compare/10.1.2...10.4.1) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c06494d319..e33e784c93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4842,9 +4842,9 @@ has@^1.0.1, has@^1.0.3: function-bind "^1.1.1" highlight.js@^10.1.2: - version "10.1.2" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.1.2.tgz#c20db951ba1c22c055010648dfffd7b2a968e00c" - integrity sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA== + version "10.4.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.4.1.tgz#d48fbcf4a9971c4361b3f95f302747afe19dbad0" + integrity sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg== hoist-non-react-statics@^3.3.0: version "3.3.2" From a85648413932281b830a71d3382cc9cb89732cfe Mon Sep 17 00:00:00 2001 From: Lukas Date: Sun, 6 Dec 2020 10:32:52 +0100 Subject: [PATCH 58/66] Fix typos in some strings This commit fixes some typos that I've stumbled upon. --- src/CallHandler.tsx | 4 ++-- src/components/views/dialogs/ServerPickerDialog.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index b5f696008d..2c30c51041 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -393,14 +393,14 @@ export default class CallHandler { title = _t("Unable to access microphone"); description =
{_t( - "Call failed because no microphone could not be accessed. " + + "Call failed because microphone could not be accessed. " + "Check that a microphone is plugged in and set up correctly.", )}
; } else if (call.type === CallType.Video) { title = _t("Unable to access webcam / microphone"); description =
- {_t("Call failed because no webcam or microphone could not be accessed. Check that:")} + {_t("Call failed because webcam or microphone could not be accessed. Check that:")}
  • {_t("A microphone and webcam are plugged in and set up correctly")}
  • {_t("Permission is granted to use the webcam")}
  • diff --git a/src/components/views/dialogs/ServerPickerDialog.tsx b/src/components/views/dialogs/ServerPickerDialog.tsx index 9eb819c98e..f528872587 100644 --- a/src/components/views/dialogs/ServerPickerDialog.tsx +++ b/src/components/views/dialogs/ServerPickerDialog.tsx @@ -153,7 +153,7 @@ export default class ServerPickerDialog extends React.PureComponent

    - {_t("We call the places you where you can host your account ‘homeservers’.")} {text} + {_t("We call the places where you can host your account ‘homeservers’.")} {text}

    Date: Mon, 7 Dec 2020 12:13:04 +0000 Subject: [PATCH 59/66] Upgrade matrix-js-sdk to 9.3.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7eff8cf388..084fdfa289 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "katex": "^0.12.0", "linkifyjs": "^2.1.9", "lodash": "^4.17.19", - "matrix-js-sdk": "9.3.0-rc.1", + "matrix-js-sdk": "9.3.0", "matrix-widget-api": "^0.1.0-beta.10", "minimist": "^1.2.5", "pako": "^1.0.11", diff --git a/yarn.lock b/yarn.lock index 2a49110d58..7cc852cdf7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6512,10 +6512,10 @@ mathml-tag-names@^2.0.1: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -matrix-js-sdk@9.3.0-rc.1: - version "9.3.0-rc.1" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-9.3.0-rc.1.tgz#d6ad2d9a5e0c539c6aec9e587a6dd2b5aa8bf2f6" - integrity sha512-H20QLwsgzBIO0Lp75CYBlw4QTOHT98vCESNZrnjIsu8FlFqsXIhdTa5C8BIYsNLex5luufxdp2an5BQtJEuAUQ== +matrix-js-sdk@9.3.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-9.3.0.tgz#e5fa3f6cb5a56e5c5386ecf3110dc35072170dbb" + integrity sha512-rzvYJS5mMP42iQVfGokX8DgmJpTUH+k15vATyB5JyBq/3r/kP22tN78RgoNxYzrIP/R4rB4OHUFNtgGzBH2u8g== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From ae835fb8a9585326f718d5494f288cb0f597256f Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 7 Dec 2020 12:31:07 +0000 Subject: [PATCH 60/66] Prepare changelog for v3.10.0 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fa0612695..151888a17e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Changes in [3.10.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.10.0) (2020-12-07) +===================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.10.0-rc.1...v3.10.0) + + * Upgrade to JS SDK 9.3.0 + Changes in [3.10.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.10.0-rc.1) (2020-12-02) =============================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.9.0...v3.10.0-rc.1) From fefe84d1015a8a6713165f627024590f6e34163f Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 7 Dec 2020 12:31:08 +0000 Subject: [PATCH 61/66] v3.10.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 084fdfa289..a318618ae8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.10.0-rc.1", + "version": "3.10.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 7d416045694ff85813ffc5d032edf7fcf6a64b13 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 7 Dec 2020 12:33:18 +0000 Subject: [PATCH 62/66] Reset matrix-js-sdk back to develop branch --- package.json | 2 +- yarn.lock | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 8621dd2535..d4a2c568d5 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "katex": "^0.12.0", "linkifyjs": "^2.1.9", "lodash": "^4.17.19", - "matrix-js-sdk": "9.3.0", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-widget-api": "^0.1.0-beta.10", "minimist": "^1.2.5", "pako": "^1.0.11", diff --git a/yarn.lock b/yarn.lock index 7cc852cdf7..73ba9563d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6512,10 +6512,9 @@ mathml-tag-names@^2.0.1: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -matrix-js-sdk@9.3.0: +"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "9.3.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-9.3.0.tgz#e5fa3f6cb5a56e5c5386ecf3110dc35072170dbb" - integrity sha512-rzvYJS5mMP42iQVfGokX8DgmJpTUH+k15vATyB5JyBq/3r/kP22tN78RgoNxYzrIP/R4rB4OHUFNtgGzBH2u8g== + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/ff6612f9d0aa1a7c08b65a0b41c5ab997506016f" dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From 9bb04a857b6c963c2d55af44cd0183dd165d78a2 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 7 Dec 2020 15:21:31 +0100 Subject: [PATCH 63/66] Add generated i18n json --- src/i18n/strings/en_EN.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a42bd5708f..8ddda3eff6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -47,9 +47,9 @@ "Try using turn.matrix.org": "Try using turn.matrix.org", "OK": "OK", "Unable to access microphone": "Unable to access microphone", - "Call failed because no microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "Call failed because no microphone could not be accessed. Check that a microphone is plugged in and set up correctly.", + "Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.", "Unable to access webcam / microphone": "Unable to access webcam / microphone", - "Call failed because no webcam or microphone could not be accessed. Check that:": "Call failed because no webcam or microphone could not be accessed. Check that:", + "Call failed because webcam or microphone could not be accessed. Check that:": "Call failed because webcam or microphone could not be accessed. Check that:", "A microphone and webcam are plugged in and set up correctly": "A microphone and webcam are plugged in and set up correctly", "Permission is granted to use the webcam": "Permission is granted to use the webcam", "No other application is using the webcam": "No other application is using the webcam", @@ -2160,7 +2160,7 @@ "Specify a homeserver": "Specify a homeserver", "Matrix.org is the biggest public homeserver in the world, so it’s a good place for many.": "Matrix.org is the biggest public homeserver in the world, so it’s a good place for many.", "Sign into your homeserver": "Sign into your homeserver", - "We call the places you where you can host your account ‘homeservers’.": "We call the places you where you can host your account ‘homeservers’.", + "We call the places where you can host your account ‘homeservers’.": "We call the places where you can host your account ‘homeservers’.", "Other homeserver": "Other homeserver", "Use your preferred Matrix homeserver if you have one, or host your own.": "Use your preferred Matrix homeserver if you have one, or host your own.", "Learn more": "Learn more", From d06a00bcce4e5b045e6fd905e01661f14c3e21be Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 7 Dec 2020 16:05:41 +0000 Subject: [PATCH 64/66] Improve usability of the Server Picker Dialog --- .../views/dialogs/ServerPickerDialog.tsx | 48 ++++++++++++++----- src/i18n/strings/en_EN.json | 3 +- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/components/views/dialogs/ServerPickerDialog.tsx b/src/components/views/dialogs/ServerPickerDialog.tsx index 9eb819c98e..c96da1b28e 100644 --- a/src/components/views/dialogs/ServerPickerDialog.tsx +++ b/src/components/views/dialogs/ServerPickerDialog.tsx @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, {createRef} from 'react'; +import React, {createRef} from "react"; +import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery"; import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; import BaseDialog from './BaseDialog'; @@ -47,9 +48,10 @@ export default class ServerPickerDialog extends React.PureComponent({ - deriveData: async ({ value: hsUrl }) => { - // Always try and use the defaults first - const defaultConfig: ValidatedServerConfig = SdkConfig.get()["validated_server_config"]; - if (defaultConfig.hsUrl === hsUrl) return {}; + deriveData: async ({ value }) => { + let hsUrl = value.trim(); // trim to account for random whitespace + + // if the URL has no protocol, try validate it as a serverName via well-known + if (!hsUrl.includes("://")) { + try { + const discoveryResult = await AutoDiscovery.findClientConfig(hsUrl); + this.validatedConf = AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(hsUrl, discoveryResult); + return {}; // we have a validated config, we don't need to try the other paths + } catch (e) { + console.error(`Attempted ${hsUrl} as a server_name but it failed`, e); + } + } + + // if we got to this stage then either the well-known failed or the URL had a protocol specified, + // so validate statically only. If the URL has no protocol, default to https. + if (!hsUrl.includes("://")) { + hsUrl = "https://" + hsUrl; + } try { this.validatedConf = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl); @@ -81,17 +98,22 @@ export default class ServerPickerDialog extends React.PureComponent Date: Mon, 7 Dec 2020 15:12:26 -0700 Subject: [PATCH 65/66] Undocumented linter cleanup --- src/components/structures/RoomView.tsx | 4 ++-- .../views/elements/EffectsOverlay.tsx | 12 +++++------- .../views/rooms/SendMessageComposer.js | 2 +- src/effects/ICanvasEffect.ts | 17 +++++++++-------- src/effects/confetti/index.ts | 2 -- src/effects/index.ts | 1 - src/effects/{effectUtilities.ts => utils.ts} | 1 - 7 files changed, 17 insertions(+), 22 deletions(-) rename src/effects/{effectUtilities.ts => utils.ts} (99%) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 0dec6d30df..d2d473fd3d 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -70,8 +70,8 @@ import RoomHeader from "../views/rooms/RoomHeader"; import {XOR} from "../../@types/common"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import EffectsOverlay from "../views/elements/EffectsOverlay"; -import {containsEmoji} from '../../effects/effectUtilities'; -import {CHAT_EFFECTS} from '../../effects' +import {containsEmoji} from '../../effects/utils'; +import {CHAT_EFFECTS} from '../../effects'; import { CallState, MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import WidgetStore from "../../stores/WidgetStore"; import {UPDATE_EVENT} from "../../stores/AsyncStore"; diff --git a/src/components/views/elements/EffectsOverlay.tsx b/src/components/views/elements/EffectsOverlay.tsx index 684b647365..6297d80768 100644 --- a/src/components/views/elements/EffectsOverlay.tsx +++ b/src/components/views/elements/EffectsOverlay.tsx @@ -2,7 +2,6 @@ Copyright 2020 Nurjin Jafar Copyright 2020 Nordeck IT + Consulting GmbH. - 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 @@ -17,14 +16,14 @@ */ import React, { FunctionComponent, useEffect, useRef } from 'react'; import dis from '../../../dispatcher/dispatcher'; -import ICanvasEffect, { ICanvasEffectConstructable } from '../../../effects/ICanvasEffect.js'; +import ICanvasEffect from '../../../effects/ICanvasEffect'; import {CHAT_EFFECTS} from '../../../effects' -export type EffectsOverlayProps = { +interface IProps { roomWidth: number; } -const EffectsOverlay: FunctionComponent = ({ roomWidth }) => { +const EffectsOverlay: FunctionComponent = ({ roomWidth }) => { const canvasRef = useRef(null); const effectsRef = useRef>(new Map()); @@ -34,12 +33,11 @@ const EffectsOverlay: FunctionComponent = ({ roomWidth }) = if (effect === null) { const options = CHAT_EFFECTS.find((e) => e.command === name)?.options try { - const { default: Effect }: { default: ICanvasEffectConstructable } - = await import(`../../../effects/${name}`); + const { default: Effect } = await import(`../../../effects/${name}`); effect = new Effect(options); effectsRef.current[name] = effect; } catch (err) { - console.warn('Unable to load effect module at \'../../../effects/${name}\'.', err) + console.warn('Unable to load effect module at \'../../../effects/${name}\'.', err); } } return effect; diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 6a7270c3d6..8171da7eca 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -42,7 +42,7 @@ import {Key, isOnlyCtrlOrCmdKeyEvent} from "../../../Keyboard"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import RateLimitedFunc from '../../../ratelimitedfunc'; import {Action} from "../../../dispatcher/actions"; -import {containsEmoji} from "../../../effects/effectUtilities"; +import {containsEmoji} from "../../../effects/utils"; import {CHAT_EFFECTS} from '../../../effects'; import SettingsStore from "../../../settings/SettingsStore"; import CountlyAnalytics from "../../../CountlyAnalytics"; diff --git a/src/effects/ICanvasEffect.ts b/src/effects/ICanvasEffect.ts index dbbde3dbe7..9bf3e9293d 100644 --- a/src/effects/ICanvasEffect.ts +++ b/src/effects/ICanvasEffect.ts @@ -2,7 +2,6 @@ Copyright 2020 Nurjin Jafar Copyright 2020 Nordeck IT + Consulting GmbH. - 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 @@ -20,10 +19,10 @@ */ export interface ICanvasEffectConstructable { /** - * @param {{[key:string]:any}} options? Optional animation options + * @param {{[key:string]:any}} options? Optional animation options * @returns ICanvasEffect Returns a new instance of the canvas effect */ - new(options?: { [key: string]: any }): ICanvasEffect + new(options?: { [key: string]: any }): ICanvasEffect; } /** @@ -31,16 +30,18 @@ export interface ICanvasEffectConstructable { */ export default interface ICanvasEffect { /** - * @param {HTMLCanvasElement} canvas The canvas instance as the render target of the animation - * @param {number} timeout? A timeout that defines the runtime of the animation (defaults to false) + * @param {HTMLCanvasElement} canvas The canvas instance as the render target of the animation + * @param {number} timeout? A timeout that defines the runtime of the animation (defaults to false) */ - start: (canvas: HTMLCanvasElement, timeout?: number) => Promise, + start: (canvas: HTMLCanvasElement, timeout?: number) => Promise; + /** * Stops the current animation */ - stop: () => Promise, + stop: () => Promise; + /** * Returns a value that defines if the animation is currently running */ - isRunning: boolean + isRunning: boolean; } diff --git a/src/effects/confetti/index.ts b/src/effects/confetti/index.ts index 646ac30524..8c4b2d2616 100644 --- a/src/effects/confetti/index.ts +++ b/src/effects/confetti/index.ts @@ -2,7 +2,6 @@ Copyright 2020 Nurjin Jafar Copyright 2020 Nordeck IT + Consulting GmbH. - 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 @@ -17,7 +16,6 @@ */ import ICanvasEffect from '../ICanvasEffect'; - export type ConfettiOptions = { /** * max confetti count diff --git a/src/effects/index.ts b/src/effects/index.ts index 067bd6848c..16a0851070 100644 --- a/src/effects/index.ts +++ b/src/effects/index.ts @@ -2,7 +2,6 @@ Copyright 2020 Nurjin Jafar Copyright 2020 Nordeck IT + Consulting GmbH. - 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 diff --git a/src/effects/effectUtilities.ts b/src/effects/utils.ts similarity index 99% rename from src/effects/effectUtilities.ts rename to src/effects/utils.ts index e708f4864e..c2b499b154 100644 --- a/src/effects/effectUtilities.ts +++ b/src/effects/utils.ts @@ -2,7 +2,6 @@ Copyright 2020 Nurjin Jafar Copyright 2020 Nordeck IT + Consulting GmbH. - 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 From 603a1c8ffb90881b163461d4992331d8df9fe3a7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 7 Dec 2020 15:37:26 -0700 Subject: [PATCH 66/66] Fix linter --- src/components/views/elements/EffectsOverlay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/EffectsOverlay.tsx b/src/components/views/elements/EffectsOverlay.tsx index 6297d80768..38be8da9a8 100644 --- a/src/components/views/elements/EffectsOverlay.tsx +++ b/src/components/views/elements/EffectsOverlay.tsx @@ -16,7 +16,7 @@ */ import React, { FunctionComponent, useEffect, useRef } from 'react'; import dis from '../../../dispatcher/dispatcher'; -import ICanvasEffect from '../../../effects/ICanvasEffect'; +import ICanvasEffect from '../../../effects/ICanvasEffect'; import {CHAT_EFFECTS} from '../../../effects' interface IProps {