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

This commit is contained in:
Šimon Brandner 2022-06-10 20:41:05 +02:00 committed by GitHub
parent 3f99f594de
commit 9b8b1d193e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 37 additions and 9 deletions

View file

@ -18,7 +18,7 @@ limitations under the License.
cursor: pointer; cursor: pointer;
&.mx_AccessibleButton_disabled { &.mx_AccessibleButton_disabled {
cursor: default; cursor: not-allowed;
&.mx_AccessibleButton_kind_primary, &.mx_AccessibleButton_kind_primary,
&.mx_AccessibleButton_kind_primary_outline, &.mx_AccessibleButton_kind_primary_outline,

View file

@ -169,6 +169,7 @@ export interface IRoomState {
searchInProgress?: boolean; searchInProgress?: boolean;
callState?: CallState; callState?: CallState;
canPeek: boolean; canPeek: boolean;
canSelfRedact: boolean;
showApps: boolean; showApps: boolean;
isPeeking: boolean; isPeeking: boolean;
showRightPanel: boolean; showRightPanel: boolean;
@ -252,6 +253,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
searchResults: null, searchResults: null,
callState: null, callState: null,
canPeek: false, canPeek: false,
canSelfRedact: false,
showApps: false, showApps: false,
isPeeking: false, isPeeking: false,
showRightPanel: false, showRightPanel: false,
@ -1173,10 +1175,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
private updatePermissions(room: Room) { private updatePermissions(room: Room) {
if (room) { if (room) {
const me = this.context.getUserId(); 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 canSendMessages = room.maySendMessage();
const canSelfRedact = room.currentState.maySendEvent(EventType.RoomRedaction, me);
this.setState({ canReact, canSendMessages }); this.setState({ canReact, canSendMessages, canSelfRedact });
} }
} }

View file

@ -45,6 +45,7 @@ interface IProps {
onClick(emoji: IEmoji): void; onClick(emoji: IEmoji): void;
onMouseEnter(emoji: IEmoji): void; onMouseEnter(emoji: IEmoji): void;
onMouseLeave(emoji: IEmoji): void; onMouseLeave(emoji: IEmoji): void;
isEmojiDisabled?: (unicode: string) => boolean;
} }
class Category extends React.PureComponent<IProps> { class Category extends React.PureComponent<IProps> {
@ -60,6 +61,7 @@ class Category extends React.PureComponent<IProps> {
onClick={onClick} onClick={onClick}
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
disabled={this.props.isEmojiDisabled?.(emoji.unicode)}
/> />
)) ))
}</div>); }</div>);

View file

