Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 11 commits into from
Jun 10, 2022
2 changes: 1 addition & 1 deletion res/css/views/elements/_AccessibleButton.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ limitations under the License.
cursor: pointer;

&.mx_AccessibleButton_disabled {
cursor: default;
cursor: not-allowed;

&.mx_AccessibleButton_kind_primary,
&.mx_AccessibleButton_kind_primary_outline,
Expand Down
10 changes: 8 additions & 2 deletions src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ export interface IRoomState {
searchInProgress?: boolean;
callState?: CallState;
canPeek: boolean;
canSelfRedact: boolean;
showApps: boolean;
isPeeking: boolean;
showRightPanel: boolean;
Expand Down Expand Up @@ -251,6 +252,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
searchResults: null,
callState: null,
canPeek: false,
canSelfRedact: false,
showApps: false,
isPeeking: false,
showRightPanel: false,
Expand Down Expand Up @@ -1165,10 +1167,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
private updatePermissions(room: Room) {
if (room) {
const me = this.context.getUserId();
const canReact = room.getMyMembership() === "join" && room.currentState.maySendEvent("m.reaction", me);
const canReact = (
room.getMyMembership() === "join" &&
room.currentState.maySendEvent(EventType.Reaction, me)
);
const canSendMessages = room.maySendMessage();
const canSelfRedact = room.currentState.maySendEvent(EventType.RoomRedaction, me);

this.setState({ canReact, canSendMessages });
this.setState({ canReact, canSendMessages, canSelfRedact });
}
}

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
12 changes: 11 additions & 1 deletion src/components/views/emojipicker/ReactionPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class ReactionPicker extends React.Component<IProps, IState> {
}
}

private getReactions() {
private getReactions(): Record<string, string> {
if (!this.props.reactions) {
return {};
}
Expand All @@ -95,6 +95,8 @@ class ReactionPicker extends React.Component<IProps, IState> {
this.props.onFinished();
const myReactions = this.getReactions();
if (myReactions.hasOwnProperty(reaction)) {
if (this.props.mxEvent.isRedacted() || !this.context.canSelfRedact) return;

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

private isEmojiDisabled = (unicode: string): boolean => {
if (!this.getReactions()[unicode]) return false;
if (this.context.canSelfRedact) return false;

return true;
};

render() {
return <EmojiPicker
onChoose={this.onChoose}
isEmojiDisabled={this.isEmojiDisabled}
selectedEmojis={this.state.selectedEmojis}
showQuickReactions={true}
data-testid='mx_ReactionPicker'
Expand Down
5 changes: 4 additions & 1 deletion src/components/views/messages/ReactionsRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ export default class ReactionsRow extends React.PureComponent<IProps, IState> {
mxEvent={mxEvent}
reactionEvents={events}
myReactionEvent={myReactionEvent}
disabled={!this.context.canReact}
disabled={
!this.context.canReact ||
(myReactionEvent && !myReactionEvent.isRedacted() && !this.context.canSelfRedact)
}
/>;
}).filter(item => !!item);

Expand Down
1 change: 1 addition & 0 deletions src/components/views/messages/ReactionsRowButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,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 Down
1 change: 1 addition & 0 deletions src/contexts/RoomContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const RoomContext = createContext<IRoomState>({
showTopUnreadMessagesBar: false,
statusBarVisible: false,
canReact: false,
canSelfRedact: false,
canSendMessages: false,
resizing: false,
layout: Layout.Group,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ function createRoomState(room: Room, narrow: boolean): IRoomState {
shouldPeek: true,
membersLoaded: false,
numUnreadMessages: 0,
canSelfRedact: false,
canPeek: false,
showApps: false,
isPeeking: false,
Expand Down
8 changes: 4 additions & 4 deletions test/components/views/rooms/SendMessageComposer-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,18 @@ const WrapWithProviders: React.FC<{
</MatrixClientContext.Provider>;

describe('<SendMessageComposer/>', () => {
const defaultRoomContext = {
const defaultRoomContext: IRoomState = {
roomLoading: true,
peekLoading: false,
shouldPeek: true,
membersLoaded: false,
numUnreadMessages: 0,
searching: false,
guestsCanJoin: false,
canPeek: false,
showApps: false,
isPeeking: false,
showRightPanel: true,
joining: false,
atEndOfLiveTimeline: true,
atEndOfLiveTimelineInit: false,
showTopUnreadMessagesBar: false,
statusBarVisible: false,
canReact: false,
Expand All @@ -82,6 +79,9 @@ describe('<SendMessageComposer/>', () => {
matrixClientIsReady: false,
timelineRenderingType: TimelineRenderingType.Room,
liveTimeline: undefined,
canSelfRedact: false,
resizing: false,
narrow: false,
};
describe("createMessageContent", () => {
const permalinkCreator = jest.fn() as any;
Expand Down