Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
"@box/collaboration-popover": "^2.2.21",
"@box/combobox-with-api": "2.2.25",
"@box/content-field": "^3.1.17",
"@box/copy-input": "^1.42.16",
"@box/copy-input": "^2.3.21",
"@box/frontend": "^11.0.1",
"@box/item-icon": "^3.2.0",
"@box/languages": "^1.0.0",
Expand All @@ -148,9 +148,9 @@
"@box/readable-time": "^2.2.21",
"@box/threaded-annotations": "^4.14.0",
"@box/types": "^2.1.8",
"@box/unified-share-modal": "^3.5.25",
"@box/unified-share-modal": "^3.6.2",
"@box/uploads-manager": "^2.4.0",
"@box/user-selector": "3.0.1",
"@box/user-selector": "^3.1.21",
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
"@chromatic-com/storybook": "^5.2.1",
"@commitlint/cli": "^19.8.0",
Expand Down Expand Up @@ -313,7 +313,7 @@
"@box/collaboration-popover": "^2.2.21",
"@box/combobox-with-api": "2.2.25",
"@box/content-field": "^3.1.17",
"@box/copy-input": "^1.42.16",
"@box/copy-input": "^2.3.21",
"@box/item-icon": "^3.2.0",
"@box/metadata-editor": "^2.11.11",
"@box/metadata-filter": "^1.80.23",
Expand All @@ -325,9 +325,9 @@
"@box/readable-time": "^2.2.21",
"@box/threaded-annotations": "^4.14.0",
"@box/types": "^2.1.8",
"@box/unified-share-modal": "^3.5.25",
"@box/unified-share-modal": "^3.6.2",
"@box/uploads-manager": "^2.4.0",
"@box/user-selector": "3.0.1",
"@box/user-selector": "^3.1.21",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
Expand Down
24 changes: 21 additions & 3 deletions src/common/types/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,12 @@ type FolderMini = {
type: typeof ITEM_TYPE_FOLDER;
};