@ -26,6 +26,7 @@ interface IProps {
onClick(emoji: IEmoji): void; onClick(emoji: IEmoji): void;
onMouseEnter(emoji: IEmoji): void; onMouseEnter(emoji: IEmoji): void;
onMouseLeave(emoji: IEmoji): void; onMouseLeave(emoji: IEmoji): void;
disabled?: boolean;
} }
class Emoji extends React.PureComponent<IProps> { class Emoji extends React.PureComponent<IProps> {
@ -40,6 +41,7 @@ class Emoji extends React.PureComponent<IProps> {
onMouseLeave={() => onMouseLeave(emoji)} onMouseLeave={() => onMouseLeave(emoji)}
className="mx_EmojiPicker_item_wrapper" className="mx_EmojiPicker_item_wrapper"
label={emoji.unicode} label={emoji.unicode}
disabled={this.props.disabled}
> >
<div className={`mx_EmojiPicker_item ${isSelected ? 'mx_EmojiPicker_item_selected' : ''}`}> <div className={`mx_EmojiPicker_item ${isSelected ? 'mx_EmojiPicker_item_selected' : ''}`}>
{ emoji.unicode } { emoji.unicode }

View file

@ -37,6 +37,7 @@ interface IProps {
selectedEmojis?: Set<string>; selectedEmojis?: Set<string>;
showQuickReactions?: boolean; showQuickReactions?: boolean;
onChoose(unicode: string): boolean; onChoose(unicode: string): boolean;
isEmojiDisabled?: (unicode: string) => boolean;
} }
interface IState { interface IState {
@ -261,6 +262,7 @@ class EmojiPicker extends React.Component<IProps, IState> {
onClick={this.onClickEmoji} onClick={this.onClickEmoji}
onMouseEnter={this.onHoverEmoji} onMouseEnter={this.onHoverEmoji}
onMouseLeave={this.onHoverEmojiEnd} onMouseLeave={this.onHoverEmojiEnd}
isEmojiDisabled={this.props.isEmojiDisabled}
selectedEmojis={this.props.selectedEmojis} selectedEmojis={this.props.selectedEmojis}
/> />
); );

View file

@ -73,7 +73,7 @@ class ReactionPicker extends React.Component<IProps, IState> {
} }
} }
private getReactions() { private getReactions(): Record<string, string> {
if (!this.props.reactions) { if (!this.props.reactions) {
return {}; return {};
} }
@ -95,6 +95,8 @@ class ReactionPicker extends React.Component<IProps, IState> {
this.props.onFinished(); this.props.onFinished();
const myReactions = this.getReactions(); const myReactions = this.getReactions();
if (myReactions.hasOwnProperty(reaction)) { if (myReactions.hasOwnProperty(reaction)) {
if (this.props.mxEvent.isRedacted() || !this.context.canSelfRedact) return;
MatrixClientPeg.get().redactEvent(this.props.mxEvent.getRoomId(), myReactions[reaction]); MatrixClientPeg.get().redactEvent(this.props.mxEvent.getRoomId(), myReactions[reaction]);
dis.dispatch<FocusComposerPayload>({ dis.dispatch<FocusComposerPayload>({
action: Action.FocusAComposer, action: Action.FocusAComposer,
@ -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() { render() {
return <EmojiPicker return <EmojiPicker
onChoose={this.onChoose} onChoose={this.onChoose}
isEmojiDisabled={this.isEmojiDisabled}
selectedEmojis={this.state.selectedEmojis} selectedEmojis={this.state.selectedEmojis}
showQuickReactions={true} showQuickReactions={true}
data-testid='mx_ReactionPicker' data-testid='mx_ReactionPicker'

View file

@ -183,7 +183,10 @@ export default class ReactionsRow extends React.PureComponent<IProps, IState> {
mxEvent={mxEvent} mxEvent={mxEvent}
reactionEvents={events} reactionEvents={events}
myReactionEvent={myReactionEvent} myReactionEvent={myReactionEvent}
disabled={!this.context.canReact} disabled={
!this.context.canReact ||
(myReactionEvent && !myReactionEvent.isRedacted() && !this.context.canSelfRedact)
}
/>; />;
}).filter(item => !!item); }).filter(item => !!item);

View file

@ -47,6 +47,7 @@ interface IState {
export default class ReactionsRowButton extends React.PureComponent<IProps, IState> { export default class ReactionsRowButton extends React.PureComponent<IProps, IState> {
static contextType = MatrixClientContext; static contextType = MatrixClientContext;
public context!: React.ContextType<typeof MatrixClientContext>;
state = { state = {
tooltipRendered: false, tooltipRendered: false,

View file

@ -43,6 +43,7 @@ const RoomContext = createContext<IRoomState>({
showTopUnreadMessagesBar: false, showTopUnreadMessagesBar: false,
statusBarVisible: false, statusBarVisible: false,
canReact: false, canReact: false,
canSelfRedact: false,
canSendMessages: false, canSendMessages: false,
resizing: false, resizing: false,
layout: Layout.Group, layout: Layout.Group,

View file

@ -209,6 +209,7 @@ function createRoomState(room: Room, narrow: boolean): IRoomState {
shouldPeek: true, shouldPeek: true,
membersLoaded: false, membersLoaded: false,
numUnreadMessages: 0, numUnreadMessages: 0,
canSelfRedact: false,
canPeek: false, canPeek: false,
showApps: false, showApps: false,
isPeeking: false, isPeeking: false,

View file

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