✨ Shared with me browser integration (#84)
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user