type UserMini = {
type UserBase = {
id: string;
type: 'user';
};

type UserMini = UserBase & {
avatar_url?: string;
email?: string;
enterprise?: {
Expand All @@ -118,11 +123,9 @@ type UserMini = {
type: 'enterprise';
};
hostname?: string;
id: string;
login?: string;
name: string;
status?: string;
type: 'user';
};

type ContactCollection = {
Expand Down Expand Up @@ -290,13 +293,26 @@ type FileRepresentationResponse = {
entries: Array<FileRepresentation>;
};

type OtpAccessEntry = {
created_at: string;
created_by: UserBase;
type: 'email' | 'domain';
value: string;
};

type OtpAccess = {
allowlist?: Array<OtpAccessEntry>;
mode: 'none' | 'open' | 'allowlist';
};

type SharedLink = {
access: Access;
download_count?: number;
download_url?: string;
effective_access?: Access;
effective_permission?: typeof PERMISSION_CAN_DOWNLOAD | typeof PERMISSION_CAN_PREVIEW;
is_password_enabled?: boolean;
one_time_passcode?: OtpAccess;
password?: string | null; // the API requires a null value to remove a password,
permissions?: BoxItemPermission;
preview_count?: number;
Expand Down Expand Up @@ -506,6 +522,7 @@ export type {
Size,
FileMini,
FolderMini,
UserBase,
UserMini,
User,
UserCollection,
Expand Down Expand Up @@ -538,4 +555,5 @@ export type {
Collaborations,
NewCollaboration,
DOMStringList,
OtpAccess,
};
28 changes: 2 additions & 26 deletions src/elements/content-sharing/ContentSharingV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ function ContentSharingV2({
const [collaborators, setCollaborators] = React.useState<Collaborator[] | null>(null);
const [collaboratorsData, setCollaboratorsData] = React.useState<Collaborations | null>(null);
const [owner, setOwner] = React.useState({ email: '', id: '', name: '' });
const [isEnterpriseUser, setIsEnterpriseUser] = React.useState<boolean | null>(null);

const config = React.useMemo(() => {
return {
Expand Down Expand Up @@ -155,7 +154,6 @@ function ContentSharingV2({
setItem(null);
setSharedLink(null);
setCurrentUser(null);
setIsEnterpriseUser(null);
setCollaborationRoles(null);
setAvatarUrlMap(null);
setCollaborators(null);
Expand Down Expand Up @@ -186,15 +184,14 @@ function ContentSharingV2({
return;
}

const { enterprise, hostname, id, login } = userData;
const { hostname, id, login } = userData;
const emailDomain = login && /@/.test(login) ? login.split('@')[1] : undefined;

setCurrentUser({ id, email: login, emailDomain });
setSharingServiceProps(prevSharingServiceProps => ({
...prevSharingServiceProps,
serverUrl: hostname ? `${hostname}v/` : '',
}));

setIsEnterpriseUser(!!enterprise);
};

(async () => {
Expand All @@ -207,27 +204,6 @@ function ContentSharingV2({
})();
}, [api, currentUser, item, itemId, itemType, sharedLink, getError]);

// Set canChangeExpiration to false for non-enterprise (individual) users on every sharedLink update
React.useEffect(() => {
if (isEnterpriseUser !== false || !sharedLink?.settings?.canChangeExpiration) {
return;
}

setSharedLink(prevSharedLink => {
if (!prevSharedLink) {
return prevSharedLink;
}

return {
...prevSharedLink,
settings: {
...prevSharedLink.settings,
canChangeExpiration: false,
},
};
});
}, [isEnterpriseUser, sharedLink]);
Comment on lines -210 to -229

@tjuanitas tjuanitas Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since canChangeExpiration now comes the backend, the logic for determining free users is already accounted in the calculation. this also fixes the logic for personal pro users


// Get collaborators
React.useEffect(() => {
if (!api || isEmpty(api) || !item || collaboratorsData) return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React from 'react';
import { fireEvent, render, type RenderResult, screen, waitFor } from '@testing-library/react';
import { render, type RenderResult, screen, waitFor } from '@testing-library/react';
import { Notification, TooltipProvider } from '@box/blueprint-web';
import { useSharingService } from '../hooks/useSharingService';
import {
DEFAULT_ITEM_API_RESPONSE,
DEFAULT_USER_API_RESPONSE,
FREE_USER_API_RESPONSE,
MOCK_ITEM,
MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK,
MOCK_ITEM_API_RESPONSE_WITH_CLASSIFICATION,
Expand Down Expand Up @@ -118,9 +117,6 @@ describe('elements/content-sharing/ContentSharingV2', () => {
expect(await screen.findByRole('button', { name: 'Can view and download' })).toBeVisible();
expect(screen.getByRole('button', { name: 'Link Settings' })).toBeVisible();
expect(screen.getByRole('button', { name: 'Copy' })).toBeVisible();
// Should have expiration toggle enabled for Business+ users
fireEvent.click(screen.getByRole('button', { name: 'Link Settings' }));
expect(screen.getByRole('switch', { name: 'Link expiration' })).not.toBeDisabled();
});

test('should see the classification elements if classification is present', async () => {
Expand Down Expand Up @@ -187,21 +183,6 @@ describe('elements/content-sharing/ContentSharingV2', () => {
expect(await screen.findByRole('button', { name: 'Send Shared Link' })).toBeVisible();
});

test('should have disabled expiration toggle for users without enterprise', async () => {
const freeUserApi = {
...defaultApiMock,
getFileAPI: jest.fn().mockReturnValue({ getFile: getFileMockWithSharedLink }),
getUsersAPI: jest.fn().mockReturnValue({
getUser: jest.fn().mockImplementation(createSuccessMock(FREE_USER_API_RESPONSE)),
getAvatarUrlWithAccessToken: getAvatarUrlMock,
}),
};

renderComponent({ api: freeUserApi });
fireEvent.click(await screen.findByRole('button', { name: 'Link Settings' }));
expect(screen.getByRole('switch', { name: 'Link expiration' })).toBeDisabled();
});

describe('getError function', () => {
const createErrorApi = error => ({
...defaultApiMock,
Expand Down
Loading
Loading