diff --git a/app/actions/remote/channel.ts b/app/actions/remote/channel.ts index 296b9f424..7f2ffb191 100644 --- a/app/actions/remote/channel.ts +++ b/app/actions/remote/channel.ts @@ -249,13 +249,14 @@ export async function createChannel(serverUrl: string, displayName: string, purp } } -export async function patchChannel(serverUrl: string, channelPatch: Partial & {id: string}) { +export async function patchChannel(serverUrl: string, channelId: string, channelPatch: ChannelPatch) { try { const client = NetworkManager.getClient(serverUrl); const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - const channelData = await client.patchChannel(channelPatch.id, channelPatch); + const channelData = await client.patchChannel(channelId, channelPatch); const models = []; + const channelInfo = (await getChannelInfo(database, channelData.id)); if (channelInfo && (channelInfo.purpose !== channelData.purpose || channelInfo.header !== channelData.header)) { channelInfo.prepareUpdate((v) => { @@ -264,10 +265,14 @@ export async function patchChannel(serverUrl: string, channelPatch: Partial { - v.displayName = channelData.display_name; + // DM and GM display names cannot be patched and are formatted client-side; do not overwrite + if (channelData.type !== General.DM_CHANNEL && channelData.type !== General.GM_CHANNEL) { + v.displayName = channelData.display_name; + } v.type = channelData.type; }); models.push(channel); diff --git a/app/client/rest/channels.ts b/app/client/rest/channels.ts index 0e2a90cd0..1fd390b60 100644 --- a/app/client/rest/channels.ts +++ b/app/client/rest/channels.ts @@ -128,7 +128,7 @@ const ClientChannels = >(superclass: TBase ); }; - patchChannel = async (channelId: string, channelPatch: Partial) => { + patchChannel = async (channelId: string, channelPatch: ChannelPatch) => { this.analytics?.trackAPI('api_channels_patch', {channel_id: channelId}); return this.doFetch( diff --git a/app/queries/servers/role.ts b/app/queries/servers/role.ts index e3ac7f1dd..ae1c05d81 100644 --- a/app/queries/servers/role.ts +++ b/app/queries/servers/role.ts @@ -101,3 +101,17 @@ export function observeCanManageChannelMembers(database: Database, channelId: st distinctUntilChanged(), ); } + +export function observeCanManageChannelSettings(database: Database, channelId: string, user: UserModel) { + return observeChannel(database, channelId).pipe( + switchMap((c) => { + if (!c || c.deleteAt !== 0 || isDMorGM(c)) { + return of$(false); + } + + const permission = c.type === General.OPEN_CHANNEL ? Permissions.MANAGE_PUBLIC_CHANNEL_PROPERTIES : Permissions.MANAGE_PRIVATE_CHANNEL_PROPERTIES; + return observePermissionForChannel(database, c, user, permission, true); + }), + distinctUntilChanged(), + ); +} diff --git a/app/screens/channel/channel_post_list/intro/public_or_private_channel/public_or_private_channel.tsx b/app/screens/channel/channel_post_list/intro/public_or_private_channel/public_or_private_channel.tsx index 300498256..87527ea10 100644 --- a/app/screens/channel/channel_post_list/intro/public_or_private_channel/public_or_private_channel.tsx +++ b/app/screens/channel/channel_post_list/intro/public_or_private_channel/public_or_private_channel.tsx @@ -74,14 +74,20 @@ const PublicOrPrivateChannel = ({channel, creator, roles, theme}: Props) => { }, []); const canManagePeople = useMemo(() => { + if (channel.deleteAt !== 0) { + return false; + } const permission = channel.type === General.OPEN_CHANNEL ? Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS : Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS; return hasPermission(roles, permission); - }, [channel.type, roles]); + }, [channel.type, roles, channel.deleteAt]); const canSetHeader = useMemo(() => { + if (channel.deleteAt !== 0) { + return false; + } const permission = channel.type === General.OPEN_CHANNEL ? Permissions.MANAGE_PUBLIC_CHANNEL_PROPERTIES : Permissions.MANAGE_PRIVATE_CHANNEL_PROPERTIES; return hasPermission(roles, permission); - }, [channel.type, roles]); + }, [channel.type, roles, channel.deleteAt]); const createdBy = useMemo(() => { const id = channel.type === General.OPEN_CHANNEL ? t('intro.public_channel') : t('intro.private_channel'); diff --git a/app/screens/channel_info/channel_info.tsx b/app/screens/channel_info/channel_info.tsx index c71c8df98..63292e136 100644 --- a/app/screens/channel_info/channel_info.tsx +++ b/app/screens/channel_info/channel_info.tsx @@ -30,6 +30,7 @@ type Props = { canEnableDisableCalls: boolean; isCallsEnabledInChannel: boolean; canManageMembers: boolean; + canManageSettings: boolean; } const edges: Edge[] = ['bottom', 'left', 'right']; @@ -57,6 +58,7 @@ const ChannelInfo = ({ canEnableDisableCalls, isCallsEnabledInChannel, canManageMembers, + canManageSettings, }: Props) => { const theme = useTheme(); const serverUrl = useServerUrl(); @@ -103,6 +105,7 @@ const ChannelInfo = ({ type={type} callsEnabled={callsAvailable} canManageMembers={canManageMembers} + canManageSettings={canManageSettings} /> {canEnableDisableCalls && diff --git a/app/screens/channel_info/index.ts b/app/screens/channel_info/index.ts index f0456e686..3cdbd799f 100644 --- a/app/screens/channel_info/index.ts +++ b/app/screens/channel_info/index.ts @@ -10,7 +10,7 @@ import {observeIsCallsEnabledInChannel} from '@calls/observers'; import {observeCallsConfig} from '@calls/state'; import {withServerUrl} from '@context/server'; import {observeCurrentChannel} from '@queries/servers/channel'; -import {observeCanManageChannelMembers} from '@queries/servers/role'; +import {observeCanManageChannelMembers, observeCanManageChannelSettings} from '@queries/servers/role'; import { observeConfigValue, observeCurrentChannelId, @@ -105,11 +105,19 @@ const enhanced = withObservables([], ({serverUrl, database}: Props) => { switchMap(([u, cId]) => (u ? observeCanManageChannelMembers(database, cId, u) : of$(false))), distinctUntilChanged(), ); + + const canManageSettings = currentUser.pipe( + combineLatestWith(channelId), + switchMap(([u, cId]) => (u ? observeCanManageChannelSettings(database, cId, u) : of$(false))), + distinctUntilChanged(), + ); + return { type, canEnableDisableCalls, isCallsEnabledInChannel, canManageMembers, + canManageSettings, }; }); diff --git a/app/screens/channel_info/options/index.tsx b/app/screens/channel_info/options/index.tsx index dab34eefb..816fc2867 100644 --- a/app/screens/channel_info/options/index.tsx +++ b/app/screens/channel_info/options/index.tsx @@ -20,6 +20,7 @@ type Props = { type?: ChannelType; callsEnabled: boolean; canManageMembers: boolean; + canManageSettings: boolean; } const Options = ({ @@ -27,6 +28,7 @@ const Options = ({ type, callsEnabled, canManageMembers, + canManageSettings, }: Props) => { const isDMorGM = isTypeDMorGM(type); @@ -50,7 +52,7 @@ const Options = ({ testID='channel_info.options.copy_channel_link.option' /> } - {type !== General.DM_CHANNEL && type !== General.GM_CHANNEL && + {canManageSettings && } diff --git a/app/screens/create_or_edit_channel/create_or_edit_channel.tsx b/app/screens/create_or_edit_channel/create_or_edit_channel.tsx index bfc465735..1872edd07 100644 --- a/app/screens/create_or_edit_channel/create_or_edit_channel.tsx +++ b/app/screens/create_or_edit_channel/create_or_edit_channel.tsx @@ -202,16 +202,16 @@ const CreateOrEditChannel = ({ return; } - const patchChannel = { - id: channel.id, - type: channel.type, - display_name: isDirect(channel) ? channel.displayName : displayName, - purpose, + const patchChannel: ChannelPatch = { header, - } as Channel; + ...!isDirect(channel) && { + display_name: displayName, + purpose, + }, + }; setCanSave(false); - const patchedChannel = await handlePatchChannel(serverUrl, patchChannel); + const patchedChannel = await handlePatchChannel(serverUrl, channel.id, patchChannel); if (patchedChannel.error) { dispatch({ type: RequestActions.FAILURE, diff --git a/types/api/channels.d.ts b/types/api/channels.d.ts index 26baed1ce..ef0f2a659 100644 --- a/types/api/channels.d.ts +++ b/types/api/channels.d.ts @@ -44,6 +44,13 @@ type Channel = { group_constrained: boolean|null; shared: boolean; }; +type ChannelPatch = { + name?: string; + display_name?: string; + header?: string; + purpose?: string; + group_constrained?: boolean|null; +}; type ChannelWithTeamData = Channel & { team_display_name: string; team_name: string;