Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ export const FIELD_UPLOADER_DISPLAY_NAME: 'uploader_display_name' = 'uploader_di
export const FIELD_CLASSIFICATION: 'classification' = 'classification';
export const FIELD_ENTERPRISE: 'enterprise' = 'enterprise';
export const FIELD_HOSTNAME: 'hostname' = 'hostname';
export const FIELD_LOGIN: 'login' = 'login';
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/* ----------------------- Item-Prefixed Fields for MD Query API --------------------------- */
const ITEM_PREFIX = 'item.';
Expand Down
14 changes: 9 additions & 5 deletions src/elements/content-sharing/ContentSharingV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import type { AvatarURLMap } from './types';

import messages from './messages';

interface ContentSharingUser extends User {
email?: string;
}
Comment thread
reneshen0328 marked this conversation as resolved.
Outdated

export interface ContentSharingV2Props {
/** api - API instance */
api: API;
Expand Down Expand Up @@ -68,7 +72,7 @@ function ContentSharingV2({
const [hasError, setHasError] = React.useState<boolean>(false);
const [sharedLink, setSharedLink] = React.useState<SharedLink | null>(null);
const [sharingServiceProps, setSharingServiceProps] = React.useState(null);
const [currentUser, setCurrentUser] = React.useState<User | null>(null);
const [currentUser, setCurrentUser] = React.useState<ContentSharingUser | null>(null);
const [collaborationRoles, setCollaborationRoles] = React.useState<CollaborationRole[] | null>(null);
const [collaborators, setCollaborators] = React.useState<Collaborator[] | null>(null);
const [collaboratorsData, setCollaboratorsData] = React.useState<Collaborations | null>(null);
Expand All @@ -87,7 +91,7 @@ function ContentSharingV2({
api,
avatarUrlMap,
collaborators,
currentUserId: currentUser?.id,
currentUser,
item,
itemId,
itemType,
Expand Down Expand Up @@ -187,8 +191,8 @@ function ContentSharingV2({
return;
}

const { enterprise, hostname, id } = userData;
setCurrentUser({ id });
const { enterprise, hostname, id, login } = userData;
setCurrentUser({ id, email: login });
setSharingServiceProps(prevSharingServiceProps => ({
...prevSharingServiceProps,
serverUrl: hostname ? `${hostname}v/` : '',
Expand Down Expand Up @@ -266,7 +270,7 @@ function ContentSharingV2({
if (avatarUrlMap && collaboratorsData && currentUser && owner) {
const collaboratorsWithAvatars = convertCollabsResponse(
collaboratorsData,
currentUser.id,
currentUser,
owner,
avatarUrlMap,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DEFAULT_USER_API_RESPONSE, MOCK_ITEM } from '../../utils/__mocks__/ContentSharingV2Mocks';
import { FIELD_ENTERPRISE, FIELD_HOSTNAME } from '../../../../constants';
import { FIELD_ENTERPRISE, FIELD_HOSTNAME, FIELD_LOGIN } from '../../../../constants';
import { fetchCurrentUser } from '..';
import { createSuccessMock, createUsersApiMock } from './testUtils';

Expand All @@ -17,7 +17,7 @@ describe('content-sharing/apis/fetchCurrentUser', () => {
expect(defaultApiMock.getUsersAPI).toHaveBeenCalledWith(false);
expect(getDefaultUserMock).toHaveBeenCalledWith(MOCK_ITEM.id, expect.any(Function), expect.any(Function), {
params: {
fields: [FIELD_ENTERPRISE, FIELD_HOSTNAME].toString(),
fields: [FIELD_ENTERPRISE, FIELD_HOSTNAME, FIELD_LOGIN].toString(),
},
});
expect(result).toEqual(DEFAULT_USER_API_RESPONSE);
Expand Down
4 changes: 2 additions & 2 deletions src/elements/content-sharing/apis/fetchCurrentUser.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { User } from '@box/unified-share-modal';

import { FIELD_ENTERPRISE, FIELD_HOSTNAME } from '../../../constants';
import { FIELD_ENTERPRISE, FIELD_HOSTNAME, FIELD_LOGIN } from '../../../constants';

import type { BaseFetchProps } from '../types';

export const fetchCurrentUser = async ({ api, itemId }: BaseFetchProps): Promise<User | null> => {
return new Promise((resolve, reject) => {
api.getUsersAPI(false).getUser(itemId, resolve, reject, {
params: {
fields: [FIELD_ENTERPRISE, FIELD_HOSTNAME].toString(),
fields: [FIELD_ENTERPRISE, FIELD_HOSTNAME, FIELD_LOGIN].toString(),
},
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const renderHookWithProps = (props = {}) => {
api: mockApi,
avatarUrlMap: {},
collaborators: [],
currentUserId: '123',
currentUser: { id: '123' },
item: mockItem,
itemId: mockItemId,
itemType: TYPE_FILE,
Expand Down Expand Up @@ -274,13 +274,13 @@ describe('elements/content-sharing/hooks/useSharingService', () => {
describe('handleSendInvitations', () => {
const mockCollaborators = [{ id: 'collab-1', email: 'existing@example.com', type: 'user' }];
const mockAvatarUrlMap = { 'user-1': 'https://example.com/avatar.jpg' };
const mockCurrentUserId = 'current-user-123';
const mockCurrentUser = { id: 'current-user-123' };

test('should call useInvites with correct parameters', () => {
renderHookWithProps({
collaborators: mockCollaborators,
avatarUrlMap: mockAvatarUrlMap,
currentUserId: mockCurrentUserId,
currentUser: mockCurrentUser,
});

expect(useInvites).toHaveBeenCalledWith(mockApi, mockItemId, TYPE_FILE, {
Expand All @@ -302,7 +302,7 @@ describe('elements/content-sharing/hooks/useSharingService', () => {
renderHookWithProps({
collaborators: mockCollaborators,
avatarUrlMap: mockAvatarUrlMap,
currentUserId: mockCurrentUserId,
currentUser: mockCurrentUser,
});

// Get the handleSuccess and setCollaborators function that was passed to useInvites
Expand All @@ -313,7 +313,7 @@ describe('elements/content-sharing/hooks/useSharingService', () => {

expect(convertCollab).toHaveBeenCalledWith({
collab: mockResponse,
currentUserId: mockCurrentUserId,
currentUser: mockCurrentUser,
isCurrentUserOwner: false,
ownerEmailDomain: 'test.com',
avatarUrlMap: mockAvatarUrlMap,
Expand Down
8 changes: 4 additions & 4 deletions src/elements/content-sharing/hooks/useSharingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const useSharingService = ({
api,
avatarUrlMap,
collaborators,
currentUserId,
currentUser,
item,
itemId,
itemType,
Expand Down Expand Up @@ -92,15 +92,15 @@ export const useSharingService = ({
const nextCollab = convertCollab({
avatarUrlMap,
collab: response,
currentUserId,
isCurrentUserOwner: currentUserId === ownerId,
currentUser,
isCurrentUserOwner: currentUser?.id === ownerId,
ownerEmailDomain,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});

return nextCollab ? [...prevCollabs, nextCollab] : prevCollabs;
});
},
[avatarUrlMap, currentUserId, setCollaborators],
[avatarUrlMap, currentUser, setCollaborators],
);

const handleSendInvitations = useInvites(api, itemId, itemType, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ const ownerFromApi = {
email: mockOwnerEmail,
name: mockOwnerName,
};
const mockCurrentUser = {
id: mockOwnerId,
email: mockOwnerEmail,
};
const itemOwner = {
id: mockOwnerEmail,
status: STATUS_ACCEPTED,
Expand Down Expand Up @@ -94,7 +98,7 @@ describe('convertCollaborators', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: mockCollaborations[1],
currentUserId: mockOwnerId,
currentUser: mockCurrentUser,
isCurrentUserOwner: false,
ownerEmailDomain,
});
Expand All @@ -118,8 +122,8 @@ describe('convertCollaborators', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: mockCollaborations[5],
currentUserId: mockOwnerId,
isCurrentUserOwner: false,
currentUser: mockCurrentUser,
isCurrentUserOwner: true,
ownerEmailDomain,
});

Expand All @@ -141,7 +145,7 @@ describe('convertCollaborators', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: mockCollaborations[3],
currentUserId: mockOwnerId,
currentUser: mockCurrentUser,
isCurrentUserOwner: false,
ownerEmailDomain,
});
Expand All @@ -165,7 +169,7 @@ describe('convertCollaborators', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab,
currentUserId: mockOwnerId,
currentUser: mockCurrentUser,
isCurrentUserOwner: false,
ownerEmailDomain,
});
Expand All @@ -177,7 +181,7 @@ describe('convertCollaborators', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: mockCollaborations[4],
currentUserId: mockOwnerId,
currentUser: mockCurrentUser,
isCurrentUserOwner: false,
ownerEmailDomain,
});
Expand All @@ -189,7 +193,7 @@ describe('convertCollaborators', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: mockCollaborations[6],
currentUserId: mockOwnerId,
currentUser: mockCurrentUser,
isCurrentUserOwner: false,
ownerEmailDomain,
});
Expand All @@ -201,7 +205,7 @@ describe('convertCollaborators', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: mockCollaborations[0],
currentUserId: mockOwnerId,
currentUser: mockCurrentUser,
isCurrentUserOwner: true,
ownerEmailDomain,
});
Expand All @@ -224,8 +228,8 @@ describe('convertCollaborators', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: mockCollaborations[2],
currentUserId: mockOwnerId,
isCurrentUserOwner: false,
currentUser: mockCurrentUser,
isCurrentUserOwner: true,
ownerEmailDomain,
});

Expand All @@ -238,7 +242,7 @@ describe('convertCollaborators', () => {
const result = convertCollab({
avatarUrlMap,
collab: mockCollaborations[1],
currentUserId: mockOwnerId,
currentUser: mockCurrentUser,
isCurrentUserOwner: false,
ownerEmailDomain,
});
Expand All @@ -257,7 +261,7 @@ describe('convertCollaborators', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: collabWithoutExpiration,
currentUserId: mockOwnerId,
currentUser: mockCurrentUser,
isCurrentUserOwner: false,
ownerEmailDomain,
});
Expand All @@ -270,7 +274,7 @@ describe('convertCollaborators', () => {
test('should convert valid collaborations data to Collaborator array', () => {
const result = convertCollabsResponse(
mockCollaborationsFromApi,
mockOwnerId,
mockCurrentUser,
ownerFromApi,
mockAvatarUrlMap,
);
Expand Down Expand Up @@ -310,7 +314,7 @@ describe('convertCollaborators', () => {
hasCustomAvatar: false,
id: '124',
isCurrentUser: false,
isExternal: false,
isExternal: true,
isPending: false,
name: 'Raccoon Queen',
role: 'viewer',
Expand All @@ -336,7 +340,7 @@ describe('convertCollaborators', () => {
hasCustomAvatar: false,
id: '127',
isCurrentUser: false,
isExternal: false,
isExternal: true,
isPending: true,
name: 'bbear@external.example.com',
role: 'editor',
Expand All @@ -346,13 +350,13 @@ describe('convertCollaborators', () => {

test('should return empty array for empty entries', () => {
const emptyCollaborations: Collaborations = { entries: [] };
const result = convertCollabsResponse(emptyCollaborations, mockOwnerId, ownerFromApi, mockAvatarUrlMap);
const result = convertCollabsResponse(emptyCollaborations, mockCurrentUser, ownerFromApi, mockAvatarUrlMap);

expect(result).toEqual([]);
});

test('should handle null avatar URL map', () => {
const collabs = convertCollabsResponse(mockCollaborationsFromApi, mockOwnerId, ownerFromApi, null);
const collabs = convertCollabsResponse(mockCollaborationsFromApi, mockCurrentUser, ownerFromApi, null);

collabs.map(collab => {
expect(collab.avatarUrl).toBeUndefined();
Expand Down
23 changes: 15 additions & 8 deletions src/elements/content-sharing/utils/convertCollaborators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import {
USM_TO_API_COLLAB_ROLE_MAP,
} from '../constants';

import type { Collaboration, Collaborations } from '../../../common/types/core';
import type { Collaboration, Collaborations, User } from '../../../common/types/core';
import type { AvatarURLMap } from '../types';

export interface ConvertCollabProps {
collab: Collaboration;
currentUserId: string;
currentUser: User;
isCurrentUserOwner: boolean;
ownerEmailDomain: string;
avatarUrlMap?: AvatarURLMap;
Expand All @@ -22,7 +22,7 @@ export interface ConvertCollabProps {
export const convertCollab = ({
avatarUrlMap,
collab,
currentUserId,
currentUser,
isCurrentUserOwner,
ownerEmailDomain,
}: ConvertCollabProps): Collaborator | null => {
Expand All @@ -48,8 +48,15 @@ export const convertCollab = ({
return null;
}

// External collaborator icons will only be displayed in the USM if the current user is the item owner or
// belongs to the same enterprise as the owner, and if the collaborator's email domain differs from the owner's enterprise email domain.
Comment thread
reneshen0328 marked this conversation as resolved.
Outdated
const currentUserEmailDomain =
!!currentUser?.email && /@/.test(currentUser.email) ? currentUser.email.split('@')[1] : null;
Comment thread
reneshen0328 marked this conversation as resolved.
Outdated
const isExternal =
!isCurrentUserOwner && !!collabEmail && !!ownerEmailDomain && collabEmail.split('@')[1] !== ownerEmailDomain;
(isCurrentUserOwner || currentUserEmailDomain === ownerEmailDomain) &&
!!collabEmail &&
!!ownerEmailDomain &&
collabEmail.split('@')[1] !== ownerEmailDomain;
const avatarUrl = avatarUrlMap ? avatarUrlMap[collabId] : undefined;

return {
Expand All @@ -58,7 +65,7 @@ export const convertCollab = ({
expiresAt,
hasCustomAvatar: !!avatarUrl,
id: `${id}`,
isCurrentUser: collabId != null && collabId === currentUserId,
isCurrentUser: collabId != null && collabId === currentUser?.id,
isExternal,
isPending: status === STATUS_PENDING,
name: collabName || collabEmail,
Expand All @@ -69,15 +76,15 @@ export const convertCollab = ({

export const convertCollabsResponse = (
collabsApiData: Collaborations,
currentUserId: string,
currentUser: User,
Comment thread
reneshen0328 marked this conversation as resolved.
Outdated
owner: { id: string; email: string; name: string },
avatarUrlMap?: AvatarURLMap,
): Collaborator[] => {
const { entries = [] } = collabsApiData;
if (!entries.length) return [];

const { id: ownerId, email: ownerEmail, name: ownerName } = owner;
const isCurrentUserOwner = currentUserId === ownerId;
const isCurrentUserOwner = currentUser?.id === ownerId;
Comment thread
reneshen0328 marked this conversation as resolved.
Outdated
const ownerEmailDomain = ownerEmail && /@/.test(ownerEmail) ? ownerEmail.split('@')[1] : null;

const itemOwner = {
Expand All @@ -92,7 +99,7 @@ export const convertCollabsResponse = (
};

return [itemOwner, ...entries].flatMap(collab => {
const converted = convertCollab({ avatarUrlMap, collab, currentUserId, isCurrentUserOwner, ownerEmailDomain });
const converted = convertCollab({ avatarUrlMap, collab, currentUser, isCurrentUserOwner, ownerEmailDomain });
return converted ? [converted] : [];
});
};
Expand Down
Loading