Show profile message actions for non-DM user profiles (#9496)
* Show profile message actions for non-DM user profiles
* Add DM profile visibility safeguards and coverage
* Simplify DM channel name parsing and add reversed order test
Remove redundant manual split('__') validation in
isDirectMessageWithViewedUser, relying on getUserIdFromChannelName
which already handles both name orderings. Add test for reversed
DM channel name order (B__A) to prevent regressions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f9001ec7cd
commit
9699318043
4 changed files with 178 additions and 9 deletions
73
app/screens/user_profile/index.test.ts
Normal file
73
app/screens/user_profile/index.test.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {General} from '@constants';
|
||||
|
||||
import {isDirectMessageWithViewedUser} from './index';
|
||||
|
||||
describe('screens/user_profile/index', () => {
|
||||
it('should return true when viewing the DM counterpart user', () => {
|
||||
const result = isDirectMessageWithViewedUser(
|
||||
{
|
||||
name: 'current_user_id__other_user_id',
|
||||
type: General.DM_CHANNEL,
|
||||
},
|
||||
'current_user_id',
|
||||
'other_user_id',
|
||||
);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when viewing a user different from the DM counterpart', () => {
|
||||
const result = isDirectMessageWithViewedUser(
|
||||
{
|
||||
name: 'current_user_id__other_user_id',
|
||||
type: General.DM_CHANNEL,
|
||||
},
|
||||
'current_user_id',
|
||||
'someone_else',
|
||||
);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for non-DM channels', () => {
|
||||
const result = isDirectMessageWithViewedUser(
|
||||
{
|
||||
name: 'current_user_id__other_user_id',
|
||||
type: General.OPEN_CHANNEL,
|
||||
},
|
||||
'current_user_id',
|
||||
'other_user_id',
|
||||
);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when DM channel name has reversed user order', () => {
|
||||
const result = isDirectMessageWithViewedUser(
|
||||
{
|
||||
name: 'other_user_id__current_user_id',
|
||||
type: General.DM_CHANNEL,
|
||||
},
|
||||
'current_user_id',
|
||||
'other_user_id',
|
||||
);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when DM channel name is malformed', () => {
|
||||
const result = isDirectMessageWithViewedUser(
|
||||
{
|
||||
name: 'malformed_channel_name',
|
||||
type: General.DM_CHANNEL,
|
||||
},
|
||||
'current_user_id',
|
||||
'other_user_id',
|
||||
);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -14,7 +14,7 @@ import {observeCanManageChannelMembers, observePermissionForChannel} from '@quer
|
|||
import {observeConfigBooleanValue, observeCurrentTeamId, observeCurrentUserId} from '@queries/servers/system';
|
||||
import {observeTeammateNameDisplay, observeCurrentUser, observeUser, observeUserIsChannelAdmin, observeUserIsTeamAdmin} from '@queries/servers/user';
|
||||
import {isDefaultChannel} from '@utils/channel';
|
||||
import {isSystemAdmin, sortCustomProfileAttributes, convertToAttributesMap, convertProfileAttributesToCustomAttributes} from '@utils/user';
|
||||
import {isSystemAdmin, sortCustomProfileAttributes, convertToAttributesMap, convertProfileAttributesToCustomAttributes, getUserIdFromChannelName} from '@utils/user';
|
||||
|
||||
import UserProfile from './user_profile';
|
||||
|
||||
|
|
@ -26,6 +26,20 @@ type EnhancedProps = WithDatabaseArgs & {
|
|||
channelId?: string;
|
||||
}
|
||||
|
||||
type DMChannelInfo = {
|
||||
name: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export function isDirectMessageWithViewedUser(channel: DMChannelInfo|undefined, currentUserId: string, viewedUserId: string|undefined): boolean {
|
||||
if (!channel || channel.type !== General.DM_CHANNEL || !viewedUserId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const dmUserId = getUserIdFromChannelName(currentUserId, channel.name);
|
||||
return Boolean(dmUserId) && dmUserId === viewedUserId;
|
||||
}
|
||||
|
||||
const enhanced = withObservables([], ({channelId, database, userId}: EnhancedProps) => {
|
||||
const currentUser = observeCurrentUser(database);
|
||||
const currentUserId = observeCurrentUserId(database);
|
||||
|
|
@ -36,9 +50,9 @@ const enhanced = withObservables([], ({channelId, database, userId}: EnhancedPro
|
|||
const isDC = channel ? channel.pipe(
|
||||
switchMap((c) => of$(isDefaultChannel(c))),
|
||||
) : of$(false);
|
||||
const isDirectMessage = channelId ? channel.pipe(
|
||||
switchMap((c) => of$(c?.type === General.DM_CHANNEL)),
|
||||
) : of$(false);
|
||||
const isDirectMessageWithUser = combineLatest([channel, currentUserId, user]).pipe(
|
||||
map(([c, currentId, viewedUser]) => isDirectMessageWithViewedUser(c, currentId, viewedUser?.id)),
|
||||
);
|
||||
const teamId = channel.pipe(switchMap((c) => (c?.teamId ? of$(c.teamId) : observeCurrentTeamId(database))));
|
||||
const isTeamAdmin = teamId.pipe(switchMap((id) => observeUserIsTeamAdmin(database, userId, id)));
|
||||
const systemAdmin = user.pipe(switchMap((u) => of$(u?.roles ? isSystemAdmin(u.roles) : false)));
|
||||
|
|
@ -81,7 +95,7 @@ const enhanced = withObservables([], ({channelId, database, userId}: EnhancedPro
|
|||
isChannelAdmin,
|
||||
isCustomStatusEnabled,
|
||||
isDefaultChannel: isDC,
|
||||
isDirectMessage,
|
||||
isDirectMessageWithUser,
|
||||
isMilitaryTime,
|
||||
isSystemAdmin: systemAdmin,
|
||||
isTeamAdmin,
|
||||
|
|
|
|||
61
app/screens/user_profile/user_profile.test.tsx
Normal file
61
app/screens/user_profile/user_profile.test.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shouldShowUserProfileOptions} from './user_profile';
|
||||
|
||||
describe('screens/user_profile/user_profile', () => {
|
||||
it('should hide profile options when viewing the same user in a DM from channel context', () => {
|
||||
const shouldShow = shouldShowUserProfileOptions({
|
||||
channelContext: true,
|
||||
isDirectMessageWithUser: true,
|
||||
manageMode: false,
|
||||
override: false,
|
||||
});
|
||||
|
||||
expect(shouldShow).toBe(false);
|
||||
});
|
||||
|
||||
it('should show profile options when viewing another user in a DM from channel context', () => {
|
||||
const shouldShow = shouldShowUserProfileOptions({
|
||||
channelContext: true,
|
||||
isDirectMessageWithUser: false,
|
||||
manageMode: false,
|
||||
override: false,
|
||||
});
|
||||
|
||||
expect(shouldShow).toBe(true);
|
||||
});
|
||||
|
||||
it('should hide profile options when in manage mode', () => {
|
||||
const shouldShow = shouldShowUserProfileOptions({
|
||||
channelContext: true,
|
||||
isDirectMessageWithUser: false,
|
||||
manageMode: true,
|
||||
override: false,
|
||||
});
|
||||
|
||||
expect(shouldShow).toBe(false);
|
||||
});
|
||||
|
||||
it('should hide profile options when override is enabled', () => {
|
||||
const shouldShow = shouldShowUserProfileOptions({
|
||||
channelContext: false,
|
||||
isDirectMessageWithUser: false,
|
||||
manageMode: false,
|
||||
override: true,
|
||||
});
|
||||
|
||||
expect(shouldShow).toBe(false);
|
||||
});
|
||||
|
||||
it('should show profile options outside channel context even for direct message with viewed user', () => {
|
||||
const shouldShow = shouldShowUserProfileOptions({
|
||||
channelContext: false,
|
||||
isDirectMessageWithUser: true,
|
||||
manageMode: false,
|
||||
override: false,
|
||||
});
|
||||
|
||||
expect(shouldShow).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -34,7 +34,7 @@ type Props = {
|
|||
isChannelAdmin: boolean;
|
||||
canManageAndRemoveMembers?: boolean;
|
||||
isCustomStatusEnabled: boolean;
|
||||
isDirectMessage: boolean;
|
||||
isDirectMessageWithUser: boolean;
|
||||
isDefaultChannel: boolean;
|
||||
isMilitaryTime: boolean;
|
||||
isSystemAdmin: boolean;
|
||||
|
|
@ -66,6 +66,22 @@ const messages = defineMessages({
|
|||
});
|
||||
const channelContextScreens: AvailableScreens[] = [Screens.CHANNEL, Screens.THREAD];
|
||||
|
||||
type ShouldShowUserProfileOptionsProps = {
|
||||
channelContext: boolean;
|
||||
isDirectMessageWithUser: boolean;
|
||||
manageMode: boolean;
|
||||
override: boolean;
|
||||
};
|
||||
|
||||
export function shouldShowUserProfileOptions({
|
||||
channelContext,
|
||||
isDirectMessageWithUser,
|
||||
manageMode,
|
||||
override,
|
||||
}: ShouldShowUserProfileOptionsProps) {
|
||||
return (!isDirectMessageWithUser || !channelContext) && !override && !manageMode;
|
||||
}
|
||||
|
||||
const UserProfile = ({
|
||||
canChangeMemberRoles,
|
||||
canManageAndRemoveMembers,
|
||||
|
|
@ -77,7 +93,7 @@ const UserProfile = ({
|
|||
isChannelAdmin,
|
||||
isCustomStatusEnabled,
|
||||
isDefaultChannel,
|
||||
isDirectMessage,
|
||||
isDirectMessageWithUser,
|
||||
isMilitaryTime,
|
||||
isSystemAdmin,
|
||||
isTeamAdmin,
|
||||
|
|
@ -113,7 +129,12 @@ const UserProfile = ({
|
|||
}
|
||||
|
||||
const showCustomStatus = isCustomStatusEnabled && Boolean(customStatus) && !user.isBot && !isCustomStatusExpired(user);
|
||||
const showUserProfileOptions = (!isDirectMessage || !channelContext) && !override && !manageMode;
|
||||
const showUserProfileOptions = shouldShowUserProfileOptions({
|
||||
channelContext,
|
||||
isDirectMessageWithUser,
|
||||
manageMode,
|
||||
override,
|
||||
});
|
||||
const showNickname = Boolean(user.nickname) && !override && !user.isBot && !manageMode;
|
||||
const showPosition = Boolean(user.position) && !override && !user.isBot && !manageMode;
|
||||
const showLocalTime = Boolean(localTime) && !override && !user.isBot && !manageMode;
|
||||
|
|
@ -175,7 +196,7 @@ const UserProfile = ({
|
|||
if (currentUserId !== user.id) {
|
||||
fetchTeamAndChannelMembership(serverUrl, user.id, teamId, channelId);
|
||||
}
|
||||
}, []);
|
||||
}, [channelId, currentUserId, serverUrl, teamId, user.id]);
|
||||
|
||||
const renderContent = () => {
|
||||
return (
|
||||
|
|
|
|||
Loading…
Reference in a new issue