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:
parent
60a352beb4
commit
d6c37bf4a2
|
@ -39,6 +39,7 @@ export interface IRoom {
|
|||
default?: boolean;
|
||||
featured?: boolean;
|
||||
muted?: string[];
|
||||
unmuted?: string[];
|
||||
teamId?: string;
|
||||
ignored?: string;
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ export interface ISubscription {
|
|||
archived: boolean;
|
||||
joinCodeRequired?: boolean;
|
||||
muted?: string[];
|
||||
unmuted?: string[];
|
||||
ignored?: string[];
|
||||
broadcast?: boolean;
|
||||
prid?: string;
|
||||
|
@ -111,9 +112,10 @@ export interface ISubscription {
|
|||
uploads: RelationModified<TUploadModel>;
|
||||
}
|
||||
|
||||
export type TSubscriptionModel = ISubscription & Model & {
|
||||
asPlain: () => ISubscription;
|
||||
};
|
||||
export type TSubscriptionModel = ISubscription &
|
||||
Model & {
|
||||
asPlain: () => ISubscription;
|
||||
};
|
||||
export type TSubscription = TSubscriptionModel | ISubscription;
|
||||
|
||||
// https://github.com/RocketChat/Rocket.Chat/blob/a88a96fcadd925b678ff27ada37075e029f78b5e/definition/ISubscription.ts#L8
|
||||
|
|
|
@ -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:"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 }]
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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') })
|
||||
});
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -140,7 +140,8 @@ const roomAttrsUpdate = [
|
|||
'onHold',
|
||||
't',
|
||||
'autoTranslate',
|
||||
'autoTranslateLanguage'
|
||||
'autoTranslateLanguage',
|
||||
'unmuted'
|
||||
] as TRoomUpdate[];
|
||||
|
||||
interface IRoomViewProps extends IActionSheetProvider, IBaseScreen<ChatsStackParamList, 'RoomView'> {
|
||||
|
|
Loading…
Reference in New Issue