From f4656043141b8b8dac4275b82b3de134aa89a5c9 Mon Sep 17 00:00:00 2001 From: reneshen0328 Date: Wed, 15 Oct 2025 11:31:05 -0700 Subject: [PATCH 1/5] feat(content-sharing): Create contact service --- .../content-sharing/ContentSharingV2.tsx | 4 +- src/elements/content-sharing/hooks/index.ts | 2 + .../hooks/useContactService.ts | 86 +++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/elements/content-sharing/hooks/index.ts create mode 100644 src/elements/content-sharing/hooks/useContactService.ts diff --git a/src/elements/content-sharing/ContentSharingV2.tsx b/src/elements/content-sharing/ContentSharingV2.tsx index a607399de9..1acf69a643 100644 --- a/src/elements/content-sharing/ContentSharingV2.tsx +++ b/src/elements/content-sharing/ContentSharingV2.tsx @@ -9,7 +9,7 @@ import Internationalize from '../common/Internationalize'; import Providers from '../common/Providers'; import { withBlueprintModernization } from '../common/withBlueprintModernization'; import { fetchAvatars, fetchCollaborators, fetchCurrentUser, fetchItem } from './apis'; -import { useSharingService } from './hooks/useSharingService'; +import { useContactService, useSharingService } from './hooks'; import { convertCollabsResponse, convertItemResponse } from './utils'; import type { Collaborations, ItemType, StringMap } from '../../common/types/core'; @@ -61,6 +61,7 @@ function ContentSharingV2({ setItem, setSharedLink, }); + const { contactService } = useContactService(api, itemID, currentUser?.id); // Handle successful GET requests to /files or /folders const handleGetItemSuccess = React.useCallback(itemData => { @@ -165,6 +166,7 @@ function ContentSharingV2({ config={config} collaborationRoles={collaborationRoles} collaborators={collaborators} + contactService={contactService} currentUser={currentUser} item={item} sharedLink={sharedLink} diff --git a/src/elements/content-sharing/hooks/index.ts b/src/elements/content-sharing/hooks/index.ts new file mode 100644 index 0000000000..6b4ba31338 --- /dev/null +++ b/src/elements/content-sharing/hooks/index.ts @@ -0,0 +1,2 @@ +export { useContactService } from './useContactService'; +export { useSharingService } from './useSharingService'; diff --git a/src/elements/content-sharing/hooks/useContactService.ts b/src/elements/content-sharing/hooks/useContactService.ts new file mode 100644 index 0000000000..f0cd6e540e --- /dev/null +++ b/src/elements/content-sharing/hooks/useContactService.ts @@ -0,0 +1,86 @@ +import * as React from 'react'; + +import { STATUS_INACTIVE } from '../../../constants'; +import useContacts from './useContacts'; +import useContactsByEmail from './useContactsByEmail'; +import { convertUserContactsByEmailResponse } from '../../../features/unified-share-modal/utils/convertData'; + +const APP_USERS_DOMAIN_REGEXP = /boxdevedition.com/; +const sortByName = ({ name: nameA = '' }, { name: nameB = '' }) => nameA.localeCompare(nameB); + +/** + * Convert an enterprise users API response into an array of internal USM contacts. + */ +export const convertUserContactsResponse = (contactsAPIData, currentUserID) => { + const { entries = [] } = contactsAPIData; + + // Return all active users except for the current user and app users + return entries + .filter( + ({ id, login: email, status }) => + id !== currentUserID && + email && + !APP_USERS_DOMAIN_REGEXP.test(email) && + status && + status !== STATUS_INACTIVE, + ) + .map(contact => { + const { id, login: email, name, type } = contact; + return { + id, + email, + name, + type, + value: email, + }; + }) + .sort(sortByName); +}; + +/** + * Convert an enterprise groups API response into an array of internal USM contacts. + */ +export const convertGroupContactsResponse = contactsAPIData => { + const { entries = [] } = contactsAPIData; + + // Only return groups with the correct permissions + return entries + .filter(({ permissions }) => { + return permissions && permissions.can_invite_as_collaborator; + }) + .map(contact => { + const { id, name, type } = contact; + return { + id, + email: 'Group', // Need this for the avatar to work for isUserContactType + name, + type, + value: 'Group', + }; + }) + .sort(sortByName); +}; + +export const useContactService = (api, itemID, currentUserID) => { + const getContacts = useContacts(api, itemID, { + transformUsers: data => convertUserContactsResponse(data, currentUserID), + transformGroups: data => convertGroupContactsResponse(data), + }); + + const getContactsByEmail = useContactsByEmail(api, itemID, { + transformUsers: data => convertUserContactsByEmailResponse(data), + }); + + const contactService = React.useMemo(() => { + if (!currentUserID) { + return null; + } + + return { + getContacts, + getContactsByEmail, + }; + }, [currentUserID, getContacts, getContactsByEmail]); + + return { contactService }; +}; From bdaead7a7df89a493cc7d300d6a887ce3201bde6 Mon Sep 17 00:00:00 2001 From: reneshen0328 Date: Thu, 16 Oct 2025 10:42:34 -0700 Subject: [PATCH 2/5] feat: add tests --- .../hooks/__tests__/useContactService.test.ts | 71 ++++ .../hooks/useContactService.ts | 77 +--- .../convertContactServiceData.test.ts | 332 ++++++++++++++++++ .../utils/convertContactServiceData.ts | 57 +++ src/elements/content-sharing/utils/index.ts | 3 +- 5 files changed, 468 insertions(+), 72 deletions(-) create mode 100644 src/elements/content-sharing/hooks/__tests__/useContactService.test.ts create mode 100644 src/elements/content-sharing/utils/__tests__/convertContactServiceData.test.ts create mode 100644 src/elements/content-sharing/utils/convertContactServiceData.ts diff --git a/src/elements/content-sharing/hooks/__tests__/useContactService.test.ts b/src/elements/content-sharing/hooks/__tests__/useContactService.test.ts new file mode 100644 index 0000000000..c397c84771 --- /dev/null +++ b/src/elements/content-sharing/hooks/__tests__/useContactService.test.ts @@ -0,0 +1,71 @@ +import { renderHook } from '@testing-library/react'; + +import { convertGroupContactsResponse, convertUserContactsResponse } from '../../utils'; +import { useContactService } from '../useContactService'; +import useContacts from '../useContacts'; + +jest.mock('../useContacts'); +jest.mock('../../utils'); + +describe('elements/content-sharing/hooks/useContactService', () => { + const mockApi = { + getMarkerBasedUsersAPI: jest.fn(), + getMarkerBasedGroupsAPI: jest.fn(), + }; + const mockItemID = '123456789'; + const mockCurrentUserID = '123'; + const mockGetContacts = jest.fn(); + + beforeEach(() => { + (useContacts as jest.Mock).mockReturnValue(mockGetContacts); + (convertGroupContactsResponse as jest.Mock).mockReturnValue([]); + (convertUserContactsResponse as jest.Mock).mockReturnValue([]); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('should return null contactService when currentUserID is null or undefined', () => { + [null, undefined].forEach(currentUserID => { + const { result } = renderHook(() => useContactService(mockApi, mockItemID, currentUserID)); + + expect(result.current.contactService).toBeNull(); + }); + }); + + test('should return contactService with getContacts function', () => { + const { result } = renderHook(() => useContactService(mockApi, mockItemID, mockCurrentUserID)); + + expect(useContacts).toHaveBeenCalledWith(mockApi, mockItemID, { + transformUsers: expect.any(Function), + transformGroups: expect.any(Function), + }); + expect(result.current.contactService).toEqual({ + getContacts: mockGetContacts, + }); + }); + + test('should pass transform functions that call correct conversion functions with params', () => { + const mockTransformedUsers = [{ id: 'user1', email: 'user1@test.com' }]; + const mockTransformedGroups = [{ id: 'group1', name: 'Test Group' }]; + const mockUserData = { entries: mockTransformedUsers }; + const mockGroupData = { entries: mockTransformedGroups }; + + (convertUserContactsResponse as jest.Mock).mockReturnValue(mockTransformedUsers); + (convertGroupContactsResponse as jest.Mock).mockReturnValue(mockTransformedGroups); + + renderHook(() => useContactService(mockApi, mockItemID, mockCurrentUserID)); + + // Get the transform functions that were passed to useContacts + const transformUsersFn = useContacts.mock.calls[0][2].transformUsers; + const transformGroupsFn = useContacts.mock.calls[0][2].transformGroups; + const resultUsers = transformUsersFn(mockUserData); + const resultGroups = transformGroupsFn(mockGroupData); + + expect(convertUserContactsResponse as jest.Mock).toHaveBeenCalledWith(mockUserData, mockCurrentUserID); + expect(convertGroupContactsResponse as jest.Mock).toHaveBeenCalledWith(mockGroupData); + expect(resultUsers).toBe(mockTransformedUsers); + expect(resultGroups).toBe(mockTransformedGroups); + }); +}); diff --git a/src/elements/content-sharing/hooks/useContactService.ts b/src/elements/content-sharing/hooks/useContactService.ts index f0cd6e540e..55350f2f08 100644 --- a/src/elements/content-sharing/hooks/useContactService.ts +++ b/src/elements/content-sharing/hooks/useContactService.ts @@ -1,86 +1,21 @@ import * as React from 'react'; -import { STATUS_INACTIVE } from '../../../constants'; +import { convertGroupContactsResponse, convertUserContactsResponse } from '../utils'; import useContacts from './useContacts'; -import useContactsByEmail from './useContactsByEmail'; -import { convertUserContactsByEmailResponse } from '../../../features/unified-share-modal/utils/convertData'; -const APP_USERS_DOMAIN_REGEXP = /boxdevedition.com/; -const sortByName = ({ name: nameA = '' }, { name: nameB = '' }) => nameA.localeCompare(nameB); - -/** - * Convert an enterprise users API response into an array of internal USM contacts. - */ -export const convertUserContactsResponse = (contactsAPIData, currentUserID) => { - const { entries = [] } = contactsAPIData; - - // Return all active users except for the current user and app users - return entries - .filter( - ({ id, login: email, status }) => - id !== currentUserID && - email && - !APP_USERS_DOMAIN_REGEXP.test(email) && - status && - status !== STATUS_INACTIVE, - ) - .map(contact => { - const { id, login: email, name, type } = contact; - return { - id, - email, - name, - type, - value: email, - }; - }) - .sort(sortByName); -}; - -/** - * Convert an enterprise groups API response into an array of internal USM contacts. - */ -export const convertGroupContactsResponse = contactsAPIData => { - const { entries = [] } = contactsAPIData; - - // Only return groups with the correct permissions - return entries - .filter(({ permissions }) => { - return permissions && permissions.can_invite_as_collaborator; - }) - .map(contact => { - const { id, name, type } = contact; - return { - id, - email: 'Group', // Need this for the avatar to work for isUserContactType - name, - type, - value: 'Group', - }; - }) - .sort(sortByName); -}; - -export const useContactService = (api, itemID, currentUserID) => { +export const useContactService = (api, itemID, currentUserId) => { const getContacts = useContacts(api, itemID, { - transformUsers: data => convertUserContactsResponse(data, currentUserID), + transformUsers: data => convertUserContactsResponse(data, currentUserId), transformGroups: data => convertGroupContactsResponse(data), }); - const getContactsByEmail = useContactsByEmail(api, itemID, { - transformUsers: data => convertUserContactsByEmailResponse(data), - }); - const contactService = React.useMemo(() => { - if (!currentUserID) { + if (!currentUserId) { return null; } - return { - getContacts, - getContactsByEmail, - }; - }, [currentUserID, getContacts, getContactsByEmail]); + return { getContacts }; + }, [currentUserId, getContacts]); return { contactService }; }; diff --git a/src/elements/content-sharing/utils/__tests__/convertContactServiceData.test.ts b/src/elements/content-sharing/utils/__tests__/convertContactServiceData.test.ts new file mode 100644 index 0000000000..ba534f57fa --- /dev/null +++ b/src/elements/content-sharing/utils/__tests__/convertContactServiceData.test.ts @@ -0,0 +1,332 @@ +import { STATUS_INACTIVE } from '../../../../constants'; +import { convertUserContactsResponse, convertGroupContactsResponse } from '../convertContactServiceData'; + +const mockCurrentUserId = '123'; + +describe('elements/content-sharing/utils/convertContactServiceData', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('convertUserContactsResponse', () => { + describe('basic conversion', () => { + test('should return empty array when entries is empty', () => { + const contactsApiData = { entries: [] }; + const result = convertUserContactsResponse(contactsApiData, mockCurrentUserId); + expect(result).toEqual([]); + }); + + test('should convert valid user contacts correctly', () => { + const contactsApiData = { + entries: [ + { + id: 'user-1', + login: 'jane.smith@example.com', + name: 'Jane Smith', + type: 'user', + status: 'active', + }, + { + id: 'user-2', + login: 'john.doe@example.com', + name: 'John Doe', + type: 'user', + status: 'active', + }, + ], + }; + + const result = convertUserContactsResponse(contactsApiData, mockCurrentUserId); + + expect(result).toEqual([ + { + id: 'user-1', + email: 'jane.smith@example.com', + name: 'Jane Smith', + type: 'user', + value: 'jane.smith@example.com', + }, + { + id: 'user-2', + email: 'john.doe@example.com', + name: 'John Doe', + type: 'user', + value: 'john.doe@example.com', + }, + ]); + }); + }); + + describe('filtering logic', () => { + test('should filter out current user', () => { + const contactsApiData = { + entries: [ + { + id: mockCurrentUserId, + login: 'current.user@example.com', + name: 'Current User', + type: 'user', + status: 'active', + }, + { + id: 'user-1', + login: 'other.user@example.com', + name: 'Other User', + type: 'user', + status: 'active', + }, + ], + }; + + const result = convertUserContactsResponse(contactsApiData, mockCurrentUserId); + + expect(result).toHaveLength(1); + expect(result[0].id).toBe('user-1'); + }); + + test('should filter out app users (boxdevedition.com domain)', () => { + const contactsApiData = { + entries: [ + { + id: 'app-user-1', + login: 'app@boxdevedition.com', + name: 'App User', + type: 'user', + status: 'active', + }, + { + id: 'user-1', + login: 'regular.user@example.com', + name: 'Regular User', + type: 'user', + status: 'active', + }, + ], + }; + + const result = convertUserContactsResponse(contactsApiData, mockCurrentUserId); + + expect(result).toHaveLength(1); + expect(result[0].id).toBe('user-1'); + }); + + test.each([null, STATUS_INACTIVE])('should filter out users with invalid status', status => { + const contactsApiData = { + entries: [ + { + id: 'inactive-user', + login: 'inactive@example.com', + name: 'Inactive User', + type: 'user', + status, + }, + { + id: 'active-user', + login: 'active@example.com', + name: 'Active User', + type: 'user', + status: 'active', + }, + ], + }; + + const result = convertUserContactsResponse(contactsApiData, mockCurrentUserId); + + expect(result).toHaveLength(1); + expect(result[0].id).toBe('active-user'); + }); + + test('should filter out users without email', () => { + const contactsApiData = { + entries: [ + { + id: 'no-email-user', + login: null, + name: 'No Email User', + type: 'user', + status: 'active', + }, + { + id: 'user-with-email', + login: 'with.email@example.com', + name: 'User With Email', + type: 'user', + status: 'active', + }, + ], + }; + + const result = convertUserContactsResponse(contactsApiData, mockCurrentUserId); + + expect(result).toHaveLength(1); + expect(result[0].id).toBe('user-with-email'); + }); + }); + + test('should sort contacts by name alphabetically', () => { + const contactsApiData = { + entries: [ + { + id: 'user-3', + login: 'charlie@example.com', + name: 'Charlie Brown', + type: 'user', + status: 'active', + }, + { + id: 'user-1', + login: 'alice@example.com', + name: 'Alice Wonder', + type: 'user', + status: 'active', + }, + { + id: 'user-2', + login: 'bob@example.com', + name: 'Bob Builder', + type: 'user', + status: 'active', + }, + ], + }; + + const result = convertUserContactsResponse(contactsApiData, mockCurrentUserId); + + expect(result).toHaveLength(3); + expect(result[0].name).toBe('Alice Wonder'); + expect(result[1].name).toBe('Bob Builder'); + expect(result[2].name).toBe('Charlie Brown'); + }); + }); + + describe('convertGroupContactsResponse', () => { + describe('basic conversion', () => { + test('should return empty array when entries is empty', () => { + const contactsApiData = { entries: [] }; + const result = convertGroupContactsResponse(contactsApiData); + expect(result).toEqual([]); + }); + + test('should convert valid group contacts correctly', () => { + const contactsApiData = { + entries: [ + { + id: 'group-1', + name: 'Engineering Team', + type: 'group', + permissions: { + can_invite_as_collaborator: true, + }, + }, + { + id: 'group-2', + name: 'Marketing Team', + type: 'group', + permissions: { + can_invite_as_collaborator: true, + }, + }, + ], + }; + + const result = convertGroupContactsResponse(contactsApiData); + + expect(result).toEqual([ + { + id: 'group-1', + email: 'Group', + name: 'Engineering Team', + type: 'group', + value: 'Group', + }, + { + id: 'group-2', + email: 'Group', + name: 'Marketing Team', + type: 'group', + value: 'Group', + }, + ]); + }); + }); + + describe('filtering logic', () => { + test('should filter out groups without can_invite_as_collaborator permission', () => { + const contactsApiData = { + entries: [ + { + id: 'group-1', + name: 'Marketing Team', + type: 'group', + permissions: { + can_invite_as_collaborator: false, + }, + }, + { + id: 'group-2', + name: 'Marketing Team', + type: 'group', + }, + { + id: 'group-3', + name: 'Marketing Team', + type: 'group', + permissions: {}, + }, + { + id: 'group-4', + name: 'Engineering Team', + type: 'group', + permissions: { + can_invite_as_collaborator: true, + }, + }, + ], + }; + + const result = convertGroupContactsResponse(contactsApiData); + + expect(result).toHaveLength(1); + expect(result[0].id).toBe('group-4'); + }); + }); + + test('should sort groups by name alphabetically', () => { + const contactsApiData = { + entries: [ + { + id: 'group-3', + name: 'Charlie Group', + type: 'group', + permissions: { + can_invite_as_collaborator: true, + }, + }, + { + id: 'group-1', + name: 'Alice Group', + type: 'group', + permissions: { + can_invite_as_collaborator: true, + }, + }, + { + id: 'group-2', + name: 'Bob Group', + type: 'group', + permissions: { + can_invite_as_collaborator: true, + }, + }, + ], + }; + + const result = convertGroupContactsResponse(contactsApiData); + + expect(result).toHaveLength(3); + expect(result[0].name).toBe('Alice Group'); + expect(result[1].name).toBe('Bob Group'); + expect(result[2].name).toBe('Charlie Group'); + }); + }); +}); diff --git a/src/elements/content-sharing/utils/convertContactServiceData.ts b/src/elements/content-sharing/utils/convertContactServiceData.ts new file mode 100644 index 0000000000..45d92e09ae --- /dev/null +++ b/src/elements/content-sharing/utils/convertContactServiceData.ts @@ -0,0 +1,57 @@ +import { STATUS_INACTIVE } from '../../../constants'; + +const APP_USERS_DOMAIN_REGEXP = /boxdevedition.com/; +const sortByName = ({ name: nameA = '' }, { name: nameB = '' }) => nameA.localeCompare(nameB); + +/** + * Convert an enterprise users API response into an array of internal USM contacts. + */ +export const convertUserContactsResponse = (contactsApiData, currentUserId) => { + const { entries = [] } = contactsApiData; + + // Return all active users except for the current user and app users + return entries + .filter( + ({ id, login: email, status }) => + id !== currentUserId && + email && + !APP_USERS_DOMAIN_REGEXP.test(email) && + status && + status !== STATUS_INACTIVE, + ) + .map(contact => { + const { id, login: email, name, type } = contact; + return { + id, + email, + name, + type, + value: email, + }; + }) + .sort(sortByName); +}; + +/** + * Convert an enterprise groups API response into an array of internal USM contacts. + */ +export const convertGroupContactsResponse = contactsApiData => { + const { entries = [] } = contactsApiData; + + // Only return groups with the correct permissions + return entries + .filter(({ permissions }) => { + return permissions && permissions.can_invite_as_collaborator; + }) + .map(contact => { + const { id, name, type } = contact; + return { + id, + email: 'Group', // Need this for the avatar to work for isUserContactType + name, + type, + value: 'Group', + }; + }) + .sort(sortByName); +}; diff --git a/src/elements/content-sharing/utils/index.ts b/src/elements/content-sharing/utils/index.ts index 287b01c3e8..a7de99c363 100644 --- a/src/elements/content-sharing/utils/index.ts +++ b/src/elements/content-sharing/utils/index.ts @@ -1,5 +1,6 @@ +export { convertCollabsResponse } from './convertCollaborators'; export { convertItemResponse } from './convertItemResponse'; +export { convertGroupContactsResponse, convertUserContactsResponse } from './convertContactServiceData'; export { convertSharedLinkPermissions, convertSharedLinkSettings } from './convertSharingServiceData'; -export { convertCollabsResponse } from './convertCollaborators'; export { getAllowedAccessLevels } from './getAllowedAccessLevels'; export { getAllowedPermissionLevels } from './getAllowedPermissionLevels'; From b517321abf74030f367d7aba263b6833642d0cf0 Mon Sep 17 00:00:00 2001 From: reneshen0328 Date: Thu, 16 Oct 2025 10:49:55 -0700 Subject: [PATCH 3/5] fix: nits --- .../hooks/__tests__/useContactService.test.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/elements/content-sharing/hooks/__tests__/useContactService.test.ts b/src/elements/content-sharing/hooks/__tests__/useContactService.test.ts index c397c84771..ba88174b4c 100644 --- a/src/elements/content-sharing/hooks/__tests__/useContactService.test.ts +++ b/src/elements/content-sharing/hooks/__tests__/useContactService.test.ts @@ -7,15 +7,15 @@ import useContacts from '../useContacts'; jest.mock('../useContacts'); jest.mock('../../utils'); -describe('elements/content-sharing/hooks/useContactService', () => { - const mockApi = { - getMarkerBasedUsersAPI: jest.fn(), - getMarkerBasedGroupsAPI: jest.fn(), - }; - const mockItemID = '123456789'; - const mockCurrentUserID = '123'; - const mockGetContacts = jest.fn(); +const mockApi = { + getMarkerBasedUsersAPI: jest.fn(), + getMarkerBasedGroupsAPI: jest.fn(), +}; +const mockItemID = '123456789'; +const mockCurrentUserID = '123'; +const mockGetContacts = jest.fn(); +describe('elements/content-sharing/hooks/useContactService', () => { beforeEach(() => { (useContacts as jest.Mock).mockReturnValue(mockGetContacts); (convertGroupContactsResponse as jest.Mock).mockReturnValue([]); @@ -29,7 +29,6 @@ describe('elements/content-sharing/hooks/useContactService', () => { test('should return null contactService when currentUserID is null or undefined', () => { [null, undefined].forEach(currentUserID => { const { result } = renderHook(() => useContactService(mockApi, mockItemID, currentUserID)); - expect(result.current.contactService).toBeNull(); }); }); From 97d1af66e5cbb3fa746f74ce8b7c79f415656f1d Mon Sep 17 00:00:00 2001 From: reneshen0328 Date: Thu, 16 Oct 2025 13:34:46 -0700 Subject: [PATCH 4/5] fix: pass correct current user id --- .../__tests__/useContacts.test.js | 1 + .../hooks/__tests__/useContactService.test.ts | 9 ++----- .../hooks/useContactService.ts | 18 ++++--------- .../content-sharing/hooks/useContacts.js | 25 ++++++++++++++++--- src/elements/content-sharing/types.js | 1 + 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/elements/content-sharing/__tests__/useContacts.test.js b/src/elements/content-sharing/__tests__/useContacts.test.js index 2e88f268e4..6a5790dde4 100644 --- a/src/elements/content-sharing/__tests__/useContacts.test.js +++ b/src/elements/content-sharing/__tests__/useContacts.test.js @@ -34,6 +34,7 @@ function FakeComponent({ const [getContacts, setGetContacts] = React.useState(null); const updatedGetContactsFn = useContacts(api, MOCK_ITEM_ID, { + currentUserId: '123', handleSuccess, handleError, transformGroups, diff --git a/src/elements/content-sharing/hooks/__tests__/useContactService.test.ts b/src/elements/content-sharing/hooks/__tests__/useContactService.test.ts index ba88174b4c..9ce3382d4c 100644 --- a/src/elements/content-sharing/hooks/__tests__/useContactService.test.ts +++ b/src/elements/content-sharing/hooks/__tests__/useContactService.test.ts @@ -26,17 +26,12 @@ describe('elements/content-sharing/hooks/useContactService', () => { jest.clearAllMocks(); }); - test('should return null contactService when currentUserID is null or undefined', () => { - [null, undefined].forEach(currentUserID => { - const { result } = renderHook(() => useContactService(mockApi, mockItemID, currentUserID)); - expect(result.current.contactService).toBeNull(); - }); - }); - test('should return contactService with getContacts function', () => { const { result } = renderHook(() => useContactService(mockApi, mockItemID, mockCurrentUserID)); expect(useContacts).toHaveBeenCalledWith(mockApi, mockItemID, { + currentUserId: mockCurrentUserID, + isContentSharingV2Enabled: true, transformUsers: expect.any(Function), transformGroups: expect.any(Function), }); diff --git a/src/elements/content-sharing/hooks/useContactService.ts b/src/elements/content-sharing/hooks/useContactService.ts index 55350f2f08..589c0547d8 100644 --- a/src/elements/content-sharing/hooks/useContactService.ts +++ b/src/elements/content-sharing/hooks/useContactService.ts @@ -1,21 +1,13 @@ -import * as React from 'react'; - import { convertGroupContactsResponse, convertUserContactsResponse } from '../utils'; import useContacts from './useContacts'; -export const useContactService = (api, itemID, currentUserId) => { - const getContacts = useContacts(api, itemID, { +export const useContactService = (api, itemId, currentUserId) => { + const getContacts = useContacts(api, itemId, { + currentUserId, + isContentSharingV2Enabled: true, transformUsers: data => convertUserContactsResponse(data, currentUserId), transformGroups: data => convertGroupContactsResponse(data), }); - const contactService = React.useMemo(() => { - if (!currentUserId) { - return null; - } - - return { getContacts }; - }, [currentUserId, getContacts]); - - return { contactService }; + return { contactService: { getContacts } }; }; diff --git a/src/elements/content-sharing/hooks/useContacts.js b/src/elements/content-sharing/hooks/useContacts.js index 24c016b02f..b6436f88b4 100644 --- a/src/elements/content-sharing/hooks/useContacts.js +++ b/src/elements/content-sharing/hooks/useContacts.js @@ -17,10 +17,19 @@ import type { ContentSharingHooksOptions, GetContactsFnType } from '../types'; */ function useContacts(api: API, itemID: string, options: ContentSharingHooksOptions): GetContactsFnType | null { const [getContacts, setGetContacts] = React.useState(null); - const { handleSuccess = noop, handleError = noop, transformGroups, transformUsers } = options; + const { + currentUserId, + handleSuccess = noop, + handleError = noop, + isContentSharingV2Enabled, + transformGroups, + transformUsers, + } = options; React.useEffect(() => { - if (getContacts) return; + if (getContacts || (isContentSharingV2Enabled && !currentUserId)) { + return; + } const resolveAPICall = ( resolve: (result: Array) => void, @@ -60,7 +69,17 @@ function useContacts(api: API, itemID: string, options: ContentSharingHooksOptio return Promise.all([getUsers, getGroups]).then(contactArrays => [...contactArrays[0], ...contactArrays[1]]); }; setGetContacts(updatedGetContactsFn); - }, [api, getContacts, handleError, handleSuccess, itemID, transformGroups, transformUsers]); + }, [ + api, + currentUserId, + getContacts, + handleError, + handleSuccess, + isContentSharingV2Enabled, + itemID, + transformGroups, + transformUsers, + ]); return getContacts; } diff --git a/src/elements/content-sharing/types.js b/src/elements/content-sharing/types.js index 3ccf79861c..ecd04cde77 100644 --- a/src/elements/content-sharing/types.js +++ b/src/elements/content-sharing/types.js @@ -92,6 +92,7 @@ export type ContentSharingItemAPIResponse = { }; export type ContentSharingHooksOptions = { + currentUserId?: string, handleError?: Function, handleRemoveSharedLinkError?: Function, handleRemoveSharedLinkSuccess?: Function, From 15982b5748508f923a61b16ec19b8ef50e70bf08 Mon Sep 17 00:00:00 2001 From: reneshen0328 Date: Thu, 16 Oct 2025 13:50:34 -0700 Subject: [PATCH 5/5] fix: flow --- src/elements/content-sharing/types.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/elements/content-sharing/types.js b/src/elements/content-sharing/types.js index ecd04cde77..4a375dbc1d 100644 --- a/src/elements/content-sharing/types.js +++ b/src/elements/content-sharing/types.js @@ -99,6 +99,7 @@ export type ContentSharingHooksOptions = { handleSuccess?: Function, handleUpdateSharedLinkError?: Function, handleUpdateSharedLinkSuccess?: Function, + isContentSharingV2Enabled?: boolean, setIsLoading?: Function, transformAccess?: Function, transformGroups?: Function,