From 188f122315f66de08bee32c552c4799e583bb7e2 Mon Sep 17 00:00:00 2001 From: Caleb Roseland Date: Wed, 13 Sep 2023 16:03:33 -0500 Subject: [PATCH 1/5] wip --- app/queries/servers/role.ts | 14 ++++++++++++++ .../public_or_private_channel.tsx | 10 ++++++++-- app/screens/channel_info/channel_info.tsx | 3 +++ app/screens/channel_info/index.ts | 10 +++++++++- app/screens/channel_info/options/index.tsx | 4 +++- .../create_or_edit_channel/channel_info_form.tsx | 4 +--- .../create_or_edit_channel.tsx | 4 ++-- 7 files changed, 40 insertions(+), 9 deletions(-) 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/channel_info_form.tsx b/app/screens/create_or_edit_channel/channel_info_form.tsx index 0ce2e2873..6267f3a83 100644 --- a/app/screens/create_or_edit_channel/channel_info_form.tsx +++ b/app/screens/create_or_edit_channel/channel_info_form.tsx @@ -231,9 +231,7 @@ export default function ChannelInfoForm({ const spaceOnTop = otherElementsSize - scrollPosition - AUTOCOMPLETE_ADJUST; const spaceOnBottom = (workingSpace + scrollPosition) - (otherElementsSize + headerFieldHeight + BOTTOM_AUTOCOMPLETE_SEPARATION); - const autocompletePosition = spaceOnBottom > spaceOnTop ? - (otherElementsSize + headerFieldHeight) - scrollPosition : - (workingSpace + scrollPosition + AUTOCOMPLETE_ADJUST + keyboardOverlap) - otherElementsSize; + const autocompletePosition = spaceOnBottom > spaceOnTop ? (otherElementsSize + headerFieldHeight) - scrollPosition : (workingSpace + scrollPosition + AUTOCOMPLETE_ADJUST + keyboardOverlap) - otherElementsSize; const autocompleteAvailableSpace = spaceOnBottom > spaceOnTop ? spaceOnBottom : spaceOnTop; const growDown = spaceOnBottom > spaceOnTop; 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..2b64ed9c8 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 @@ -206,8 +206,8 @@ const CreateOrEditChannel = ({ id: channel.id, type: channel.type, display_name: isDirect(channel) ? channel.displayName : displayName, - purpose, - header, + purpose: isDirect(channel) ? null : purpose, + header: isDirect(channel) ? null : header, } as Channel; setCanSave(false); From cdbafea6074f3559889198af4984e99f2c759f0d Mon Sep 17 00:00:00 2001 From: Caleb Roseland Date: Wed, 4 Oct 2023 11:22:58 -0500 Subject: [PATCH 2/5] fix: editing DM/GM channel header --- app/actions/remote/channel.ts | 12 +++++++++--- app/client/rest/channels.ts | 2 +- .../create_or_edit_channel.tsx | 16 ++++++++-------- types/api/channels.d.ts | 7 +++++++ 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/app/actions/remote/channel.ts b/app/actions/remote/channel.ts index 296b9f424..eac64e338 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,11 +265,16 @@ 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; + logInfo({channelData}); }); 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/screens/create_or_edit_channel/create_or_edit_channel.tsx b/app/screens/create_or_edit_channel/create_or_edit_channel.tsx index 2b64ed9c8..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: isDirect(channel) ? null : purpose, - header: isDirect(channel) ? null : header, - } as Channel; + const patchChannel: ChannelPatch = { + header, + ...!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; From 5811b34b750063fefa242d523f40ac4d7f36259e Mon Sep 17 00:00:00 2001 From: Caleb Roseland Date: Wed, 4 Oct 2023 15:35:14 -0500 Subject: [PATCH 3/5] remove log --- app/actions/remote/channel.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/actions/remote/channel.ts b/app/actions/remote/channel.ts index eac64e338..7f2ffb191 100644 --- a/app/actions/remote/channel.ts +++ b/app/actions/remote/channel.ts @@ -274,7 +274,6 @@ export async function patchChannel(serverUrl: string, channelId: string, channel v.displayName = channelData.display_name; } v.type = channelData.type; - logInfo({channelData}); }); models.push(channel); } From 0bd2ea7faa7090949b2637fb7a5d468b55ff691f Mon Sep 17 00:00:00 2001 From: Caleb Roseland Date: Wed, 4 Oct 2023 15:35:36 -0500 Subject: [PATCH 4/5] fix multiline ternary linting --- app/screens/create_or_edit_channel/channel_info_form.tsx | 4 +++- eslint/eslint-mattermost.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/screens/create_or_edit_channel/channel_info_form.tsx b/app/screens/create_or_edit_channel/channel_info_form.tsx index 6267f3a83..0ce2e2873 100644 --- a/app/screens/create_or_edit_channel/channel_info_form.tsx +++ b/app/screens/create_or_edit_channel/channel_info_form.tsx @@ -231,7 +231,9 @@ export default function ChannelInfoForm({ const spaceOnTop = otherElementsSize - scrollPosition - AUTOCOMPLETE_ADJUST; const spaceOnBottom = (workingSpace + scrollPosition) - (otherElementsSize + headerFieldHeight + BOTTOM_AUTOCOMPLETE_SEPARATION); - const autocompletePosition = spaceOnBottom > spaceOnTop ? (otherElementsSize + headerFieldHeight) - scrollPosition : (workingSpace + scrollPosition + AUTOCOMPLETE_ADJUST + keyboardOverlap) - otherElementsSize; + const autocompletePosition = spaceOnBottom > spaceOnTop ? + (otherElementsSize + headerFieldHeight) - scrollPosition : + (workingSpace + scrollPosition + AUTOCOMPLETE_ADJUST + keyboardOverlap) - otherElementsSize; const autocompleteAvailableSpace = spaceOnBottom > spaceOnTop ? spaceOnBottom : spaceOnTop; const growDown = spaceOnBottom > spaceOnTop; diff --git a/eslint/eslint-mattermost.json b/eslint/eslint-mattermost.json index 664a9a97b..1c5007b81 100644 --- a/eslint/eslint-mattermost.json +++ b/eslint/eslint-mattermost.json @@ -185,7 +185,7 @@ ], "multiline-ternary": [ 1, - "never" + "always-multiline" ], "new-cap": 2, "new-parens": 2, From 251850a8eeea2f44c42cc3140a53a584d211de89 Mon Sep 17 00:00:00 2001 From: Caleb Roseland Date: Wed, 4 Oct 2023 16:46:27 -0500 Subject: [PATCH 5/5] undo ternary lint rule change --- eslint/eslint-mattermost.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint/eslint-mattermost.json b/eslint/eslint-mattermost.json index 1c5007b81..664a9a97b 100644 --- a/eslint/eslint-mattermost.json +++ b/eslint/eslint-mattermost.json @@ -185,7 +185,7 @@ ], "multiline-ternary": [ 1, - "always-multiline" + "never" ], "new-cap": 2, "new-parens": 2,