⭐️ 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:
@@ -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>
|
||||||
|
|||||||
+1
-1
@@ -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' && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user