From 9a7dce8b42f8cc525cde08b8c3e25f97eebab6d8 Mon Sep 17 00:00:00 2001 From: Eric Doughty-Papassideris Date: Sun, 21 Apr 2024 22:10:36 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20front:=20moved=20drivei?= =?UTF-8?q?tem=20access=20entities=20editing=20to=20a=20helper=20file=20(#?= =?UTF-8?q?50)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/app/features/drive/types.ts | 4 +- .../files/utils/access-info-helpers.ts | 167 ++++++++++++++++++ .../views/client/body/drive/context-menu.tsx | 7 +- .../body/drive/documents/document-row.tsx | 3 +- .../body/drive/documents/folder-row.tsx | 3 +- .../views/client/body/drive/header-path.tsx | 3 +- .../body/drive/modals/update-access/index.tsx | 18 +- .../update-access/inherit-access-options.tsx | 88 +++------ .../modals/update-access/internal-access.tsx | 46 +---- .../update-access/public-link-access.tsx | 17 +- 10 files changed, 225 insertions(+), 131 deletions(-) create mode 100644 tdrive/frontend/src/app/features/files/utils/access-info-helpers.ts diff --git a/tdrive/frontend/src/app/features/drive/types.ts b/tdrive/frontend/src/app/features/drive/types.ts index 4356ea3b4..702284585 100644 --- a/tdrive/frontend/src/app/features/drive/types.ts +++ b/tdrive/frontend/src/app/features/drive/types.ts @@ -38,6 +38,8 @@ export type DriveItem = { scope: string; }; +export type DriveFileAccessLevelForInherited = 'none' | 'manage'; +export type DriveFileAccessLevelForPublicLink = 'none' | 'read' | 'write'; export type DriveFileAccessLevel = 'none' | 'read' | 'write' | 'manage'; export type DriveItemAccessInfo = { @@ -50,7 +52,7 @@ export type DriveItemAccessInfo = { entities: AuthEntity[]; }; -type AuthEntity = { +export type AuthEntity = { type: 'user' | 'channel' | 'company' | 'folder'; id: string | 'parent'; level: DriveFileAccessLevel; diff --git a/tdrive/frontend/src/app/features/files/utils/access-info-helpers.ts b/tdrive/frontend/src/app/features/files/utils/access-info-helpers.ts new file mode 100644 index 000000000..a4e6475eb --- /dev/null +++ b/tdrive/frontend/src/app/features/files/utils/access-info-helpers.ts @@ -0,0 +1,167 @@ +import { + AuthEntity, + DriveItemAccessInfo, + DriveFileAccessLevel, + DriveFileAccessLevelForInherited, + DriveFileAccessLevelForPublicLink, + DriveItem, +} from '@features/drive/types'; + +const entityMatcher = (entityType: AuthEntity['type'], entityId?: string) => + (entity: AuthEntity) => + entity && entity.type === entityType && (entityId == undefined || entity.id === entityId); + +/** Updates the level of entities matching the given type and id, or adds the return + * of updater with no argument. + * + * If an item is not unique, all are modified and a console warning is printed except for `entityType` "channel". + */ +function upsertEntities( + entities: AuthEntity[] | undefined, + entityType: AuthEntity['type'], + entityId: string | undefined, + level: DriveFileAccessLevel, +) { + const updater = (existing?: AuthEntity) => { + if (!existing && !entityId) throw new Error("Cannot create entry without entityId"); + return { + ...(existing || { type: entityType, id: entityId }), + level, + }; + }; + let found = false; + if (!entities || entities.length == 0) return [updater()]; + const matcher = entityMatcher(entityType, entityId); + const mapped = entities.map(item => { + if (!matcher(item)) return item; + if (found && entityType != "channel") console.warn(`DriveItem has more than one access_info entry for '${entityType}' id = ${entityId}:`, item); + found = true; + return updater(item); + }).filter(x => x); + if (found) return mapped; + return [...entities, updater()]; +} + +/** If `level == false`, deletes matching entries, otherwise calls `upsertEntities` + */ +function editEntities( + entities: AuthEntity[] | undefined, + entityType: AuthEntity['type'], + entityId: string | undefined, + level: DriveFileAccessLevel | false, +) { + if (level) return upsertEntities(entities, entityType, entityId, level); + const matcher = entityMatcher(entityType, entityId); + return entities && entities.filter(item => !matcher(item)); +} + +/** Return a list of all DriveItemAccessInfo for entities with the provided type sorted by id */ +const accessInfosOfEntitiesOfType = (item: DriveItem | undefined, entityType: AuthEntity['type']) => + item?.access_info.entities.filter(entityMatcher(entityType)).sort((a, b) => a.id.localeCompare(b.id)) || []; + +/** Find access level for the given entity in the DriveItem's access_info and return it's level if any, or undefined */ +export function accessLevelOfEntityForItem( + item: DriveItem | undefined, + entityType: AuthEntity['type'], + entityId: string, +) { + const matcher = entityMatcher(entityType, entityId); + return item?.access_info.entities.filter(matcher)[0]?.level; +} + +/** Return the item with the access right for the given entity added or changed to level; or if its false, remove if any */ +const itemWithEntityAccessChanged = ( + item: DriveItem, + entityType: AuthEntity['type'], + entityId: string | undefined, + level: DriveFileAccessLevel | false, +) => ({ + ...item, + access_info: { + ...item.access_info, + entities: editEntities(item.access_info.entities, entityType, entityId, level), + }, + } as DriveItem); + +// Note this just assumes uniqueness and just logs in the console if that is not the case and uses the first one. +const getAccessLevelOfUniqueForType = (item: DriveItem | undefined, entityType: AuthEntity['type'], entityId?: string) => { + if (!item) return undefined; + const accesses = item.access_info.entities.filter(entityMatcher(entityType, entityId)); + if (accesses.length != 1 && entityType != "channel") + console.warn(`DriveItem doesn't have exactly one access_info entry for '${entityType}${entityId ? " id: " + entityId : ""}':`, item); + return accesses[0]?.level; +} + +/** Return a shallow copy of item with the access right for the given user added or changed to level; or if its false, removed */ +export const changeUserAccess = (item: DriveItem, userId: string, level: DriveFileAccessLevel | false) => + itemWithEntityAccessChanged(item, "user", userId, level); + +/** Return a list of all DriveItemAccessInfo for users sorted by id */ +export const getAllUserAccesses = (item: DriveItem) => accessInfosOfEntitiesOfType(item, "user"); + +/** Return the access level for the provided user; an entry is expected to exist (or there is a `console.warn`) */ +export const getUserAccessLevel = (item: DriveItem, userId: string) => getAccessLevelOfUniqueForType(item, "user", userId); + + + +/** Return current access level inherited from parent folder by item */ +export const getInheritedAccessLevel = (item?: DriveItem) => getAccessLevelOfUniqueForType(item, "folder"); + +/** Return a shallow copy of item with the access right for the given entity added or changed to level; or if its false, removed */ +export const changeInheritedAccess = (item: DriveItem, level: DriveFileAccessLevelForInherited | false) => + itemWithEntityAccessChanged(item, "folder", "parent", level); + + + +/** Return access level for the company type entity if any */ +export const getCompanyAccessLevel = (item?: DriveItem) => getAccessLevelOfUniqueForType(item, "company"); + +/** Return a shallow copy of item with the access right for the given entity changed to level; or if its false, removed. + * Removing is irreversible. Calling this method setting a level on a `DriveItem` that doesn't have a company entry will + * throw an Error. + */ +export const changeCompanyAccessLevel = (item: DriveItem, level: DriveFileAccessLevel | false) => + itemWithEntityAccessChanged(item, "company", undefined, level); + + + +/** Return the first, if any, access level of a channel */ +export const getFirstChannelAccessLevel = (item?: DriveItem) => getAccessLevelOfUniqueForType(item, "channel"); + +/** Return a shallow copy of item with the access right for the given entity changed to level; or if its false, removed. + * Removing is irreversible. Calling this method setting a level on a `DriveItem` that doesn't have a channel entry will + * throw an Error. + */ +export const changeAllChannelAccessLevels = (item: DriveItem, level: DriveFileAccessLevel | false) => + itemWithEntityAccessChanged(item, "channel", undefined, level); + + + +/** Return a shallow copy of item with the public link properties for the given entity changed + * + * - level defaults to 'none' if not set and this is called without a value in `changes` for it + * - sets all fields in `changes` argument as they are, even if their value is null, undefined, false etc + * - runtime does not validate that fields in `changes` argument are properly typed or correctly named + */ +export function changePublicLink( + item: DriveItem, + changes: { + level?: DriveFileAccessLevelForPublicLink, + expiration?: number, + password?: string, + }, +) { + const publicSettings : DriveItemAccessInfo["public"] = item.access_info.public ? { ...item.access_info.public } : { token: '', level: "none" }; + Object.entries(changes).forEach(([field, value]) => (publicSettings as { [k: string]: unknown })[field] = value); + return { + ...item, + access_info: { + ...item.access_info, + public: publicSettings, + }, + } as DriveItem; +} + +/** Return the access level of the public link if it's set, and it isn't `"none"`, or returns false */ +export const hasAnyPublicLinkAccess = (item?: Partial) : Exclude | false => + (item?.access_info?.public?.level && item.access_info.public.level != "none") ? item.access_info.public.level : false; \ No newline at end of file diff --git a/tdrive/frontend/src/app/views/client/body/drive/context-menu.tsx b/tdrive/frontend/src/app/views/client/body/drive/context-menu.tsx index 130be4367..a4a09bbb7 100644 --- a/tdrive/frontend/src/app/views/client/body/drive/context-menu.tsx +++ b/tdrive/frontend/src/app/views/client/body/drive/context-menu.tsx @@ -25,6 +25,7 @@ import RouterServices from '@features/router/services/router-service'; import useRouterCompany from '@features/router/hooks/use-router-company'; import _ from 'lodash'; import Languages from 'features/global/services/languages-service'; +import { hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers'; /** * This will build the context menu in different contexts @@ -50,7 +51,7 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s const { open: preview } = useDrivePreview(); const { viewId } = useRouteState(); const company = useRouterCompany(); - + function getIdsFromArray(arr: DriveItem[]): string[] { return arr.map((obj) => obj.id); } @@ -309,9 +310,7 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s { type: 'menu', text: Languages.t('components.item_context_menu.copy_link'), - hide: - !parent?.item?.access_info?.public?.level || - parent?.item?.access_info?.public?.level === 'none', + hide: !hasAnyPublicLinkAccess(item), onClick: () => { copyToClipboard(getPublicLink(item || parent?.item)); ToasterService.success( diff --git a/tdrive/frontend/src/app/views/client/body/drive/documents/document-row.tsx b/tdrive/frontend/src/app/views/client/body/drive/documents/document-row.tsx index 30681bdc0..ae4d3e90c 100644 --- a/tdrive/frontend/src/app/views/client/body/drive/documents/document-row.tsx +++ b/tdrive/frontend/src/app/views/client/body/drive/documents/document-row.tsx @@ -13,6 +13,7 @@ import { useHistory } from 'react-router-dom'; import RouterServices from '@features/router/services/router-service'; import useRouteState from 'app/features/router/hooks/use-route-state'; import { DocumentIcon } from './document-icon'; +import { hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers'; export const DocumentRow = ({ item, @@ -79,7 +80,7 @@ export const DocumentRow = ({ {item.name}
- {item?.access_info?.public?.level !== 'none' && ( + {hasAnyPublicLinkAccess(item) && ( )}
diff --git a/tdrive/frontend/src/app/views/client/body/drive/documents/folder-row.tsx b/tdrive/frontend/src/app/views/client/body/drive/documents/folder-row.tsx index 31888eb06..a5609df39 100644 --- a/tdrive/frontend/src/app/views/client/body/drive/documents/folder-row.tsx +++ b/tdrive/frontend/src/app/views/client/body/drive/documents/folder-row.tsx @@ -7,6 +7,7 @@ import { formatBytes } from '@features/drive/utils'; import { useState } from 'react'; import { PublicIcon } from '../components/public-icon'; import { CheckableIcon, DriveItemProps } from './common'; +import { hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers'; import './style.scss'; export const FolderRow = ({ @@ -49,7 +50,7 @@ export const FolderRow = ({ {item.name}
- {item?.access_info?.public?.level !== 'none' && ( + {hasAnyPublicLinkAccess(item) && ( )}
diff --git a/tdrive/frontend/src/app/views/client/body/drive/header-path.tsx b/tdrive/frontend/src/app/views/client/body/drive/header-path.tsx index 1b0d85143..8ea5e1dae 100644 --- a/tdrive/frontend/src/app/views/client/body/drive/header-path.tsx +++ b/tdrive/frontend/src/app/views/client/body/drive/header-path.tsx @@ -10,6 +10,7 @@ import Languages from 'features/global/services/languages-service'; import { useHistory } from 'react-router-dom'; import useRouterCompany from '@features/router/hooks/use-router-company'; import RouterServices from '@features/router/services/router-service'; +import { hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers'; export default ({ path: livePath, @@ -193,7 +194,7 @@ const PathItem = ({ })()} - {item?.access_info?.public?.level && item?.access_info?.public?.level !== 'none' && ( + {hasAnyPublicLinkAccess(item) && ( )} {first && !!user?.id && viewId?.includes('trash') && ( diff --git a/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/index.tsx b/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/index.tsx index d7212a74c..e1c879779 100644 --- a/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/index.tsx +++ b/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/index.tsx @@ -15,6 +15,7 @@ import { ArrowLeftIcon, LockClosedIcon } from '@heroicons/react/outline'; import { PublicLinkAccessOptions } from './public-link-access-options'; import { CuteDepictionOfFolderHierarchy } from './cute-depiction-of-folder-hierarchy'; import { InheritAccessOptions } from './inherit-access-options'; +import { changePublicLink, hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers'; export type AccessModalType = { open: boolean; @@ -94,20 +95,9 @@ const AccessModalContent = (props: { refresh(id); refreshCompany(); }, []); - const havePublicLink = (item?.access_info?.public?.level || 'none') !== 'none'; + const havePublicLink = hasAnyPublicLinkAccess(item); const haveAdvancedSettings = parentItem?.parent_id !== null || havePublicLink; - const updatePublicAccess = (key: string, value: string | number, skipLoading?: true) => - update({ - access_info: { - entities: item?.access_info.entities || [], - public: { - ...item!.access_info!.public!, - [key]: value || '', - }, - }, - }, skipLoading); - return ( { - updatePublicAccess('password', password || '', true); + item && changePublicLink(item, { password: password || '' }); }} onChangeExpiration={(expiration: number) => { - updatePublicAccess('expiration', expiration || 0); + item && changePublicLink(item, { expiration: expiration || 0 }); }} />} { parentItem?.parent_id !== null && <> diff --git a/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/inherit-access-options.tsx b/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/inherit-access-options.tsx index 9152d0cc0..8f489d105 100644 --- a/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/inherit-access-options.tsx +++ b/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/inherit-access-options.tsx @@ -1,6 +1,14 @@ import Languages from 'features/global/services/languages-service'; import type { DriveItem } from 'app/features/drive/types'; +import { + changeAllChannelAccessLevels, + changeCompanyAccessLevel, + changeInheritedAccess, + getCompanyAccessLevel, + getFirstChannelAccessLevel, + getInheritedAccessLevel, +} from '@features/files/utils/access-info-helpers'; import { Base, Info, Subtitle } from '@atoms/text'; import { Checkbox } from '@atoms/input/input-checkbox'; @@ -12,21 +20,17 @@ export const InheritAccessOptions = (props: { disabled: boolean, onUpdate: (item: Partial) => void, }) => { - const folderEntity = props.item?.access_info.entities.filter(a => a.type === 'folder')?.[0] || { - type: 'folder', - id: 'parent', - level: 'manage', - }; - const companyEntity = props.item?.access_info.entities.filter(a => a.type === 'company')?.[0]; - const channelEntities = props.item?.access_info.entities.filter(a => a.type === 'channel') || []; + // TODO: The default to 'manage' surprises me but it's what previous code did, as this commit is a refactoring, aim is to not affect function + const folderEntityLevel = getInheritedAccessLevel(props.item) || 'manage'; + const companyEntityLevel = getCompanyAccessLevel(props.item); + const channelEntitiesLevel = getFirstChannelAccessLevel(props.item); return ( <> - {(companyEntity || folderEntity?.level === 'none' || channelEntities.length > 0) && + {(companyEntityLevel || folderEntityLevel === 'none' || channelEntitiesLevel) && {Languages.t('components.internal-access_access_manage')}} - {folderEntity && ( -
+ {
{Languages.t('components.internal-access_inherit_parent')}
@@ -36,23 +40,15 @@ export const InheritAccessOptions = (props: { { - props.onUpdate({ - access_info: { - entities: [ - ...(props.item?.access_info.entities.filter(a => a.type !== 'folder') || []), - { ...folderEntity, level: status ? 'manage' : 'none' }, - ], - public: props.item?.access_info.public, - }, - }); + props.item && props.onUpdate(changeInheritedAccess(props.item, status ? 'manage' : 'none')); }} - value={folderEntity.level === 'manage'} + value={folderEntityLevel === 'manage'} />
- )} + } - {companyEntity && folderEntity.level === 'none' && ( + { companyEntityLevel && folderEntityLevel === 'none' && (
{Languages.t('components.internal-access_company_member')} @@ -61,29 +57,21 @@ export const InheritAccessOptions = (props: { { - props.onUpdate({ - access_info: { - entities: [ - ...(props.item?.access_info.entities.filter(a => a.type !== 'company') || []), - ...(level !== 'remove' ? [{ ...companyEntity, level }] : []), - ], - public: props.item?.access_info.public, - }, - }); + props.item && props.onUpdate(changeCompanyAccessLevel(props.item, level === 'remove' ? false : level)); }} - level={companyEntity.level} + level={companyEntityLevel} />
)} - {channelEntities.length > 0 && ( + {channelEntitiesLevel && (
{Languages.t('components.internal-access_cannal')}
- {channelEntities.length} {Languages.t('components.internal-access_cannal_info')} + {channelEntitiesLevel.length} {Languages.t('components.internal-access_cannal_info')}
@@ -92,41 +80,21 @@ export const InheritAccessOptions = (props: { hiddenLevels={['none']} canRemove onChange={level => { - if (level === 'remove') { + if (level === 'remove') AlertManager.confirm( async () => { //Remove channel access - props.onUpdate({ - access_info: { - entities: - props.item?.access_info?.entities.filter(e => e.type !== 'channel') || [], - public: props.item?.access_info.public, - }, - }); - }, - () => { - //Do nothing + props.item && props.onUpdate(changeAllChannelAccessLevels(props.item, false)); }, + () => { /* Do nothing */ }, { text: Languages.t('components.internal-access_cannal_info_give_back'), }, ); - } else { - props.onUpdate({ - access_info: { - entities: - props.item?.access_info?.entities.map(e => { - if (e.type === 'channel') { - return { ...e, level }; - } - return e; - }) || [], - public: props.item?.access_info.public, - }, - }); - } + else + props.item && props.onUpdate(changeAllChannelAccessLevels(props.item, level)); }} - level={channelEntities[0].level} + level={channelEntitiesLevel} />
diff --git a/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/internal-access.tsx b/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/internal-access.tsx index e658a323b..084de1801 100644 --- a/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/internal-access.tsx +++ b/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/internal-access.tsx @@ -9,11 +9,11 @@ import { useState } from 'react'; import SelectUsers from '../../components/select-users'; import { AccessLevelDropdown } from './access-level-dropdown'; import Languages from 'features/global/services/languages-service'; +import { changeUserAccess, getUserAccessLevel, getAllUserAccesses } from '@features/files/utils/access-info-edits'; export const InternalAccessManager = ({ id, disabled }: { id: string; disabled: boolean }) => { const { item } = useDriveItem(id); - const userEntities = item?.access_info.entities.filter(a => a.type === 'user') || []; return ( <> @@ -21,11 +21,9 @@ export const InternalAccessManager = ({ id, disabled }: { id: string; disabled:
- {userEntities - ?.sort((a, b) => a?.id?.localeCompare(b?.id)) - ?.map(user => ( - - ))} + {item && getAllUserAccesses(item)?.map(user => + + )}
@@ -43,18 +41,8 @@ const UserAccessSelector = ({ id, disabled }: { id: string; disabled: boolean }) className="rounded-r-none" level={level} onChange={(users: UserType[]) => { - const id = users[0]?.id; - update({ - access_info: { - entities: [ - //Add or replace existing user - ...(item?.access_info.entities.filter(a => a.type !== 'user' || a.id !== id) || - []), - ...((id ? [{ type: 'user', id, level }] : []) as any), - ], - public: item?.access_info.public, - }, - }); + const id = users[0]?.id; //TODO: all others ignored + item && id && update(changeUserAccess(item, id, level)); }} initialUsers={[]} /> @@ -83,7 +71,6 @@ const UserAccessLevel = ({ const { item, loading, update } = useDriveItem(id); const user = useUser(userId); const { user: currentUser } = useCurrentUser(); - const level = item?.access_info.entities.filter(a => a.type === 'user' && a.id === userId)?.[0]?.level || 'none'; return ( - update({ - access_info: { - entities: - level === 'remove' - ? item?.access_info?.entities.filter( - e => e.type !== 'user' || e.id !== userId, - ) || [] - : item?.access_info?.entities.map(e => { - if (e.type === 'user' && e.id === userId) - return { ...e, level }; - return e; - }) || [], - public: item?.access_info.public, - }, - }) - } + onChange={level => item && update(changeUserAccess(item, userId, level === 'remove' ? false : level))} /> } /> diff --git a/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/public-link-access.tsx b/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/public-link-access.tsx index 5a4359137..fb4b4af7e 100644 --- a/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/public-link-access.tsx +++ b/tdrive/frontend/src/app/views/client/body/drive/modals/update-access/public-link-access.tsx @@ -7,7 +7,8 @@ import { AccessLevelDropdown } from './access-level-dropdown'; import Languages from 'features/global/services/languages-service'; import { Button } from '@atoms/button/button'; import { LinkIcon, UserGroupIcon, CheckCircleIcon } from '@heroicons/react/outline'; -import type { DriveFileAccessLevel } from 'app/features/drive/types'; +import type { DriveFileAccessLevelForPublicLink } from 'app/features/drive/types'; +import { changePublicLink } from '@features/files/utils/access-info-helpers'; export const PublicLinkManager = ({ id, disabled }: { id: string; disabled?: boolean }) => { const { item, loading, update } = useDriveItem(id); @@ -15,15 +16,10 @@ export const PublicLinkManager = ({ id, disabled }: { id: string; disabled?: boo const defaultPublicLinkLevel = 'read'; const publicLinkLevel = item?.access_info?.public?.level || 'none'; const havePublicLink = publicLinkLevel !== 'none'; - const [publicLinkCreationLevel, setPublicLinkCreationLevel] = useState(defaultPublicLinkLevel); + const [publicLinkCreationLevel, setPublicLinkCreationLevel] = useState(defaultPublicLinkLevel); const publicLinkCreationLevelSafe = havePublicLink ? publicLinkLevel || defaultPublicLinkLevel : publicLinkCreationLevel; - const updatePublicLinkLevel = (level: DriveFileAccessLevel) => { - update({ - access_info: { - entities: item?.access_info.entities || [], - public: { ...(item?.access_info?.public || { token: '' }), level }, - }, - }); + const updatePublicLinkLevel = (level: DriveFileAccessLevelForPublicLink) => { + item && update(changePublicLink(item, { level })); if (level === 'none') setPublicLinkCreationLevel(defaultPublicLinkLevel); }; @@ -57,9 +53,8 @@ export const PublicLinkManager = ({ id, disabled }: { id: string; disabled?: boo setDidJustCompleteACopy(true); setTimeout(() => setDidJustCompleteACopy(false), 1500); } - } else { + } else updatePublicLinkLevel(publicLinkCreationLevel); - } }} theme={didJustCompleteACopy ? "green" : "primary"} className="absolute top-0 right-0 justify-center"