Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Disable redacting reactions if we don't have sufficient permissions #8767

Merged
merged 11 commits into from
Jun 10, 2022
4 changes: 4 additions & 0 deletions res/css/views/emojipicker/_EmojiPicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ limitations under the License.
list-style: none;
width: 38px;
cursor: pointer;

&.mx_AccessibleButton_disabled {
cursor: not-allowed;
}
SimonBrandner marked this conversation as resolved.
Show resolved Hide resolved
}

.mx_EmojiPicker_item {
Expand Down
2 changes: 2 additions & 0 deletions src/components/views/emojipicker/Category.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ interface IProps {
onClick(emoji: IEmoji): void;
onMouseEnter(emoji: IEmoji): void;
onMouseLeave(emoji: IEmoji): void;
isEmojiDisabled?: (unicode: string) => boolean;
}

class Category extends React.PureComponent<IProps> {
Expand All @@ -60,6 +61,7 @@ class Category extends React.PureComponent<IProps> {
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
disabled={this.props.isEmojiDisabled(emoji.unicode)}
/>
))
}</div>);
Expand Down
2 changes: 2 additions & 0 deletions src/components/views/emojipicker/Emoji.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface IProps {
onClick(emoji: IEmoji): void;
onMouseEnter(emoji: IEmoji): void;
onMouseLeave(emoji: IEmoji): void;
disabled?: boolean;
}

class Emoji extends React.PureComponent<IProps> {
Expand All @@ -40,6 +41,7 @@ class Emoji extends React.PureComponent<IProps> {
onMouseLeave={() => onMouseLeave(emoji)}
className="mx_EmojiPicker_item_wrapper"
label={emoji.unicode}
disabled={this.props.disabled}
>
<div className={`mx_EmojiPicker_item ${isSelected ? 'mx_EmojiPicker_item_selected' : ''}`}>
{ emoji.unicode }
Expand Down
2 changes: 2 additions & 0 deletions src/components/views/emojipicker/EmojiPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface IProps {
selectedEmojis?: Set<string>;
showQuickReactions?: boolean;
onChoose(unicode: string): boolean;
isEmojiDisabled?: (unicode: string) => boolean;
}

interface IState {
Expand Down Expand Up @@ -261,6 +262,7 @@ class EmojiPicker extends React.Component<IProps, IState> {
onClick={this.onClickEmoji}
onMouseEnter={this.onHoverEmoji}
onMouseLeave={this.onHoverEmojiEnd}
isEmojiDisabled={this.props.isEmojiDisabled}
selectedEmojis={this.props.selectedEmojis}
/>
);
Expand Down
18 changes: 15 additions & 3 deletions src/components/views/emojipicker/ReactionPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import dis from "../../../dispatcher/dispatcher";
import { Action } from '../../../dispatcher/actions';
import RoomContext from "../../../contexts/RoomContext";
import { FocusComposerPayload } from '../../../dispatcher/payloads/FocusComposerPayload';
import { canRedact } from "../messages/ReactionsRowButton";

interface IProps {
mxEvent: MatrixEvent;
Expand Down Expand Up @@ -73,15 +74,15 @@ class ReactionPicker extends React.Component<IProps, IState> {
}
}

private getReactions() {
private getReactions(): Record<string, MatrixEvent> {
if (!this.props.reactions) {
return {};
}
const userId = MatrixClientPeg.get().getUserId();
const myAnnotations = this.props.reactions.getAnnotationsBySender()[userId] || [];
return Object.fromEntries([...myAnnotations]
.filter(event => !event.isRedacted())
.map(event => [event.getRelation().key, event.getId()]));
.map(event => [event.getRelation().key, event]));
}

private onReactionsChange = () => {
Expand All @@ -93,9 +94,12 @@ class ReactionPicker extends React.Component<IProps, IState> {
private onChoose = (reaction: string) => {
this.componentWillUnmount();
this.props.onFinished();
const roomId = this.props.mxEvent.getRoomId();
const myReactions = this.getReactions();
if (myReactions.hasOwnProperty(reaction)) {
MatrixClientPeg.get().redactEvent(this.props.mxEvent.getRoomId(), myReactions[reaction]);
if (!canRedact(roomId, myReactions[reaction])) return;

MatrixClientPeg.get().redactEvent(roomId, myReactions[reaction].getId());
dis.dispatch<FocusComposerPayload>({
action: Action.FocusAComposer,
context: this.context.timelineRenderingType,
Expand All @@ -119,9 +123,17 @@ class ReactionPicker extends React.Component<IProps, IState> {
}
};

private isEmojiDisabled = (unicode: string): boolean => {
const reaction = this.getReactions()[unicode];

if (!reaction) return false;
return !canRedact(this.props.mxEvent.getRoomId(), reaction);
};

render() {
return <EmojiPicker
onChoose={this.onChoose}
isEmojiDisabled={this.isEmojiDisabled}
SimonBrandner marked this conversation as resolved.
Show resolved Hide resolved
selectedEmojis={this.state.selectedEmojis}
showQuickReactions={true}
data-testid='mx_ReactionPicker'
Expand Down
15 changes: 13 additions & 2 deletions src/components/views/messages/ReactionsRowButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ import dis from "../../../dispatcher/dispatcher";
import ReactionsRowButtonTooltip from "./ReactionsRowButtonTooltip";
import AccessibleButton from "../elements/AccessibleButton";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { MatrixClientPeg } from "../../../MatrixClientPeg";

export const canRedact = (roomId: string, mxEvent: MatrixEvent): boolean => {
SimonBrandner marked this conversation as resolved.
Show resolved Hide resolved
const client = MatrixClientPeg.get();
const room = client.getRoom(roomId);
return room.currentState.maySendRedactionForEvent(mxEvent, client.getUserId());
};

interface IProps {
// The event we're displaying reactions for
Expand All @@ -47,6 +54,7 @@ interface IState {

export default class ReactionsRowButton extends React.PureComponent<IProps, IState> {
static contextType = MatrixClientContext;
public context!: React.ContextType<typeof MatrixClientContext>;

state = {
tooltipRendered: false,
Expand All @@ -56,8 +64,11 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
onClick = () => {
const { mxEvent, myReactionEvent, content } = this.props;
if (myReactionEvent) {
const roomId = mxEvent.getRoomId();
if (!canRedact(roomId, myReactionEvent)) return;

this.context.redactEvent(
mxEvent.getRoomId(),
roomId,
myReactionEvent.getId(),
);
} else {
Expand Down Expand Up @@ -126,7 +137,7 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
className={classes}
aria-label={label}
onClick={this.onClick}
disabled={this.props.disabled}
disabled={this.props.disabled || (myReactionEvent && !canRedact(mxEvent.getRoomId(), myReactionEvent))}
onMouseOver={this.onMouseOver}
onMouseLeave={this.onMouseLeave}
>
Expand Down