feat: capability to enable/disable writing in rooms read only (#5298)

* feat: capability to enable writing in rooms read only

* minor tweak

* add shallowEqual

* change the message box properly when the user is enable to write
This commit is contained in:
Reinaldo Neto 2023-11-17 15:48:32 -03:00 committed by GitHub
parent 60a352beb4
commit d6c37bf4a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 99 additions and 50 deletions

View File

@ -39,6 +39,7 @@ export interface IRoom {
default?: boolean;
featured?: boolean;
muted?: string[];
unmuted?: string[];
teamId?: string;
ignored?: string;

View File

@ -72,6 +72,7 @@ export interface ISubscription {
archived: boolean;
joinCodeRequired?: boolean;
muted?: string[];
unmuted?: string[];
ignored?: string[];
broadcast?: boolean;
prid?: string;
@ -111,7 +112,8 @@ export interface ISubscription {
uploads: RelationModified<TUploadModel>;
}
export type TSubscriptionModel = ISubscription & Model & {
export type TSubscriptionModel = ISubscription &
Model & {
asPlain: () => ISubscription;
};
export type TSubscription = TSubscriptionModel | ISubscription;

View File

@ -754,5 +754,9 @@
"Supported_versions_expired_title": "{{workspace_name}} is running an unsupported version of Rocket.Chat",
"Supported_versions_expired_description": "An admin needs to update the workspace to a supported version in order to reenable access from mobile and desktop apps.",
"Supported_versions_warning_update_required": "Update required",
"The_user_wont_be_able_to_type_in_roomName": "The user won't be able to type in {{roomName}}",
"The_user_will_be_able_to_type_in_roomName": "The user will be able to type in {{roomName}}",
"Enable_writing_in_room": "Enable writing in room",
"Disable_writing_in_room": "Disable writing in room",
"Pinned_a_message": "Pinned a message:"
}

View File

@ -753,5 +753,9 @@
"Call_started": "Chamada iniciada",
"Supported_versions_expired_title": "{{workspace_name}} está executando uma versão não suportada do Rocket.Chat",
"Supported_versions_expired_description": "Um administrador precisa atualizar o espaço de trabalho para uma versão suportada a fim de reabilitar o acesso a partir de aplicativos móveis e de desktop.",
"Supported_versions_warning_update_required": "Atualização necessária"
"Supported_versions_warning_update_required": "Atualização necessária",
"The_user_wont_be_able_to_type_in_roomName": "O usuário não poderá digitar em {{roomName}}",
"The_user_will_be_able_to_type_in_roomName": "O usuário poderá digitar em {{roomName}}",
"Enable_writing_in_room": "Permitir escrita na sala",
"Disable_writing_in_room": "Desabilitar escrita na sala"
}

View File

@ -79,6 +79,8 @@ export default class Subscription extends Model {
@json('muted', sanitizer) muted;
@json('unmuted', sanitizer) unmuted;
@json('ignored', sanitizer) ignored;
@field('broadcast') broadcast;

View File

@ -275,6 +275,15 @@ export default schemaMigrations({
columns: [{ name: 'sanitized_fname', type: 'string', isOptional: true }]
})
]
},
{
toVersion: 23,
steps: [
addColumns({
table: 'subscriptions',
columns: [{ name: 'unmuted', type: 'string', isOptional: true }]
})
]
}
]
});

View File

