♻️ front: moved driveitem access entities editing to a helper file (#50)
This commit is contained in:
committed by
ericlinagora
parent
ecf106c5f8
commit
fc3ebaa911
@@ -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;
|
||||
|
||||
@@ -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<DriveItem>) : Exclude<DriveFileAccessLevel, "none"> | false =>
|
||||
(item?.access_info?.public?.level && item.access_info.public.level != "none") ? item.access_info.public.level : false;
|
||||
@@ -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(
|
||||
|
||||
@@ -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 = ({
|
||||
<Base className="flex maxWidth100">{item.name}</Base>
|
||||
</div>
|
||||
<div className="shrink-0 ml-4">
|
||||
{item?.access_info?.public?.level !== 'none' && (
|
||||
{hasAnyPublicLinkAccess(item) && (
|
||||
<PublicIcon className="h-5 w-5 text-blue-500" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -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 = ({
|
||||
<Base className="!font-semibold flex maxWidth100">{item.name}</Base>
|
||||
</div>
|
||||
<div className="shrink-0 ml-4">
|
||||
{item?.access_info?.public?.level !== 'none' && (
|
||||
{hasAnyPublicLinkAccess(item) && (
|
||||
<PublicIcon className="h-5 w-5 text-blue-500" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -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 = ({
|
||||
})()}
|
||||
</Title>
|
||||
</a>
|
||||
{item?.access_info?.public?.level && item?.access_info?.public?.level !== 'none' && (
|
||||
{hasAnyPublicLinkAccess(item) && (
|
||||
<PublicIcon className="h-5 w-5 ml-2" />
|
||||
)}
|
||||
{first && !!user?.id && viewId?.includes('trash') && (
|
||||
|
||||
@@ -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 (
|
||||
<ModalContent
|
||||
title={
|
||||
@@ -138,10 +128,10 @@ const AccessModalContent = (props: {
|
||||
password={item?.access_info?.public?.password}
|
||||
expiration={item?.access_info?.public?.expiration}
|
||||
onChangePassword={(password: string) => {
|
||||
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 && <>
|
||||
|
||||
+28
-60
@@ -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<DriveItem>) => 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) &&
|
||||
<Subtitle className="block mt-4 mb-1">{Languages.t('components.internal-access_access_manage')}</Subtitle>}
|
||||
|
||||
{folderEntity && (
|
||||
<div className="p-4 flex flex-row items-center justify-center">
|
||||
{ <div className="p-4 flex flex-row items-center justify-center">
|
||||
<div className="grow">
|
||||
<Base>{Languages.t('components.internal-access_inherit_parent')}</Base>
|
||||
<br />
|
||||
@@ -36,23 +40,15 @@ export const InheritAccessOptions = (props: {
|
||||
<Checkbox
|
||||
disabled={props.disabled}
|
||||
onChange={status => {
|
||||
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'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
}
|
||||
|
||||
{companyEntity && folderEntity.level === 'none' && (
|
||||
{ companyEntityLevel && folderEntityLevel === 'none' && (
|
||||
<div className="p-4 flex flex-row items-center justify-center">
|
||||
<div className="grow">
|
||||
<Base>{Languages.t('components.internal-access_company_member')}</Base>
|
||||
@@ -61,29 +57,21 @@ export const InheritAccessOptions = (props: {
|
||||
<AccessLevelDropdown
|
||||
disabled={props.disabled}
|
||||
onChange={level => {
|
||||
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}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{channelEntities.length > 0 && (
|
||||
{channelEntitiesLevel && (
|
||||
<div className="p-4 border-b flex flex-row items-center justify-center">
|
||||
<div className="grow">
|
||||
<Base>{Languages.t('components.internal-access_cannal')}</Base>
|
||||
<br />
|
||||
<Info>
|
||||
{channelEntities.length} {Languages.t('components.internal-access_cannal_info')}
|
||||
{channelEntitiesLevel.length} {Languages.t('components.internal-access_cannal_info')}
|
||||
</Info>
|
||||
</div>
|
||||
<div className="shrink-0 ml-2">
|
||||
@@ -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}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+8
-38
@@ -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:
|
||||
<div className="rounded-md border mt-2 dark:border-zinc-700">
|
||||
<UserAccessSelector id={id} disabled={disabled} />
|
||||
|
||||
{userEntities
|
||||
?.sort((a, b) => a?.id?.localeCompare(b?.id))
|
||||
?.map(user => (
|
||||
<UserAccessLevel key={user.id} id={id} userId={user?.id} disabled={disabled} />
|
||||
))}
|
||||
{item && getAllUserAccesses(item)?.map(user =>
|
||||
<UserAccessLevel key={user.id} id={id} userId={user?.id} disabled={disabled} />
|
||||
)}
|
||||
<div className="-mb-px" />
|
||||
</div>
|
||||
</>
|
||||
@@ -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 (
|
||||
<UserBlock
|
||||
className="p-4 border-t dark:border-zinc-700"
|
||||
@@ -92,26 +79,9 @@ const UserAccessLevel = ({
|
||||
suffix={
|
||||
<AccessLevelDropdown
|
||||
disabled={loading || disabled || user?.id === currentUser?.id}
|
||||
level={level}
|
||||
level={(item && getUserAccessLevel(item, userId)) || "none"}
|
||||
canRemove={true}
|
||||
onChange={
|
||||
level =>
|
||||
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))}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
+6
-11
@@ -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<DriveFileAccessLevel>(defaultPublicLinkLevel);
|
||||
const [publicLinkCreationLevel, setPublicLinkCreationLevel] = useState<DriveFileAccessLevelForPublicLink>(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"
|
||||
|
||||
Reference in New Issue
Block a user