refactoring roomView / slashCommands / SendMessageComposer with the new effects configurations and fix confetti animation timeout

This commit is contained in:
nurjinn jafar 2020-10-21 13:37:36 +02:00
parent 48633f76ab
commit 1c6d28b861
7 changed files with 59 additions and 52 deletions

View File

@ -46,6 +46,7 @@ import { EffectiveMembership, getEffectiveMembership, leaveRoomBehaviour } from
import SdkConfig from "./SdkConfig"; import SdkConfig from "./SdkConfig";
import SettingsStore from "./settings/SettingsStore"; import SettingsStore from "./settings/SettingsStore";
import {UIFeature} from "./settings/UIFeature"; import {UIFeature} from "./settings/UIFeature";
import effects from "./components/views/elements/effects"
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
interface HTMLInputEvent extends Event { interface HTMLInputEvent extends Event {
@ -1040,22 +1041,28 @@ export const Commands = [
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
}), }),
new Command({ ...effects.map((effect) => {
command: "confetti", return new Command({
description: _td("Sends the given message with confetti"), command: effect.command,
args: '<message>', description: effect.description(),
runFn: function(roomId, args) { args: '<message>',
return success((async () => { runFn: function(roomId, args) {
if (!args) { return success((async () => {
args = "sends confetti"; if (!args) {
MatrixClientPeg.get().sendEmoteMessage(roomId, args); args = effect.fallbackMessage();
} else { MatrixClientPeg.get().sendEmoteMessage(roomId, args);
MatrixClientPeg.get().sendTextMessage(roomId, args); } else {
} const content = {
dis.dispatch({action: 'effects.confetti'}); msgtype: effect.msgType,
})()); body: args,
}, };
category: CommandCategories.effects, MatrixClientPeg.get().sendMessage(roomId, content);
}
dis.dispatch({action: `effects.${effect.command}`});
})());
},
category: CommandCategories.effects,
})
}), }),
// Command definitions for autocompletion ONLY: // Command definitions for autocompletion ONLY:

View File

@ -73,7 +73,8 @@ import {XOR} from "../../@types/common";
import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore";
import { CallState, CallType, MatrixCall } from "matrix-js-sdk/lib/webrtc/call"; import { CallState, CallType, MatrixCall } from "matrix-js-sdk/lib/webrtc/call";
import EffectsOverlay from "../views/elements/effects/EffectsOverlay"; 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; const DEBUG = false;
let debuglog = function(msg: string) {}; let debuglog = function(msg: string) {};
@ -800,10 +801,9 @@ export default class RoomView extends React.Component<IProps, IState> {
} }
} }
}; };
private onEventDecrypted = (ev) => { private onEventDecrypted = (ev) => {
if (ev.isBeingDecrypted() || ev.isDecryptionFailure() || if (ev.isDecryptionFailure()) return;
this.state.room.getUnreadNotificationCount() === 0) return;
this.handleConfetti(ev); this.handleConfetti(ev);
}; };
@ -813,11 +813,13 @@ export default class RoomView extends React.Component<IProps, IState> {
}; };
private handleConfetti = (ev) => { private handleConfetti = (ev) => {
if (this.state.room.getUnreadNotificationCount() === 0) return;
if (this.state.matrixClientIsReady) { if (this.state.matrixClientIsReady) {
const messageBody = 'sends confetti'; effects.map(effect => {
if (isConfettiEmoji(ev.getContent()) || ev.getContent().body === messageBody) { if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) {
dis.dispatch({action: 'effects.confetti'}); dis.dispatch({action: `effects.${effect.command}`});
} }
})
} }
}; };

View File

@ -1,5 +1,5 @@
export default interface ICanvasEffect { export default interface ICanvasEffect {
start: (canvas: HTMLCanvasElement, timeout?: number) => Promise<void>, start: (canvas: HTMLCanvasElement, timeout: number) => Promise<void>,
stop: () => Promise<void>, stop: () => Promise<void>,
isRunning: boolean isRunning: boolean
} }

View File

@ -47,7 +47,7 @@ export default class Confetti implements ICanvasEffect {
this.options = options; this.options = options;
} }
private context: CanvasRenderingContext2D | null; private context: CanvasRenderingContext2D | null = null;
private supportsAnimationFrame = window.requestAnimationFrame || private supportsAnimationFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame || window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame || window.mozRequestAnimationFrame ||
@ -64,7 +64,7 @@ export default class Confetti implements ICanvasEffect {
public isRunning: boolean; public isRunning: boolean;
public start = async (canvas: HTMLCanvasElement, timeout?: number) => { public start = async (canvas: HTMLCanvasElement, timeout = 3000) => {
if(!canvas) { if(!canvas) {
return; return;
} }
@ -88,13 +88,13 @@ export default class Confetti implements ICanvasEffect {
this.isRunning = true; this.isRunning = true;
this.runAnimation(); this.runAnimation();
if (timeout) { if (timeout) {
window.setTimeout(this.stop, timeout || 3000); window.setTimeout(this.stop, timeout);
} }
} }
public stop = async () => { public stop = async () => {
this.isRunning = false; this.isRunning = false;
this.particles = []; // this.particles = [];
} }
private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => { private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => {
@ -177,21 +177,3 @@ export default class Confetti implements ICanvasEffect {
} }
} }
} }
const convertToHex = (data: string): Array<string> => {
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'));
}

View File

@ -0,0 +1,3 @@
export const containsEmoji = (content: { msgtype: string, body: string }, emojis: Array<string>): boolean => {
return emojis.some((emoji) => content.body.includes(emoji));
}

View File

@ -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") + " 🎉",
},
]

View File

@ -42,8 +42,8 @@ import {Key} from "../../../Keyboard";
import MatrixClientContext from "../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext";
import RateLimitedFunc from '../../../ratelimitedfunc'; import RateLimitedFunc from '../../../ratelimitedfunc';
import {Action} from "../../../dispatcher/actions"; import {Action} from "../../../dispatcher/actions";
import {isConfettiEmoji} from "../elements/effects/confetti"; import {containsEmoji} from "../elements/effects/effectUtilities";
import SettingsStore from "../../../settings/SettingsStore"; import effects from '../elements/effects';
function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) { function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) {
const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent); const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent);
@ -318,9 +318,11 @@ export default class SendMessageComposer extends React.Component {
}); });
} }
dis.dispatch({action: "message_sent"}); dis.dispatch({action: "message_sent"});
if (isConfettiEmoji(content)) { effects.map( (effect) => {
dis.dispatch({action: 'effects.confetti'}); if (containsEmoji(content, effect.emojis)) {
dis.dispatch({action: `effects.${effect.command}`});
} }
});
} }
this.sendHistoryManager.save(this.model, replyToEvent); this.sendHistoryManager.save(this.model, replyToEvent);