Shared with me browser integration (#84)

This commit is contained in:
Montassar Ghanmy
2023-06-21 15:25:11 +01:00
committed by GitHub
parent 19b2d7d009
commit 4bef09abff
8 changed files with 175 additions and 33 deletions
@@ -1,5 +1,5 @@
import Api from '../../global/framework/api-service';
import { DriveItem, DriveItemDetails, DriveItemVersion } from '../types';
import { BrowseFilter, DriveItem, DriveItemDetails, DriveItemVersion } from '../types';
import Workspace from '@deprecated/workspaces/workspaces';
import Logger from 'features/global/framework/logger-service';
import { JWTDataType } from 'app/features/auth/jwt-storage-service';
@@ -70,6 +70,13 @@ export class DriveApiClient {
);
}
static async browse(companyId: string, id: string | 'trash' | '', filter: BrowseFilter) {
return await Api.post<BrowseFilter, DriveItemDetails>(
`/internal/services/documents/v1/companies/${companyId}/browse/${id}${appendTdriveToken()}`,
filter,
);
}
static async remove(companyId: string, id: string | 'trash' | '') {
return await Api.delete<void>(
`/internal/services/documents/v1/companies/${companyId}/item/${id}${appendTdriveToken()}`,
@@ -156,7 +163,11 @@ export class DriveApiClient {
static async sharedWithMe(filter?: any, options?: BaseSearchOptions) {
const companyId = options?.company_id ? options.company_id : Workspace.currentGroupId;
const query = `/internal/services/documents/v1/companies/${companyId}/shared-with-me`;
const filterData = { mime_type: filter.mimeType, creator: filter.creator, view: "shared_with_me" };
const filterData = {
mime_type: filter.mimeType,
creator: filter.creator,
view: 'shared_with_me',
};
const res = await Api.post<sharedWithMeFilterBody, { entities: DriveItem[] }>(
query,
filterData,
@@ -1,11 +1,11 @@
import { ToasterService } from '@features/global/services/toaster-service';
import useRouterCompany from '@features/router/hooks/use-router-company';
import { useCallback } from 'react';
import { useRecoilCallback } from 'recoil';
import { useRecoilValue, useRecoilCallback } from 'recoil';
import { DriveApiClient } from '../api-client/api-client';
import { DriveItemAtom, DriveItemChildrenAtom } from '../state/store';
import { DriveItem, DriveItemVersion } from '../types';
import { BrowseFilter, DriveItem, DriveItemVersion } from '../types';
import { SharedWithMeFilterState } from '../state/shared-with-me-filter';
/**
* Returns the children of a drive item
* @param id
@@ -13,13 +13,18 @@ import { DriveItem, DriveItemVersion } from '../types';
*/
export const useDriveActions = () => {
const companyId = useRouterCompany();
const sharedFilter = useRecoilValue(SharedWithMeFilterState);
const refresh = useRecoilCallback(
({ set, snapshot }) =>
async (parentId: string) => {
if (parentId) {
const filter:BrowseFilter = {
company_id: companyId,
mime_type: sharedFilter.mimeType.value,
};
try {
const details = await DriveApiClient.get(companyId, parentId);
const details = await DriveApiClient.browse(companyId, parentId, filter);
set(DriveItemChildrenAtom(parentId), details.children);
set(DriveItemAtom(parentId), details);
for (const child of details.children) {
@@ -71,8 +71,10 @@ export const useDriveItem = (id: string) => {
);
const inTrash = id === 'trash' || item?.path?.some(i => i.parent_id === 'trash');
const sharedWithMe = id =="shared_with_me";
return {
sharedWithMe,
inTrash,
loading: loading,
children: children || [],
@@ -2,16 +2,28 @@ import { string } from 'prop-types';
import { atom } from 'recoil';
export type SharedWithMeFilter = {
mimeType: string;
mimeType: {
key: string;
value: string
};
creator: string;
date: string;
date: {
key: string;
value: string;
};
};
export const SharedWithMeFilterState = atom<SharedWithMeFilter>({
key: 'SharedWithMeFilterState',
default: {
mimeType: '',
mimeType: {
key: '',
value: ''
},
creator: '',
date: '',
date: {
key: '',
value: ''
}
},
});
@@ -1,3 +1,8 @@
export type BrowseFilter = {
company_id: string;
mime_type?: string;
}
export type DriveItemDetails = {
item: DriveItem;
versions: DriveItemVersion[];
@@ -11,12 +11,16 @@ import { useDriveUpload } from '@features/drive/hooks/use-drive-upload';
import { DriveItemSelectedList } from '@features/drive/state/store';
import { formatBytes } from '@features/drive/utils';
import useRouterCompany from '@features/router/hooks/use-router-company';
import useRouterView from '@features/router/hooks/use-router-view';
import _ from 'lodash';
import { memo, Suspense, useCallback, useEffect, useRef, useState } from 'react';
import { atomFamily, useRecoilState, useSetRecoilState } from 'recoil';
import { DrivePreview } from '../../viewer/drive-preview';
import { useOnBuildContextMenu } from './context-menu';
import {
useOnBuildContextMenu,
useOnBuildFileTypeContextMenu,
useOnBuildPeopleContextMenu,
useOnBuildDateContextMenu,
} from './context-menu';
import { DocumentRow } from './documents/document-row';
import { FolderRow } from './documents/folder-row';
import HeaderPath from './header-path';
@@ -27,9 +31,10 @@ import { PropertiesModal } from './modals/properties';
import { AccessModal } from './modals/update-access';
import { VersionsModal } from './modals/versions';
import { SharedFilesTable } from './shared-files-table';
import RouterServices from '@features/router/services/router-service';
import useRouteState from 'app/features/router/hooks/use-route-state';
import Languages from "features/global/services/languages-service";
import { SharedWithMeFilterState } from '@features/drive/state/shared-with-me-filter';
import MenusManager from '@components/menus/menus-manager.jsx';
import Languages from 'features/global/services/languages-service';
export const DriveCurrentFolderAtom = atomFamily<
string,
@@ -53,6 +58,7 @@ export default memo(
}) => {
const companyId = useRouterCompany();
setTdriveTabToken(tdriveTabContextToken || null);
const [filter, setFilter] = useRecoilState(SharedWithMeFilterState);
const [parentId, _setParentId] = useRecoilState(
DriveCurrentFolderAtom({ context: context, initialFolderId: initialParentId || 'root' }),
@@ -60,6 +66,7 @@ export default memo(
const [loadingParentChange, setLoadingParentChange] = useState(false);
const {
sharedWithMe,
details,
access,
item,
@@ -96,14 +103,14 @@ export default memo(
//In case we are kicked out of the current folder, we need to reset the parent id
useEffect(() => {
if (!loading && !path?.length && !inPublicSharing) setParentId('root');
if (!loading && !path?.length && !inPublicSharing && !sharedWithMe) setParentId('root');
}, [path, loading, setParentId]);
useEffect(() => {
setChecked({});
refresh(parentId);
if (!inPublicSharing) refresh('trash');
}, [parentId, refresh]);
}, [parentId, refresh, filter]);
const openItemModal = useCallback(() => {
if (item?.id) setCreationModalState({ open: true, parent_id: item.id });
@@ -127,6 +134,10 @@ export default memo(
const onBuildContextMenu = useOnBuildContextMenu(children, initialParentId);
const { viewId } = useRouteState();
const buildFileTypeContextMenu = useOnBuildFileTypeContextMenu();
const buildPeopleContextMen = useOnBuildPeopleContextMenu();
const buildDateContextMenu = useOnBuildDateContextMenu();
return (
<>
{viewId == 'shared-with-me' ? (
@@ -171,16 +182,90 @@ export default memo(
}
>
<div className="flex flex-row shrink-0 items-center mb-4">
<HeaderPath path={path || []} inTrash={inTrash} setParentId={setParentId} />
{sharedWithMe ? (
<div>
<Title className="mb-4 block">
{Languages.t('scenes.app.shared_with_me.shared_with_me')}
</Title>
{/* Filters */}
<div className="flex items-center space-x-4 mb-6">
<div className="">
<Button
theme="secondary"
className="flex items-center"
onClick={evt => {
MenusManager.openMenu(
buildFileTypeContextMenu(),
{ x: evt.clientX, y: evt.clientY },
'center',
);
}}
>
<span>
{filter.mimeType.key && filter.mimeType.key != 'All'
? filter.mimeType.key
: Languages.t('scenes.app.shared_with_me.file_type')}
</span>
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
</Button>
</div>
<div className="flex items-center space-x-2">
<Button
theme="secondary"
className="flex items-center"
onClick={evt => {
MenusManager.openMenu(
buildPeopleContextMen(),
{ x: evt.clientX, y: evt.clientY },
'center',
);
}}
>
<span>{Languages.t('scenes.app.shared_with_me.people')}</span>
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
</Button>
</div>
<div className="flex items-center space-x-2">
<Button
theme="secondary"
className="flex items-center"
onClick={evt => {
MenusManager.openMenu(
buildDateContextMenu(),
{ x: evt.clientX, y: evt.clientY },
'center',
);
}}
>
<span>
{filter.date.key && filter.date.key != 'All'
? filter.date.key
: Languages.t('scenes.app.shared_with_me.last_modified')}
</span>
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
</Button>
</div>
</div>
</div>
) : (
<HeaderPath path={path || []} inTrash={inTrash} setParentId={setParentId} />
)}
<div className="grow" />
{access !== 'read' && (
<BaseSmall>{formatBytes(item?.size || 0)} { Languages.t('scenes.app.drive.used')}</BaseSmall>
<BaseSmall>
{formatBytes(item?.size || 0)} {Languages.t('scenes.app.drive.used')}
</BaseSmall>
)}
<Menu menu={() => onBuildContextMenu(details)}>
{' '}
<Button theme="secondary" className="ml-4 flex flex-row items-center">
<span>{selectedCount > 1 ? `${selectedCount} items` : Languages.t('scenes.app.drive.context_menu')} </span>
<span>
{selectedCount > 1
? `${selectedCount} items`
: Languages.t('scenes.app.drive.context_menu')}{' '}
</span>
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
</Button>
@@ -218,12 +303,12 @@ export default memo(
{documents.length === 0 && !loading && (
<div className="mt-4 text-center border-2 border-dashed rounded-md p-8">
<Subtitle className="block mb-2">{Languages.t('scenes.app.drive.nothing')}</Subtitle>
<Subtitle className="block mb-2">
{Languages.t('scenes.app.drive.nothing')}
</Subtitle>
{!inTrash && access != 'read' && (
<>
<Base>
{Languages.t('scenes.app.drive.drag_and_drop')}
</Base>
<Base>{Languages.t('scenes.app.drive.drag_and_drop')}</Base>
<br />
<Button onClick={() => openItemModal()} theme="primary" className="mt-4">
{Languages.t('scenes.app.drive.add_doc')}
@@ -44,7 +44,7 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
return useCallback(
async (parent?: Partial<DriveItemDetails> | null, item?: DriveItem) => {
if (!parent || !parent.item || !parent.access) return [];
if (!parent || !parent.access) return [];
try {
const inTrash = parent.path?.[0]?.id === 'trash';
@@ -310,9 +310,17 @@ export const useOnBuildFileTypeContextMenu = () => {
const [filter, setFilter] = useRecoilState(SharedWithMeFilterState);
const mimeTypes = [
{ key: Languages.t('components.item_context_menu.all'), value: '' },
{ key: 'PDF', value: 'application/pdf' },
{ key: 'CSV', value: 'text/csv' },
{ key: 'DOC', value: 'application/msword' },
{ key: 'GIF', value: 'image/gif' },
{ key: 'JPEG', value: 'image/jpeg' },
{ key: 'JPG', value: 'image/jpeg' },
{ key: 'PDF', value: 'application/pdf' },
{ key: 'PNG', value: 'image/png' },
{ key: 'PPT', value: 'application/vnd.ms-powerpoint' },
{ key: 'TXT', value: 'text/plain' },
{ key: 'XLS', value: 'application/vnd.ms-excel' },
{ key: 'ZIP', value: 'application/zip' },
];
return useCallback(() => {
const menuItems = mimeTypes.map(item => {
@@ -323,7 +331,7 @@ export const useOnBuildFileTypeContextMenu = () => {
setFilter(prevFilter => {
const newFilter = {
...prevFilter,
mimeType: item.value,
mimeType: item,
};
return newFilter;
});
@@ -370,7 +378,10 @@ export const useOnBuildDateContextMenu = () => {
setFilter(prevFilter => {
const newFilter = {
...prevFilter,
date: '',
date: {
key: 'All',
value: ''
},
};
return newFilter;
});
@@ -383,7 +394,10 @@ export const useOnBuildDateContextMenu = () => {
setFilter(prevFilter => {
const newFilter = {
...prevFilter,
date: 'today',
date: {
key: 'Today',
value: 'today'
}
};
return newFilter;
});
@@ -396,7 +410,10 @@ export const useOnBuildDateContextMenu = () => {
setFilter(prevFilter => {
const newFilter = {
...prevFilter,
date: 'last_week',
date: {
key: 'Last week',
value: 'last_week'
}
};
return newFilter;
});
@@ -409,7 +426,10 @@ export const useOnBuildDateContextMenu = () => {
setFilter(prevFilter => {
const newFilter = {
...prevFilter,
date: 'last_month',
date: {
key: 'Last month',
value: 'last_month'
}
};
return newFilter;
});
@@ -23,6 +23,7 @@ import Actions from './actions';
import { useHistory, useLocation } from 'react-router-dom';
import RouterServices from '@features/router/services/router-service';
import Languages from "features/global/services/languages-service";
import shared from '../body/drive/shared';
export default () => {
const history = useHistory();
@@ -35,11 +36,12 @@ export default () => {
const { user } = useCurrentUser();
const active = false;
const { access: rootAccess } = useDriveItem('root');
const { inTrash, path } = useDriveItem(parentId);
const { sharedWithMe, inTrash, path } = useDriveItem(parentId);
const activeClass = 'bg-zinc-50 dark:bg-zinc-800 !text-blue-500';
let folderType = 'home';
if ((path || [])[0]?.id === 'user_' + user?.id) folderType = 'personal';
if (inTrash) folderType = 'trash';
if (sharedWithMe) folderType = 'shared';
const { viewId } = RouterServices.getStateFromRoute();
return (
<div className="grow flex flex-col overflow-auto -m-4 p-4 relative">
@@ -77,10 +79,10 @@ export default () => {
<UserIcon className="w-5 h-5 mr-4" /> {Languages.t('components.side_menu.my_drive')}
</Button>
<Button
onClick={() => history.push(RouterServices.generateRouteFromState({companyId: company, viewId: "shared-with-me"}))}
onClick={() => {setParentId('shared_with_me')}}
size="lg"
theme="white"
className={'w-full mb-1 ' + (viewId === 'shared-with-me' ? activeClass : '')}
className={'w-full mb-1 ' + (folderType === 'shared' && viewId == ''? activeClass : '')}
>
<UserGroupIcon className="w-5 h-5 mr-4" /> Shared with me
</Button>