Skip to content

Commit

Permalink
Link attatchment (#82)
Browse files Browse the repository at this point in the history
* Link attatchment

* Fix lint

* fix

* Update ContextMenu.tsx

* Update ContextMenu.tsx
  • Loading branch information
TheDana1 authored Mar 4, 2024
1 parent 67d8b72 commit 71ded54
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 39 deletions.
22 changes: 21 additions & 1 deletion client/src/components/lib/Canvas.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { MouseEvent, useEffect, useRef, FocusEvent } from 'react';
import { createElement } from '@/lib/canvasElements/canvasElementUtils';
import { FileIcon } from '@radix-ui/react-icons';
import { FileIcon, Link2Icon } from '@radix-ui/react-icons';
import {
adjustElementCoordinatesById,
rescalePointsInElem,
Expand Down Expand Up @@ -102,6 +102,7 @@ export default function Canvas() {
isSelectionFrameSet,
toolOptions,
attachedFileUrls,
attachedUrls,
} = useCanvasElementStore([
'addCanvasShape',
'addCanvasFreehand',
Expand Down Expand Up @@ -134,6 +135,7 @@ export default function Canvas() {
'isSelectionFrameSet',
'toolOptions',
'attachedFileUrls',
'attachedUrls',
]);

const { socket, setWebsocketAction, setRoomID, setTenants, clearTenants } =
Expand Down Expand Up @@ -749,6 +751,24 @@ export default function Canvas() {
</a>
)}

{tool === 'select' &&
attachedUrls[selectedElementIds[0]] !== undefined && (
<a
href={attachedUrls[selectedElementIds[0]]}
target="_blank"
rel="noopener noreferrer"
style={{
position: 'absolute',
bottom: 0,
left: '50%',
transform: 'translateX(-50%)',
zIndex: 10,
}}
>
<Link2Icon />
</a>
)}

{action === 'writing' && (
<textarea
ref={textAreaRef}
Expand Down
67 changes: 48 additions & 19 deletions client/src/components/lib/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ContextMenuItem from './ContextMenuItem';
import { useWebSocketStore } from '@/stores/WebSocketStore';
import StableDiffusionContextItem from './StableDiffusion/StableDiffusionContextItem';
import FileUpload from './UploadFilesToFirebase';
import LinkAttach from './UploadLink';
import deleteFilefromFirebase from './DeleteFileFromFirebase';

/**
Expand All @@ -22,14 +23,18 @@ const ContextMenu = () => {
selectedElementIds,
pushCanvasHistory,
removeAttachedFileUrl,
removeAttachedUrl,
attachedFileUrls,
attachedUrls,
} = useCanvasElementStore([
'removeCanvasElements',
'setSelectedElements',
'selectedElementIds',
'pushCanvasHistory',
'removeAttachedFileUrl',
'removeAttachedUrl',
'attachedFileUrls',
'attachedUrls',
]);

const { setWebsocketAction } = useWebSocketStore(['setWebsocketAction']);
Expand Down Expand Up @@ -62,26 +67,50 @@ const ContextMenu = () => {
</ContextMenuItem>
<ExportSelectedPNGContextItem />
<StableDiffusionContextItem />
<FileUpload />

<ContextMenuItem
onClick={() => {
const ids = selectedElementIds;
setSelectedElements([]);
const link = attachedFileUrls[selectedElementIds[0]];
if (link !== undefined) {
deleteFilefromFirebase(link);
}
removeAttachedFileUrl(ids); //Don't want users to be able to undo deletion
setWebsocketAction(ids, 'removeAttachedFileUrl');
}}
className="text-red-700"
>
Delete File{' '}
<div className="ml-auto pl-5 text-red-700 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
<TrashIcon />
</div>
</ContextMenuItem>
{attachedUrls[selectedElementIds[0]] === undefined && (
<FileUpload />
)}
{attachedUrls[selectedElementIds[0]] === undefined && (
<ContextMenuItem
onClick={() => {
const ids = selectedElementIds;
setSelectedElements([]);
const link = attachedFileUrls[selectedElementIds[0]];
if (link !== undefined) {
deleteFilefromFirebase(link);
}
removeAttachedFileUrl(ids); //Don't want users to be able to undo deletion
setWebsocketAction(ids, 'removeAttachedFileUrl');
}}
className="text-red-700"
>
Delete Attached File{' '}
<div className="ml-auto pl-5 text-red-700 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
<TrashIcon />
</div>
</ContextMenuItem>
)}

{attachedFileUrls[selectedElementIds[0]] === undefined && (
<LinkAttach />
)}
{attachedFileUrls[selectedElementIds[0]] === undefined && (
<ContextMenuItem
onClick={() => {
const ids = selectedElementIds;
setSelectedElements([]);
removeAttachedUrl(ids);
setWebsocketAction(ids, 'removeAttachedUrl');
}}
className="text-red-700"
>
Delete Attatched Link{' '}
<div className="ml-auto pl-5 text-red-700 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
<TrashIcon />
</div>
</ContextMenuItem>
)}
</>
) : null}
</RadixContextMenu.Content>
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/lib/DeleteFileFromFirebase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ function deleteFilefromFirebase(downloadURL: string) {
const fileRef = ref(storage, filePath);
deleteObject(fileRef)
.then(() => {
console.log('File deleted successfully'); //Will replace with toast next sprint
console.log('File deleted successfully');
})
.catch((error) => {
console.error('Error deleting file: ', error); //Will replace with toast next sprint
console.error('Error deleting file: ', error);
});
}

Expand Down
45 changes: 29 additions & 16 deletions client/src/components/lib/UploadFilesToFirebase.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import React from 'react';
import React, { useRef } from 'react';
import { getDownloadURL, getStorage, ref, uploadBytes } from 'firebase/storage';
import { firebaseApp } from '../../firebaseDB/firebase';
import { useCanvasElementStore } from '@/stores/CanvasElementsStore';
import { useWebSocketStore } from '@/stores/WebSocketStore';
import { useToast } from '@/components/ui/use-toast';

/**
* Defines a context menu option that allows users to attatch a file to a
* canvas element by uploading it to firebase
* @author Dana El Sherif
*/
const FileUpload = () => {
const { selectedElementIds, updateAttachedFileUrl } = useCanvasElementStore([
'selectedElementIds',
'updateAttachedFileUrl',
]);
const { setWebsocketAction } = useWebSocketStore(['setWebsocketAction']);
const fileInputRef = useRef<HTMLInputElement>(null);
const { toast } = useToast();

const onFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = event.target.files;
Expand All @@ -25,25 +21,42 @@ const FileUpload = () => {
const storageRef = ref(storage, `uploads/${fileToUpload.name}`);
uploadBytes(storageRef, fileToUpload)
.then((snapshot) => {
alert('File uploaded successfully');
toast({
title: 'Success',
description: 'File uploaded successfully',
});
return getDownloadURL(snapshot.ref);
})
.then((downloadURL) => {
updateAttachedFileUrl(selectedElementIds[0], downloadURL);
setWebsocketAction(
{ selectedElementIds, downloadURL },
'addAttachedFileUrl',
);
})
.catch(() => {
alert('Error uploading');
toast({
title: 'Error',
description: 'Error uploading file',
});
});
}
};

const triggerFileInput = () => {
fileInputRef.current?.click();
};

return (
<div className="ml-auto pl-5 text-violet-500 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
<input type="file" onChange={onFileChange} />
<div className="ml-auto pl-6 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
<input
type="file"
onChange={onFileChange}
ref={fileInputRef}
style={{ display: 'none' }}
/>
<span
onClick={triggerFileInput}
className="cursor-pointer text-[13px] leading-none text-violet-500"
>
Choose File
</span>
</div>
);
};
Expand Down
41 changes: 41 additions & 0 deletions client/src/components/lib/UploadLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import { useCanvasElementStore } from '@/stores/CanvasElementsStore';
import { useToast } from '@/components/ui/use-toast';
/**
* Component that allows users to attach a link to a canvas element.
* Modified to prompt for link input on button click.
* @author Dana El Sherif
*/
const LinkAttach = () => {
const { selectedElementIds, updateAttachedUrl } = useCanvasElementStore([
'selectedElementIds',
'updateAttachedUrl',
]);
const { toast } = useToast();

const attachLink = () => {
if (selectedElementIds.length > 0) {
const link = window.prompt('Enter link:');
if (link !== null && link !== '') {
updateAttachedUrl(selectedElementIds[0], link);
toast({
title: 'Link Attached',
description: 'Link has been successfully attached.',
});
}
}
};

return (
<div className="ml-auto pl-6 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8">
<button
onClick={attachLink}
className="cursor-pointer text-[13px] leading-none text-violet-500"
>
Attach Link
</button>
</div>
);
};

export default LinkAttach;
Loading

0 comments on commit 71ded54

Please sign in to comment.