From 54bdf28020f76fab55bfbd38a85602243feff658 Mon Sep 17 00:00:00 2001 From: Chillmastonmuutos Date: Wed, 2 Mar 2022 10:34:52 +0200 Subject: [PATCH] Add reactions dialog --- res/css/_components.scss | 1 + res/css/views/dialogs/_ReactionsDialog.scss | 67 +++++++++++ .../views/dialogs/ReactionsDialog.tsx | 112 ++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 res/css/views/dialogs/_ReactionsDialog.scss create mode 100644 src/components/views/dialogs/ReactionsDialog.tsx diff --git a/res/css/_components.scss b/res/css/_components.scss index 9c7f6b878e3..c77ef0e0929 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -108,6 +108,7 @@ @import "./views/dialogs/_ModalWidgetDialog.scss"; @import "./views/dialogs/_NewSessionReviewDialog.scss"; @import "./views/dialogs/_PollCreateDialog.scss"; +@import "./views/dialogs/_ReactionsDialog.scss"; @import "./views/dialogs/_RegistrationEmailPromptDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss"; @import "./views/dialogs/_RoomSettingsDialogBridges.scss"; diff --git a/res/css/views/dialogs/_ReactionsDialog.scss b/res/css/views/dialogs/_ReactionsDialog.scss new file mode 100644 index 00000000000..db8cbf68433 --- /dev/null +++ b/res/css/views/dialogs/_ReactionsDialog.scss @@ -0,0 +1,67 @@ +.mx_ReactionsDialog { + width: 520px; + color: $primary-content; + display: flex; + flex-direction: column; + flex-wrap: nowrap; + min-height: 0; + height: 80vh; + + .mx_ReactionsWrapper { + display: flex; + flex-direction: row; + + > .mx_EmojiList { + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + } + + > ul { + list-style-type: none; + } + + .mx_SenderList { + padding: 0; + li { + margin-top: 0.5em; + margin-bottom: 0.5em; + } + } + } + + .mx_EmojiFilterButton { + display: inline-flex; + line-height: $font-20px; + margin-right: 6px; + margin-bottom: 0.5em; + padding: 1px 6px; + border: 1px solid $message-action-bar-border-color; + border-radius: 10px; + background-color: $header-panel-bg-color; + cursor: pointer; + user-select: none; + vertical-align: middle; + + &:hover { + border-color: $reaction-row-button-hover-border-color; + } + + &.mx_ReactionsRowButton_selected { + background-color: $reaction-row-button-selected-bg-color; + border-color: $accent; + } + + &.mx_AccessibleButton_disabled { + cursor: not-allowed; + } + + .mx_ReactionsRowButton_content { + max-width: 100px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + padding-right: 4px; + } + } +} diff --git a/src/components/views/dialogs/ReactionsDialog.tsx b/src/components/views/dialogs/ReactionsDialog.tsx new file mode 100644 index 00000000000..04f5187dc65 --- /dev/null +++ b/src/components/views/dialogs/ReactionsDialog.tsx @@ -0,0 +1,112 @@ +import React from 'react'; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { Relations } from 'matrix-js-sdk/src/models/relations'; + +import { _t } from '../../../languageHandler'; +import { IDialogProps } from "./IDialogProps"; +import { replaceableComponent } from "../../../utils/replaceableComponent"; +import BaseDialog from "./BaseDialog"; +import { MatrixClientPeg } from '../../../MatrixClientPeg'; +import AccessibleButton from '../elements/AccessibleButton'; + +interface IProps extends IDialogProps { + mxEvent: MatrixEvent; + reactions: Relations; +} + +interface IState { + filteredEmoji: string|null; + allAnnotations: any[]; +} + +@replaceableComponent("views.dialogs.ReactionsDialog") +export default class ReactionsDialog extends React.Component { + constructor(props: IProps) { + super(props); + } + + state = { + filteredEmoji: null, + allAnnotations: [], + }; + + componentDidMount() { + this.setState({ allAnnotations: this.getAllAnnotations() }); + } + + private sortAnnotations(arr1, arr2) { + return arr2[1].size - arr1[1].size; + } + + private getAllAnnotations() { + const client = MatrixClientPeg.get(); + const room = client.getRoom(this.props.mxEvent.getRoomId()); + const reactions = this.props.reactions; + const sortedAnnotations = reactions.getSortedAnnotationsByKey() + .sort(this.sortAnnotations); + const senders = []; + for (const reaction of sortedAnnotations) { + const emoji = reaction[0]; + const reactionEvents = reaction[1]; + for (const reactionEvent of reactionEvents) { + const member = room.getMember(reactionEvent.getSender()); + const name = member ? member.name : reactionEvent.getSender(); + senders.push([emoji, name]); + } + } + return senders; + } + + private getFilteredAnnotations() { + const filterEmoji = this.state.filteredEmoji; + return this.state.allAnnotations + .filter(ann => filterEmoji === null || ann[0] == filterEmoji); + } + + private setFilterEmoji(emoji) { + const matchingEmoji = this.state.allAnnotations + .filter(([existingEmoji, _]) => emoji === existingEmoji); + if (matchingEmoji.length == 0) { + emoji = null; + } + this.setState({ filteredEmoji: emoji }); + } + + private emojiFilterButton(emoji, size) { + return ( this.setFilterEmoji(emoji)} + > + { emoji } { size } + ); + } + + render() { + const reactions = this.props.reactions.getSortedAnnotationsByKey() + .map(([emoji, senders]): [string, number] => [emoji, senders.size]); + const totalReactions = reactions.reduce((acc, [_, size]) => size + acc, 0); + reactions.unshift([_t('All'), totalReactions]); + + const emojiList = reactions.map(([emoji, size]) => + (
  • { this.emojiFilterButton(emoji, size) }
  • )); + const senderList = this.getFilteredAnnotations() + .map(([emoji, sender]) => (
  • { emoji } { sender }
  • )); + return ( + +
    +
      + { emojiList } +
    +
      + { senderList } +
    +
    +
    + ); + } +}