@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({
version: 22,
version: 23,
tables: [
tableSchema({
name: 'subscriptions',
@ -65,7 +65,8 @@ export default appSchema({
{ name: 'on_hold', type: 'boolean', isOptional: true },
{ name: 'source', type: 'string', isOptional: true },
{ name: 'hide_mention_status', type: 'boolean', isOptional: true },
{ name: 'users_count', type: 'number', isOptional: true }
{ name: 'users_count', type: 'number', isOptional: true },
{ name: 'unmuted', type: 'string', isOptional: true }
]
}),
tableSchema({

View File

@ -38,6 +38,7 @@ export default async (subscriptions: IServerSubscription[], rooms: IServerRoom[]
archived: s.archived,
joinCodeRequired: s.joinCodeRequired,
muted: s.muted,
unmuted: s.unmuted,
broadcast: s.broadcast,
prid: s.prid,
draftMessage: s.draftMessage,
@ -78,6 +79,7 @@ export default async (subscriptions: IServerSubscription[], rooms: IServerRoom[]
ro: r.ro,
broadcast: r.broadcast,
muted: r.muted,
unmuted: r.unmuted,
sysMes: r.sysMes,
v: r.v,
departmentId: r.departmentId,

View File

@ -2,11 +2,13 @@ import { store as reduxStore } from '../../store/auxStore';
import { ISubscription } from '../../../definitions';
import { hasPermission } from './helpers';
const canPostReadOnly = async ({ rid }: { rid: string }) => {
const canPostReadOnly = async (room: Partial<ISubscription>, username: string) => {
// RC 6.4.0
const isUnmuted = !!room?.unmuted?.find(m => m === username);
// TODO: this is not reactive. If this permission changes, the component won't be updated
const postReadOnlyPermission = reduxStore.getState().permissions['post-readonly'];
const permission = await hasPermission([postReadOnlyPermission], rid);
return permission[0];
const permission = await hasPermission([postReadOnlyPermission], room.rid);
return permission[0] || isUnmuted;
};
const isMuted = (room: Partial<ISubscription>, username: string) =>
@ -20,7 +22,7 @@ export const isReadOnly = async (room: Partial<ISubscription>, username: string)
return true;
}
if (room?.ro) {
const allowPost = await canPostReadOnly({ rid: room.rid as string });
const allowPost = await canPostReadOnly(room, username);
if (allowPost) {
return false;
}

View File

@ -67,6 +67,11 @@ export const merge = (
} else {
mergedSubscription.muted = [];
}
if (room?.unmuted?.length) {
mergedSubscription.unmuted = room.unmuted.filter(unmuted => !!unmuted);
} else {
mergedSubscription.unmuted = [];
}
if (room?.v) {
mergedSubscription.visitor = room.v;
}

View File

@ -83,6 +83,7 @@ const createOrUpdateSubscription = async (subscription: ISubscription, room: ISe
archived: s.archived,
joinCodeRequired: s.joinCodeRequired,
muted: s.muted,
unmuted: s.unmuted,
ignored: s.ignored,
broadcast: s.broadcast,
prid: s.prid,

View File

@ -37,7 +37,7 @@ export const fetchRoomMembersRoles = async (roomType: TRoomType, rid: string, up
export const handleMute = async (user: TUserModel, rid: string) => {
try {
await Services.toggleMuteUserInRoom(rid, user?.username, !user?.muted);
await Services.toggleMuteUserInRoom(rid, user?.username, !user.muted);
EventEmitter.emit(LISTENER, {
message: I18n.t('User_has_been_key', { key: user?.muted ? I18n.t('unmuted') : I18n.t('muted') })
});

View File

@ -1,10 +1,11 @@
import { NavigationProp, RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import React, { useEffect, useReducer } from 'react';
import { FlatList, Text, View } from 'react-native';
import { shallowEqual } from 'react-redux';
import { TActionSheetOptionsItem, useActionSheet } from '../../containers/ActionSheet';
import ActivityIndicator from '../../containers/ActivityIndicator';
import { CustomIcon } from '../../containers/CustomIcon';
import { CustomIcon, TIconsName } from '../../containers/CustomIcon';
import * as HeaderButton from '../../containers/HeaderButton';
import * as List from '../../containers/List';
import { RadioButton } from '../../containers/RadioButton';
@ -15,7 +16,7 @@ import UserItem from '../../containers/UserItem';
import { TSubscriptionModel, TUserModel } from '../../definitions';
import I18n from '../../i18n';
import { useAppSelector, usePermissions } from '../../lib/hooks';
import { getRoomTitle, isGroupChat } from '../../lib/methods/helpers';
import { compareServerVersion, getRoomTitle, isGroupChat } from '../../lib/methods/helpers';
import { handleIgnore } from '../../lib/methods/helpers/handleIgnore';
import { showConfirmationAlert } from '../../lib/methods/helpers/info';
import log from '../../lib/methods/helpers/log';
@ -73,10 +74,15 @@ const RoomMembersView = (): React.ReactElement => {
const { params } = useRoute<RouteProp<ModalStackParamList, 'RoomMembersView'>>();
const navigation = useNavigation<NavigationProp<ModalStackParamList, 'RoomMembersView'>>();
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
const useRealName = useAppSelector(state => state.settings.UI_Use_Real_Name);
const user = useAppSelector(state => getUserSelector(state));
const { isMasterDetail, serverVersion, useRealName, user } = useAppSelector(
state => ({
isMasterDetail: state.app.isMasterDetail,
useRealName: state.settings.UI_Use_Real_Name,
user: getUserSelector(state),
serverVersion: state.server.version
}),
shallowEqual
);
const [state, updateState] = useReducer(
(state: IRoomMembersViewState, newState: Partial<IRoomMembersViewState>) => ({ ...state, ...newState }),
@ -200,38 +206,6 @@ const RoomMembersView = (): React.ReactElement => {
}
];
// Ignore
if (selectedUser._id !== user.id) {
const { ignored } = room;
const isIgnored = ignored?.includes?.(selectedUser._id);
options.push({
icon: 'ignore',
title: I18n.t(isIgnored ? 'Unignore' : 'Ignore'),
onPress: () => handleIgnore(selectedUser._id, !isIgnored, room.rid),
testID: 'action-sheet-ignore-user'
});
}
if (muteUserPermission) {
const { muted = [] } = room;
const userIsMuted = muted.find?.(m => m === selectedUser.username);
selectedUser.muted = !!userIsMuted;
options.push({
icon: userIsMuted ? 'audio' : 'audio-disabled',
title: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
onPress: () => {
showConfirmationAlert({
message: I18n.t(`The_user_${userIsMuted ? 'will' : 'wont'}_be_able_to_type_in_roomName`, {
roomName: getRoomTitle(room)
}),
confirmationText: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
onPress: () => handleMute(selectedUser, room.rid)
});
},
testID: 'action-sheet-mute-user'
});
}
// Owner
if (setOwnerPermission) {
const isOwner = fetchRole('owner', selectedUser, roomRoles);
@ -277,6 +251,47 @@ const RoomMembersView = (): React.ReactElement => {
});
}
if (muteUserPermission) {
const { muted = [], ro: readOnly, unmuted = [] } = room;
let userIsMuted = !!muted.find?.(m => m === selectedUser.username);
let icon: TIconsName = userIsMuted ? 'audio' : 'audio-disabled';
let title = I18n.t(userIsMuted ? 'Unmute' : 'Mute');
if (compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '6.4.0')) {
if (readOnly) {
userIsMuted = !unmuted?.find?.(m => m === selectedUser.username);
}
icon = userIsMuted ? 'message' : 'message-disabled';
title = I18n.t(userIsMuted ? 'Enable_writing_in_room' : 'Disable_writing_in_room');
}
selectedUser.muted = !!userIsMuted;
options.push({
icon,
title,
onPress: () => {
showConfirmationAlert({
message: I18n.t(`The_user_${userIsMuted ? 'will' : 'wont'}_be_able_to_type_in_roomName`, {
roomName: getRoomTitle(room)
}),
confirmationText: title,
onPress: () => handleMute(selectedUser, room.rid)
});
},
testID: 'action-sheet-mute-user'
});
}
// Ignore
if (selectedUser._id !== user.id) {
const { ignored } = room;
const isIgnored = ignored?.includes?.(selectedUser._id);
options.push({
icon: 'ignore',
title: I18n.t(isIgnored ? 'Unignore' : 'Ignore'),
onPress: () => handleIgnore(selectedUser._id, !isIgnored, room.rid),
testID: 'action-sheet-ignore-user'
});
}
// Remove from team
if (editTeamMemberPermission) {
options.push({

View File

@ -140,7 +140,8 @@ const roomAttrsUpdate = [
'onHold',
't',
'autoTranslate',
'autoTranslateLanguage'
'autoTranslateLanguage',
'unmuted'
] as TRoomUpdate[];
interface IRoomViewProps extends IActionSheetProvider, IBaseScreen<ChatsStackParamList, 'RoomView'> {