️ Writeable shared links part 1 (#66)

* Minor fixes and implemented writeable shared links (no security)

* Go t onode 16 for frontend
This commit is contained in:
Romaric Mourgues
2023-06-05 10:06:22 +02:00
committed by GitHub
parent f8d3fc9784
commit 49ec20d60e
12 changed files with 97 additions and 95 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ jobs:
path: tdrive/frontend/build/ path: tdrive/frontend/build/
publish-frontend: publish-frontend:
# needs: build-frontend # needs: build-frontend
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/qa' || github.ref == 'refs/heads/canary' if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/qa' || github.ref == 'refs/heads/canary'
steps: steps:
@@ -51,24 +51,6 @@ class CurrentUser extends Observable {
return getUser(Login.currentUserId); return getUser(Login.currentUserId);
} }
updateUserStatus = (newStatus: string[]) => {
return () => {
const { updateStatus } = useCurrentUser();
updateStatus(newStatus);
};
};
async updateStatusIcon(status: string[]) {
const update = {
id: Login.currentUserId,
status_icon: status,
};
Collections.get('users').updateObject(update);
this.updateUserStatus(status);
await UserAPIClient.updateUserStatus(`${status[0]} ${status[1]}`);
}
updateTutorialStatus(key: string, set_false?: unknown) { updateTutorialStatus(key: string, set_false?: unknown) {
const user = this.get(); const user = this.get();
if (!user) { if (!user) {
@@ -106,7 +106,9 @@ export const useCurrentCompanyRealtime = () => {
* @param user * @param user
* @returns CompanyType | undefined * @returns CompanyType | undefined
*/ */
export function useBestCandidateCompany(user: UserType | undefined): CompanyType | undefined { export function useBestCandidateCompany(
user: UserType | undefined | null,
): CompanyType | undefined {
const routerCompanyId = useRouterCompany(); const routerCompanyId = useRouterCompany();
const storageCompanyId = (LocalStorage.getItem('default_company_id') as string) || null; const storageCompanyId = (LocalStorage.getItem('default_company_id') as string) || null;
@@ -24,6 +24,10 @@ export const setPublicLinkToken = (token: string | null) => {
publicLinkToken = token; publicLinkToken = token;
}; };
export const getPublicLinkToken = () => {
return publicLinkToken;
};
export const setTdriveTabToken = (token: string | null) => { export const setTdriveTabToken = (token: string | null) => {
tdriveTabToken = token; tdriveTabToken = token;
}; };
@@ -6,18 +6,17 @@ import { useRecoilState } from 'recoil';
import { CurrentUserState } from '../state/atoms/current-user'; import { CurrentUserState } from '../state/atoms/current-user';
import { useRealtimeRoom } from '@features/global/hooks/use-realtime'; import { useRealtimeRoom } from '@features/global/hooks/use-realtime';
import Languages from '@features/global/services/languages-service'; import Languages from '@features/global/services/languages-service';
import { RealtimeApplicationEvent } from '@features/global/types/realtime-types';
import { useSetUserList } from './use-user-list'; import { useSetUserList } from './use-user-list';
import { getPublicLinkToken } from 'app/features/drive/api-client/api-client';
export const useCurrentUser = () => { export const useCurrentUser = () => {
console.log("getting current user");
const [user, setUser] = useRecoilState(CurrentUserState); const [user, setUser] = useRecoilState(CurrentUserState);
const { set: setUserList } = useSetUserList('useCurrentUser'); const { set: setUserList } = useSetUserList('useCurrentUser');
//Depreciated way to get use update from LoginService //Depreciated way to get use update from LoginService
LoginService.recoilUpdateUser = setUser; LoginService.recoilUpdateUser = setUser;
useEffect(() => { useEffect(() => {
if (!user) { if (!user && !getPublicLinkToken()) {
LoginService.init(); LoginService.init();
LoginService.login({}); LoginService.login({});
} }
@@ -29,17 +28,13 @@ export const useCurrentUser = () => {
if (user?.preferences?.locale) Languages.setLanguage(user?.preferences?.locale); if (user?.preferences?.locale) Languages.setLanguage(user?.preferences?.locale);
}, [user?.preferences?.locale]); }, [user?.preferences?.locale]);
const updateStatus = async (userStatus: string[]) => {
await UserAPIClient.updateUserStatus(`${userStatus[0]} ${userStatus[1]}`);
await refresh();
};
const refresh = async () => { const refresh = async () => {
await LoginService.updateUser(); if (!getPublicLinkToken()) {
await LoginService.updateUser();
}
}; };
return { user, refresh, updateStatus }; return { user, refresh };
}; };
export const useCurrentUserRealtime = () => { export const useCurrentUserRealtime = () => {
@@ -118,7 +118,7 @@ export default memo(
return ( return (
<UploadZone <UploadZone
overClassName={''} overClassName={''}
className="min-h-full" className="h-full overflow-hidden"
disableClick disableClick
parent={''} parent={''}
multiple={true} multiple={true}
@@ -146,7 +146,7 @@ export default memo(
<div <div
className={ className={
'flex flex-col grow h-full overflow-auto ' + 'flex flex-col grow h-full overflow-hidden ' +
(loading && (!children?.length || loadingParentChange) ? 'opacity-50 ' : '') (loading && (!children?.length || loadingParentChange) ? 'opacity-50 ' : '')
} }
> >
@@ -167,7 +167,7 @@ export default memo(
</Menu> </Menu>
</div> </div>
<div className="grow"> <div className="grow overflow-auto">
{folders.length > 0 && ( {folders.length > 0 && (
<> <>
<Title className="mb-2 block">Folders</Title> <Title className="mb-2 block">Folders</Title>
@@ -8,7 +8,7 @@ import { PropertiesModalAtom } from './modals/properties';
import { SelectorModalAtom } from './modals/selector'; import { SelectorModalAtom } from './modals/selector';
import { AccessModalAtom } from './modals/update-access'; import { AccessModalAtom } from './modals/update-access';
import { VersionsModalAtom } from './modals/versions'; import { VersionsModalAtom } from './modals/versions';
import { DriveApiClient } from '@features/drive/api-client/api-client'; import { DriveApiClient, getPublicLinkToken } from '@features/drive/api-client/api-client';
import { useDriveActions } from '@features/drive/hooks/use-drive-actions'; import { useDriveActions } from '@features/drive/hooks/use-drive-actions';
import { getPublicLink } from '@features/drive/hooks/use-drive-item'; import { getPublicLink } from '@features/drive/hooks/use-drive-item';
import { useDrivePreview } from '@features/drive/hooks/use-drive-preview'; import { useDrivePreview } from '@features/drive/hooks/use-drive-preview';
@@ -71,8 +71,8 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
}, },
{ {
type: 'menu', type: 'menu',
text: 'Manage access', text: 'Manage access and sharing',
hide: access === 'read', hide: access === 'read' || getPublicLinkToken(),
onClick: () => setAccessModalState({ open: true, id: item.id }), onClick: () => setAccessModalState({ open: true, id: item.id }),
}, },
{ {
@@ -95,7 +95,7 @@ const PathItem = ({
href="#" href="#"
className="text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white" className="text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white"
onClick={evt => { onClick={evt => {
if (first) { if (first && user?.id) {
MenusManager.openMenu( MenusManager.openMenu(
[ [
{ type: 'menu', text: 'Home', onClick: () => onClick('root') }, { type: 'menu', text: 'Home', onClick: () => onClick('root') },
@@ -114,7 +114,7 @@ const PathItem = ({
{item?.access_info?.public?.level && item?.access_info?.public?.level !== 'none' && ( {item?.access_info?.public?.level && item?.access_info?.public?.level !== 'none' && (
<PublicIcon className="h-5 w-5 ml-2" /> <PublicIcon className="h-5 w-5 ml-2" />
)} )}
{first && ( {first && !!user?.id && (
<span className="ml-2 -mr-1 text-gray-700"> <span className="ml-2 -mr-1 text-gray-700">
<ChevronDownIcon className="w-4 h-4" /> <ChevronDownIcon className="w-4 h-4" />
</span> </span>
@@ -41,7 +41,7 @@ export const PublicLinkManager = ({ id, disabled }: { id: string; disabled?: boo
</div> </div>
<div className="shrink-0"> <div className="shrink-0">
<AccessLevel <AccessLevel
hiddenLevels={['manage', 'write']} hiddenLevels={['manage']}
disabled={loading || disabled} disabled={loading || disabled}
level={item?.access_info?.public?.level || null} level={item?.access_info?.public?.level || null}
onChange={level => { onChange={level => {
@@ -6,15 +6,20 @@ import Languages from '@features/global/services/languages-service';
import { addApiUrlIfNeeded } from '@features/global/utils/URLUtils'; import { addApiUrlIfNeeded } from '@features/global/utils/URLUtils';
import RouterService from '@features/router/services/router-service'; import RouterService from '@features/router/services/router-service';
import Drive from '@views/client/body/drive'; import Drive from '@views/client/body/drive';
import { Button } from 'app/atoms/button/button';
import { Input } from 'app/atoms/input/input-text';
import { Base, Subtitle, Title } from 'app/atoms/text';
import UploadsViewer from 'app/components/file-uploads/uploads-viewer';
import { useDriveItem } from 'app/features/drive/hooks/use-drive-item';
import { useDriveUpload } from 'app/features/drive/hooks/use-drive-upload';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import shortUUID from 'short-uuid'; import shortUUID from 'short-uuid';
import Avatar from '../../../../atoms/avatar'; import Avatar from '../../../../atoms/avatar';
import { setPublicLinkToken } from '../../../../features/drive/api-client/api-client'; import { setPublicLinkToken } from '../../../../features/drive/api-client/api-client';
import useRouterCompany from '../../../../features/router/hooks/use-router-company'; import useRouterCompany from '../../../../features/router/hooks/use-router-company';
import { useDriveItem } from 'app/features/drive/hooks/use-drive-item'; import { CreateModalAtom } from './modals/create';
import { Base, Subtitle, Title } from 'app/atoms/text'; import { CreateModalWithUploadZones } from '../../side-bar/actions';
import { Input } from 'app/atoms/input/input-text';
import { Button } from 'app/atoms/button/button';
export default () => { export default () => {
const companyId = useRouterCompany(); const companyId = useRouterCompany();
@@ -71,6 +76,8 @@ export default () => {
</AccessChecker> </AccessChecker>
</div> </div>
<MenusBodyLayer /> <MenusBodyLayer />
<UploadsViewer />
<CreateModalWithUploadZones />
</div> </div>
); );
}; };
@@ -46,7 +46,7 @@ export default React.memo((): JSX.Element => {
<FeatureToggles features={activeFeatureNames}> <FeatureToggles features={activeFeatureNames}>
<> <>
<Header openSideMenu={() => setMenuIsOpen(true)} /> <Header openSideMenu={() => setMenuIsOpen(true)} />
<div className="grow flex flex-row grid-cols-2 sm:px-2 sm:pb-2"> <div className="grow flex flex-row grid-cols-2 sm:px-2 sm:pb-2 overflow-hidden">
<div <div
style={ style={
menuIsOpen menuIsOpen
@@ -12,72 +12,84 @@ import { ConfirmDeleteModalAtom } from '../body/drive/modals/confirm-delete';
import { CreateModal, CreateModalAtom } from '../body/drive/modals/create'; import { CreateModal, CreateModalAtom } from '../body/drive/modals/create';
import { Button } from '@atoms/button/button'; import { Button } from '@atoms/button/button';
export default () => { export const CreateModalWithUploadZones = () => {
const companyId = useRouterCompany(); const companyId = useRouterCompany();
const uploadZoneRef = useRef<UploadZone | null>(null);
const uploadFolderZoneRef = useRef<UploadZone | null>(null);
const setCreationModalState = useSetRecoilState(CreateModalAtom);
const { uploadTree, uploadFromUrl } = useDriveUpload();
const [parentId, _] = useRecoilState(DriveCurrentFolderAtom('root'));
return (
<>
<UploadZone
overClassName={'!hidden'}
className="hidden"
disableClick
parent={''}
multiple={true}
ref={uploadZoneRef}
driveCollectionKey={'side-menu'}
onAddFiles={async (_, event) => {
const tree = await getFilesTree(event);
setCreationModalState({ parent_id: '', open: false });
uploadTree(tree, {
companyId,
parentId,
});
}}
/>
<UploadZone
overClassName={'!hidden'}
className="hidden"
disableClick
parent={''}
multiple={true}
ref={uploadFolderZoneRef}
directory={true}
driveCollectionKey={'side-menu'}
onAddFiles={async (_, event) => {
const tree = await getFilesTree(event);
setCreationModalState({ parent_id: '', open: false });
uploadTree(tree, {
companyId,
parentId,
});
}}
/>
<CreateModal
selectFolderFromDevice={() => uploadFolderZoneRef.current?.open()}
selectFromDevice={() => uploadZoneRef.current?.open()}
addFromUrl={(url, name) => {
setCreationModalState({ parent_id: '', open: false });
uploadFromUrl(url, name, {
companyId,
parentId,
});
}}
/>
</>
);
};
export default () => {
const [parentId, _] = useRecoilState(DriveCurrentFolderAtom('root')); const [parentId, _] = useRecoilState(DriveCurrentFolderAtom('root'));
const { access, item, inTrash } = useDriveItem(parentId); const { access, item, inTrash } = useDriveItem(parentId);
const { children: trashChildren } = useDriveItem('trash'); const { children: trashChildren } = useDriveItem('trash');
const uploadZoneRef = useRef<UploadZone | null>(null); const uploadZoneRef = useRef<UploadZone | null>(null);
const uploadFolderZoneRef = useRef<UploadZone | null>(null);
const setConfirmDeleteModalState = useSetRecoilState(ConfirmDeleteModalAtom); const setConfirmDeleteModalState = useSetRecoilState(ConfirmDeleteModalAtom);
const setCreationModalState = useSetRecoilState(CreateModalAtom); const setCreationModalState = useSetRecoilState(CreateModalAtom);
const { uploadTree, uploadFromUrl } = useDriveUpload();
const openItemModal = useCallback(() => { const openItemModal = useCallback(() => {
if (item?.id) setCreationModalState({ open: true, parent_id: item.id }); if (item?.id) setCreationModalState({ open: true, parent_id: item.id });
}, [item?.id, setCreationModalState]); }, [item?.id, setCreationModalState]);
return ( return (
<div className="-m-4"> <div className="-m-4 overflow-hidden">
<AnimatedHeight> <AnimatedHeight>
<div className="p-4"> <div className="p-4">
<UploadZone <CreateModalWithUploadZones />
overClassName={'!hidden'}
className="hidden"
disableClick
parent={''}
multiple={true}
ref={uploadZoneRef}
driveCollectionKey={'side-menu'}
onAddFiles={async (_, event) => {
const tree = await getFilesTree(event);
setCreationModalState({ parent_id: '', open: false });
uploadTree(tree, {
companyId,
parentId,
});
}}
/>
<UploadZone
overClassName={'!hidden'}
className="hidden"
disableClick
parent={''}
multiple={true}
ref={uploadFolderZoneRef}
directory={true}
driveCollectionKey={'side-menu'}
onAddFiles={async (_, event) => {
const tree = await getFilesTree(event);
setCreationModalState({ parent_id: '', open: false });
uploadTree(tree, {
companyId,
parentId,
});
}}
/>
<CreateModal
selectFolderFromDevice={() => uploadFolderZoneRef.current?.open()}
selectFromDevice={() => uploadZoneRef.current?.open()}
addFromUrl={(url, name) => {
setCreationModalState({ parent_id: '', open: false });
uploadFromUrl(url, name, {
companyId,
parentId,
});
}}
/>
{inTrash && access === 'manage' && ( {inTrash && access === 'manage' && (
<> <>