♻️ 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;
|
scope: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DriveFileAccessLevelForInherited = 'none' | 'manage';
|
||||||
|
export type DriveFileAccessLevelForPublicLink = 'none' | 'read' | 'write';
|
||||||
export type DriveFileAccessLevel = 'none' | 'read' | 'write' | 'manage';
|
export type DriveFileAccessLevel = 'none' | 'read' | 'write' | 'manage';
|
||||||
|
|
||||||
export type DriveItemAccessInfo = {
|
export type DriveItemAccessInfo = {
|
||||||
@@ -50,7 +52,7 @@ export type DriveItemAccessInfo = {
|
|||||||
entities: AuthEntity[];
|
entities: AuthEntity[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type AuthEntity = {
|
export type AuthEntity = {
|
||||||
type: 'user' | 'channel' | 'company' | 'folder';
|
type: 'user' | 'channel' | 'company' | 'folder';
|
||||||
id: string | 'parent';
|
id: string | 'parent';
|
||||||
level: DriveFileAccessLevel;
|
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 useRouterCompany from '@features/router/hooks/use-router-company';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import Languages from 'features/global/services/languages-service';
|
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
|
* This will build the context menu in different contexts
|
||||||
@@ -309,9 +310,7 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
|
|||||||
{
|
{
|
||||||
type: 'menu',
|
type: 'menu',
|
||||||
text: Languages.t('components.item_context_menu.copy_link'),
|
text: Languages.t('components.item_context_menu.copy_link'),
|
||||||
hide:
|
hide: !hasAnyPublicLinkAccess(item),
|
||||||
!parent?.item?.access_info?.public?.level ||
|
|
||||||
parent?.item?.access_info?.public?.level === 'none',
|
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
copyToClipboard(getPublicLink(item || parent?.item));
|
copyToClipboard(getPublicLink(item || parent?.item));
|
||||||
ToasterService.success(
|
ToasterService.success(
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { useHistory } from 'react-router-dom';
|
|||||||
import RouterServices from '@features/router/services/router-service';
|
import RouterServices from '@features/router/services/router-service';
|
||||||
import useRouteState from 'app/features/router/hooks/use-route-state';
|
import useRouteState from 'app/features/router/hooks/use-route-state';
|
||||||
import { DocumentIcon } from './document-icon';
|
import { DocumentIcon } from './document-icon';
|
||||||
|
import { hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers';
|
||||||
|
|
||||||
export const DocumentRow = ({
|
export const DocumentRow = ({
|
||||||
item,
|
item,
|
||||||
@@ -79,7 +80,7 @@ export const DocumentRow = ({
|
|||||||
<Base className="flex maxWidth100">{item.name}</Base>
|
<Base className="flex maxWidth100">{item.name}</Base>
|
||||||
</div>
|
</div>
|
||||||
<div className="shrink-0 ml-4">
|
<div className="shrink-0 ml-4">
|
||||||
{item?.access_info?.public?.level !== 'none' && (
|
{hasAnyPublicLinkAccess(item) && (
|
||||||
<PublicIcon className="h-5 w-5 text-blue-500" />
|
<PublicIcon className="h-5 w-5 text-blue-500" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { formatBytes } from '@features/drive/utils';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { PublicIcon } from '../components/public-icon';
|
import { PublicIcon } from '../components/public-icon';
|
||||||
import { CheckableIcon, DriveItemProps } from './common';
|
import { CheckableIcon, DriveItemProps } from './common';
|
||||||
|
import { hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
export const FolderRow = ({
|
export const FolderRow = ({
|
||||||
@@ -49,7 +50,7 @@ export const FolderRow = ({
|
|||||||
<Base className="!font-semibold flex maxWidth100">{item.name}</Base>
|
<Base className="!font-semibold flex maxWidth100">{item.name}</Base>
|
||||||
</div>
|
</div>
|
||||||
<div className="shrink-0 ml-4">
|
<div className="shrink-0 ml-4">
|
||||||
{item?.access_info?.public?.level !== 'none' && (
|
{hasAnyPublicLinkAccess(item) && (
|
||||||
<PublicIcon className="h-5 w-5 text-blue-500" />
|
<PublicIcon className="h-5 w-5 text-blue-500" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import Languages from 'features/global/services/languages-service';
|
|||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import useRouterCompany from '@features/router/hooks/use-router-company';
|
import useRouterCompany from '@features/router/hooks/use-router-company';
|
||||||
import RouterServices from '@features/router/services/router-service';
|
import RouterServices from '@features/router/services/router-service';
|
||||||
|
import { hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers';
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
path: livePath,
|
path: livePath,
|
||||||
@@ -193,7 +194,7 @@ const PathItem = ({
|
|||||||
})()}
|
})()}
|
||||||
</Title>
|
</Title>
|
||||||
</a>
|
</a>
|
||||||
{item?.access_info?.public?.level && item?.access_info?.public?.level !== 'none' && (
|
{hasAnyPublicLinkAccess(item) && (
|
||||||
<PublicIcon className="h-5 w-5 ml-2" />
|
<PublicIcon className="h-5 w-5 ml-2" />
|
||||||
)}
|
)}
|
||||||
{first && !!user?.id && viewId?.includes('trash') && (
|
{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 { PublicLinkAccessOptions } from './public-link-access-options';
|
||||||
import { CuteDepictionOfFolderHierarchy } from './cute-depiction-of-folder-hierarchy';
|
import { CuteDepictionOfFolderHierarchy } from './cute-depiction-of-folder-hierarchy';
|
||||||
import { InheritAccessOptions } from './inherit-access-options';
|
import { InheritAccessOptions } from './inherit-access-options';
|
||||||
|
import { changePublicLink, hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers';
|
||||||
|
|
||||||
export type AccessModalType = {
|
export type AccessModalType = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -94,20 +95,9 @@ const AccessModalContent = (props: {
|
|||||||
refresh(id);
|
refresh(id);
|
||||||
refreshCompany();
|
refreshCompany();
|
||||||
}, []);
|
}, []);
|
||||||
const havePublicLink = (item?.access_info?.public?.level || 'none') !== 'none';
|
const havePublicLink = hasAnyPublicLinkAccess(item);
|
||||||
const haveAdvancedSettings = parentItem?.parent_id !== null || havePublicLink;
|
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 (
|
return (
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title={
|
title={
|
||||||
@@ -138,10 +128,10 @@ const AccessModalContent = (props: {
|
|||||||
password={item?.access_info?.public?.password}
|
password={item?.access_info?.public?.password}
|
||||||
expiration={item?.access_info?.public?.expiration}
|
expiration={item?.access_info?.public?.expiration}
|
||||||
onChangePassword={(password: string) => {
|
onChangePassword={(password: string) => {
|
||||||
updatePublicAccess('password', password || '', true);
|
item && changePublicLink(item, { password: password || '' });
|
||||||
}}
|
}}
|
||||||
onChangeExpiration={(expiration: number) => {
|
onChangeExpiration={(expiration: number) => {
|
||||||
updatePublicAccess('expiration', expiration || 0);
|
item && changePublicLink(item, { expiration: expiration || 0 });
|
||||||
}}
|
}}
|
||||||
/>}
|
/>}
|
||||||
{ parentItem?.parent_id !== null && <>
|
{ parentItem?.parent_id !== null && <>
|
||||||
|
|||||||
+28
-60
@@ -1,6 +1,14 @@
|
|||||||
import Languages from 'features/global/services/languages-service';
|
import Languages from 'features/global/services/languages-service';
|
||||||
|
|
||||||
import type { DriveItem } from 'app/features/drive/types';
|
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 { Base, Info, Subtitle } from '@atoms/text';
|
||||||
import { Checkbox } from '@atoms/input/input-checkbox';
|
import { Checkbox } from '@atoms/input/input-checkbox';
|
||||||
@@ -12,21 +20,17 @@ export const InheritAccessOptions = (props: {
|
|||||||
disabled: boolean,
|
disabled: boolean,
|
||||||
onUpdate: (item: Partial<DriveItem>) => void,
|
onUpdate: (item: Partial<DriveItem>) => void,
|
||||||
}) => {
|
}) => {
|
||||||
const folderEntity = props.item?.access_info.entities.filter(a => a.type === 'folder')?.[0] || {
|
// 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
|
||||||
type: 'folder',
|
const folderEntityLevel = getInheritedAccessLevel(props.item) || 'manage';
|
||||||
id: 'parent',
|
const companyEntityLevel = getCompanyAccessLevel(props.item);
|
||||||
level: 'manage',
|
const channelEntitiesLevel = getFirstChannelAccessLevel(props.item);
|
||||||
};
|
|
||||||
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') || [];
|
|
||||||
|
|
||||||
return (
|
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>}
|
<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">
|
<div className="grow">
|
||||||
<Base>{Languages.t('components.internal-access_inherit_parent')}</Base>
|
<Base>{Languages.t('components.internal-access_inherit_parent')}</Base>
|
||||||
<br />
|
<br />
|
||||||
@@ -36,23 +40,15 @@ export const InheritAccessOptions = (props: {
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
onChange={status => {
|
onChange={status => {
|
||||||
props.onUpdate({
|
props.item && props.onUpdate(changeInheritedAccess(props.item, status ? 'manage' : 'none'));
|
||||||
access_info: {
|
|
||||||
entities: [
|
|
||||||
...(props.item?.access_info.entities.filter(a => a.type !== 'folder') || []),
|
|
||||||
{ ...folderEntity, level: status ? 'manage' : 'none' },
|
|
||||||
],
|
|
||||||
public: props.item?.access_info.public,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
value={folderEntity.level === 'manage'}
|
value={folderEntityLevel === 'manage'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
}
|
||||||
|
|
||||||
{companyEntity && folderEntity.level === 'none' && (
|
{ companyEntityLevel && folderEntityLevel === 'none' && (
|
||||||
<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">
|
<div className="grow">
|
||||||
<Base>{Languages.t('components.internal-access_company_member')}</Base>
|
<Base>{Languages.t('components.internal-access_company_member')}</Base>
|
||||||
@@ -61,29 +57,21 @@ export const InheritAccessOptions = (props: {
|
|||||||
<AccessLevelDropdown
|
<AccessLevelDropdown
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
onChange={level => {
|
onChange={level => {
|
||||||
props.onUpdate({
|
props.item && props.onUpdate(changeCompanyAccessLevel(props.item, level === 'remove' ? false : level));
|
||||||
access_info: {
|
|
||||||
entities: [
|
|
||||||
...(props.item?.access_info.entities.filter(a => a.type !== 'company') || []),
|
|
||||||
...(level !== 'remove' ? [{ ...companyEntity, level }] : []),
|
|
||||||
],
|
|
||||||
public: props.item?.access_info.public,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
level={companyEntity.level}
|
level={companyEntityLevel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{channelEntities.length > 0 && (
|
{channelEntitiesLevel && (
|
||||||
<div className="p-4 border-b flex flex-row items-center justify-center">
|
<div className="p-4 border-b flex flex-row items-center justify-center">
|
||||||
<div className="grow">
|
<div className="grow">
|
||||||
<Base>{Languages.t('components.internal-access_cannal')}</Base>
|
<Base>{Languages.t('components.internal-access_cannal')}</Base>
|
||||||
<br />
|
<br />
|
||||||
<Info>
|
<Info>
|
||||||
{channelEntities.length} {Languages.t('components.internal-access_cannal_info')}
|
{channelEntitiesLevel.length} {Languages.t('components.internal-access_cannal_info')}
|
||||||
</Info>
|
</Info>
|
||||||
</div>
|
</div>
|
||||||
<div className="shrink-0 ml-2">
|
<div className="shrink-0 ml-2">
|
||||||
@@ -92,41 +80,21 @@ export const InheritAccessOptions = (props: {
|
|||||||
hiddenLevels={['none']}
|
hiddenLevels={['none']}
|
||||||
canRemove
|
canRemove
|
||||||
onChange={level => {
|
onChange={level => {
|
||||||
if (level === 'remove') {
|
if (level === 'remove')
|
||||||
AlertManager.confirm(
|
AlertManager.confirm(
|
||||||
async () => {
|
async () => {
|
||||||
//Remove channel access
|
//Remove channel access
|
||||||
props.onUpdate({
|
props.item && props.onUpdate(changeAllChannelAccessLevels(props.item, false));
|
||||||
access_info: {
|
|
||||||
entities:
|
|
||||||
props.item?.access_info?.entities.filter(e => e.type !== 'channel') || [],
|
|
||||||
public: props.item?.access_info.public,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
//Do nothing
|
|
||||||
},
|
},
|
||||||
|
() => { /* Do nothing */ },
|
||||||
{
|
{
|
||||||
text: Languages.t('components.internal-access_cannal_info_give_back'),
|
text: Languages.t('components.internal-access_cannal_info_give_back'),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
else
|
||||||
props.onUpdate({
|
props.item && props.onUpdate(changeAllChannelAccessLevels(props.item, level));
|
||||||
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,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
level={channelEntities[0].level}
|
level={channelEntitiesLevel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+8
-38
@@ -9,11 +9,11 @@ import { useState } from 'react';
|
|||||||
import SelectUsers from '../../components/select-users';
|
import SelectUsers from '../../components/select-users';
|
||||||
import { AccessLevelDropdown } from './access-level-dropdown';
|
import { AccessLevelDropdown } from './access-level-dropdown';
|
||||||
import Languages from 'features/global/services/languages-service';
|
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 }) => {
|
export const InternalAccessManager = ({ id, disabled }: { id: string; disabled: boolean }) => {
|
||||||
const { item } = useDriveItem(id);
|
const { item } = useDriveItem(id);
|
||||||
const userEntities = item?.access_info.entities.filter(a => a.type === 'user') || [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -21,11 +21,9 @@ export const InternalAccessManager = ({ id, disabled }: { id: string; disabled:
|
|||||||
<div className="rounded-md border mt-2 dark:border-zinc-700">
|
<div className="rounded-md border mt-2 dark:border-zinc-700">
|
||||||
<UserAccessSelector id={id} disabled={disabled} />
|
<UserAccessSelector id={id} disabled={disabled} />
|
||||||
|
|
||||||
{userEntities
|
{item && getAllUserAccesses(item)?.map(user =>
|
||||||
?.sort((a, b) => a?.id?.localeCompare(b?.id))
|
<UserAccessLevel key={user.id} id={id} userId={user?.id} disabled={disabled} />
|
||||||
?.map(user => (
|
)}
|
||||||
<UserAccessLevel key={user.id} id={id} userId={user?.id} disabled={disabled} />
|
|
||||||
))}
|
|
||||||
<div className="-mb-px" />
|
<div className="-mb-px" />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -43,18 +41,8 @@ const UserAccessSelector = ({ id, disabled }: { id: string; disabled: boolean })
|
|||||||
className="rounded-r-none"
|
className="rounded-r-none"
|
||||||
level={level}
|
level={level}
|
||||||
onChange={(users: UserType[]) => {
|
onChange={(users: UserType[]) => {
|
||||||
const id = users[0]?.id;
|
const id = users[0]?.id; //TODO: all others ignored
|
||||||
update({
|
item && id && update(changeUserAccess(item, id, level));
|
||||||
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,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
initialUsers={[]}
|
initialUsers={[]}
|
||||||
/>
|
/>
|
||||||
@@ -83,7 +71,6 @@ const UserAccessLevel = ({
|
|||||||
const { item, loading, update } = useDriveItem(id);
|
const { item, loading, update } = useDriveItem(id);
|
||||||
const user = useUser(userId);
|
const user = useUser(userId);
|
||||||
const { user: currentUser } = useCurrentUser();
|
const { user: currentUser } = useCurrentUser();
|
||||||
const level = item?.access_info.entities.filter(a => a.type === 'user' && a.id === userId)?.[0]?.level || 'none';
|
|
||||||
return (
|
return (
|
||||||
<UserBlock
|
<UserBlock
|
||||||
className="p-4 border-t dark:border-zinc-700"
|
className="p-4 border-t dark:border-zinc-700"
|
||||||
@@ -92,26 +79,9 @@ const UserAccessLevel = ({
|
|||||||
suffix={
|
suffix={
|
||||||
<AccessLevelDropdown
|
<AccessLevelDropdown
|
||||||
disabled={loading || disabled || user?.id === currentUser?.id}
|
disabled={loading || disabled || user?.id === currentUser?.id}
|
||||||
level={level}
|
level={(item && getUserAccessLevel(item, userId)) || "none"}
|
||||||
canRemove={true}
|
canRemove={true}
|
||||||
onChange={
|
onChange={level => item && update(changeUserAccess(item, userId, level === 'remove' ? false : level))}
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+6
-11
@@ -7,7 +7,8 @@ import { AccessLevelDropdown } from './access-level-dropdown';
|
|||||||
import Languages from 'features/global/services/languages-service';
|
import Languages from 'features/global/services/languages-service';
|
||||||
import { Button } from '@atoms/button/button';
|
import { Button } from '@atoms/button/button';
|
||||||
import { LinkIcon, UserGroupIcon, CheckCircleIcon } from '@heroicons/react/outline';
|
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 }) => {
|
export const PublicLinkManager = ({ id, disabled }: { id: string; disabled?: boolean }) => {
|
||||||
const { item, loading, update } = useDriveItem(id);
|
const { item, loading, update } = useDriveItem(id);
|
||||||
@@ -15,15 +16,10 @@ export const PublicLinkManager = ({ id, disabled }: { id: string; disabled?: boo
|
|||||||
const defaultPublicLinkLevel = 'read';
|
const defaultPublicLinkLevel = 'read';
|
||||||
const publicLinkLevel = item?.access_info?.public?.level || 'none';
|
const publicLinkLevel = item?.access_info?.public?.level || 'none';
|
||||||
const havePublicLink = publicLinkLevel !== 'none';
|
const havePublicLink = publicLinkLevel !== 'none';
|
||||||
const [publicLinkCreationLevel, setPublicLinkCreationLevel] = useState<DriveFileAccessLevel>(defaultPublicLinkLevel);
|
const [publicLinkCreationLevel, setPublicLinkCreationLevel] = useState<DriveFileAccessLevelForPublicLink>(defaultPublicLinkLevel);
|
||||||
const publicLinkCreationLevelSafe = havePublicLink ? publicLinkLevel || defaultPublicLinkLevel : publicLinkCreationLevel;
|
const publicLinkCreationLevelSafe = havePublicLink ? publicLinkLevel || defaultPublicLinkLevel : publicLinkCreationLevel;
|
||||||
const updatePublicLinkLevel = (level: DriveFileAccessLevel) => {
|
const updatePublicLinkLevel = (level: DriveFileAccessLevelForPublicLink) => {
|
||||||
update({
|
item && update(changePublicLink(item, { level }));
|
||||||
access_info: {
|
|
||||||
entities: item?.access_info.entities || [],
|
|
||||||
public: { ...(item?.access_info?.public || { token: '' }), level },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (level === 'none')
|
if (level === 'none')
|
||||||
setPublicLinkCreationLevel(defaultPublicLinkLevel);
|
setPublicLinkCreationLevel(defaultPublicLinkLevel);
|
||||||
};
|
};
|
||||||
@@ -57,9 +53,8 @@ export const PublicLinkManager = ({ id, disabled }: { id: string; disabled?: boo
|
|||||||
setDidJustCompleteACopy(true);
|
setDidJustCompleteACopy(true);
|
||||||
setTimeout(() => setDidJustCompleteACopy(false), 1500);
|
setTimeout(() => setDidJustCompleteACopy(false), 1500);
|
||||||
}
|
}
|
||||||
} else {
|
} else
|
||||||
updatePublicLinkLevel(publicLinkCreationLevel);
|
updatePublicLinkLevel(publicLinkCreationLevel);
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
theme={didJustCompleteACopy ? "green" : "primary"}
|
theme={didJustCompleteACopy ? "green" : "primary"}
|
||||||
className="absolute top-0 right-0 justify-center"
|
className="absolute top-0 right-0 justify-center"
|
||||||
|
|||||||
Reference in New Issue
Block a user