This repository has been archived by the owner on Jan 20, 2022. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 589
feat: Attachment of Files to Documents using S3. #280
Open
chanchadsahil7
wants to merge
7
commits into
outline:main
Choose a base branch
from
chanchadsahil7:develop
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
bc94f6f
First draft for files upload to S3
29e77ce
All file Type support without hendering Image.
cec2dfd
Added fildoc node similar to image node
cc1a066
Merge pull request #1 from chanchadsahil7/sahil/full-fledge-file-upload
chanchadsahil7 49a0b72
Using mark link for adding files in document
f305d33
removed createlink for removing creation of doc.
bd2971d
Minor Code review changes
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { ToastType } from "../types"; | ||
|
||
const insertAllFiles = function(view, event, files, options) { | ||
if (files.length === 0) return; | ||
|
||
const { | ||
dictionary, | ||
uploadFile, | ||
onFileUploadStart, | ||
onFileUploadStop, | ||
onShowToast, | ||
} = options; | ||
|
||
if (!uploadFile) { | ||
console.warn("uploadFile callback must be defined to handle file uploads."); | ||
return; | ||
} | ||
|
||
// okay, we have some dropped files and a handler – lets stop this | ||
// event going any further up the stack | ||
event.preventDefault(); | ||
|
||
// let the user know we're starting to process the files | ||
if (onFileUploadStart) onFileUploadStart(); | ||
|
||
// we'll use this to track of how many files have succeeded or failed | ||
let complete = 0; | ||
|
||
const { state } = view; | ||
const { from, to } = state.selection; | ||
|
||
// the user might have dropped multiple files at once, we need to loop | ||
for (const file of files) { | ||
// start uploading the file file to the server. Using "then" syntax | ||
// to allow all placeholders to be entered at once with the uploads | ||
// happening in the background in parallel. | ||
uploadFile(file) | ||
.then(src => { | ||
const title = file.name; | ||
const href = src; | ||
|
||
view.dispatch( | ||
view.state.tr | ||
.insertText(title, from, to) | ||
.addMark( | ||
from, | ||
to + title.length, | ||
state.schema.marks.link.create({ href }) | ||
) | ||
); | ||
}) | ||
.catch(error => { | ||
console.error(error); | ||
if (onShowToast) { | ||
onShowToast(dictionary.fileUploadError, ToastType.Error); | ||
} | ||
}) | ||
// eslint-disable-next-line no-loop-func | ||
.finally(() => { | ||
complete++; | ||
|
||
// once everything is done, let the user know | ||
if (complete === files.length) { | ||
if (onFileUploadStop) onFileUploadStop(); | ||
} | ||
}); | ||
} | ||
}; | ||
|
||
export default insertAllFiles; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import Input from "./Input"; | |
import VisuallyHidden from "./VisuallyHidden"; | ||
import getDataTransferFiles from "../lib/getDataTransferFiles"; | ||
import insertFiles from "../commands/insertFiles"; | ||
import insertAllFiles from "../commands/insertAllFiles"; | ||
import getMenuItems from "../menus/block"; | ||
import baseDictionary from "../dictionary"; | ||
|
||
|
@@ -22,8 +23,11 @@ type Props = { | |
view: EditorView; | ||
search: string; | ||
uploadImage?: (file: File) => Promise<string>; | ||
uploadFile?: (file: File) => Promise<string>; | ||
onImageUploadStart?: () => void; | ||
onImageUploadStop?: () => void; | ||
onFileUploadStart?: () => void; | ||
onFileUploadStop?: () => void; | ||
onShowToast?: (message: string, id: string) => void; | ||
onLinkToolbarOpen: () => void; | ||
onClose: () => void; | ||
|
@@ -42,6 +46,7 @@ type State = { | |
class BlockMenu extends React.Component<Props, State> { | ||
menuRef = React.createRef<HTMLDivElement>(); | ||
inputRef = React.createRef<HTMLInputElement>(); | ||
fileInputRef = React.createRef<HTMLInputElement>(); | ||
|
||
state: State = { | ||
left: -1000, | ||
|
@@ -154,6 +159,8 @@ class BlockMenu extends React.Component<Props, State> { | |
switch (item.name) { | ||
case "image": | ||
return this.triggerImagePick(); | ||
case "file": | ||
return this.triggerFilePick(); | ||
case "embed": | ||
return this.triggerLinkInput(item); | ||
case "link": { | ||
|
@@ -235,6 +242,12 @@ class BlockMenu extends React.Component<Props, State> { | |
} | ||
}; | ||
|
||
triggerFilePick = () => { | ||
if (this.fileInputRef.current) { | ||
this.fileInputRef.current.click(); | ||
} | ||
}; | ||
|
||
triggerLinkInput = item => { | ||
this.setState({ insertItem: item }); | ||
}; | ||
|
@@ -273,6 +286,32 @@ class BlockMenu extends React.Component<Props, State> { | |
this.props.onClose(); | ||
}; | ||
|
||
handleFilePicked = event => { | ||
const files = getDataTransferFiles(event); | ||
|
||
const { | ||
view, | ||
uploadFile, | ||
onFileUploadStart, | ||
onFileUploadStop, | ||
onShowToast, | ||
} = this.props; | ||
const { state } = view; | ||
const parent = findParentNode(node => !!node)(state.selection); | ||
|
||
if (parent) { | ||
insertAllFiles(view, event, files, { | ||
uploadFile, | ||
onFileUploadStart, | ||
onFileUploadStop, | ||
onShowToast, | ||
dictionary: this.props.dictionary, | ||
}); | ||
} | ||
|
||
this.props.onClose(); | ||
}; | ||
|
||
clearSearch() { | ||
const { state, dispatch } = this.props.view; | ||
const parent = findParentNode(node => !!node)(state.selection); | ||
|
@@ -344,7 +383,13 @@ class BlockMenu extends React.Component<Props, State> { | |
} | ||
|
||
get filtered() { | ||
const { dictionary, embeds, search = "", uploadImage } = this.props; | ||
const { | ||
dictionary, | ||
embeds, | ||
search = "", | ||
uploadImage, | ||
uploadFile, | ||
} = this.props; | ||
let items: (EmbedDescriptor | MenuItem)[] = getMenuItems(dictionary); | ||
const embedItems: EmbedDescriptor[] = []; | ||
|
||
|
@@ -370,6 +415,9 @@ class BlockMenu extends React.Component<Props, State> { | |
// If no image upload callback has been passed, filter the image block out | ||
if (!uploadImage && item.name === "image") return false; | ||
|
||
// If no file upload callback has been passed, filter the file block out | ||
if (!uploadFile && item.name === "file") return false; | ||
|
||
const n = search.toLowerCase(); | ||
return ( | ||
(item.title || "").toLowerCase().includes(n) || | ||
|
@@ -399,7 +447,7 @@ class BlockMenu extends React.Component<Props, State> { | |
} | ||
|
||
render() { | ||
const { dictionary, isActive, uploadImage } = this.props; | ||
const { dictionary, isActive, uploadImage, uploadFile } = this.props; | ||
const items = this.filtered; | ||
const { insertItem, ...positioning } = this.state; | ||
|
||
|
@@ -470,6 +518,16 @@ class BlockMenu extends React.Component<Props, State> { | |
/> | ||
</VisuallyHidden> | ||
)} | ||
{uploadFile && ( | ||
<VisuallyHidden> | ||
<input | ||
type="file" | ||
ref={this.fileInputRef} | ||
onChange={this.handleFilePicked} | ||
accept="*" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense to make this a prop There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are you talking about the default accept prop? |
||
/> | ||
</VisuallyHidden> | ||
)} | ||
</Wrapper> | ||
</Portal> | ||
); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -107,6 +107,12 @@ export default function blockMenuItems( | |
icon: ImageIcon, | ||
keywords: "picture photo", | ||
}, | ||
{ | ||
name: "file", | ||
title: "File", | ||
icon: ImageIcon, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Going to need a unique icon here – there isn't a simple "file" icon in the set, but I can get one added. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes please get it added will use the same Icon here |
||
keywords: "doc pdf", | ||
}, | ||
{ | ||
name: "link", | ||
title: dictionary.link, | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The delay between the user starting the upload and the files being inserted will result in strange and potentially broken scenarios – the document content can have changed substantially in that time.
With the image extension a placeholder widget is used to hold the position for this reason, but a better comparison might be the logic when creating a document from the link menu – a "placeholder" link is created and then updated when the document has been successfully created. A similar approach seems necessary here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let me know if I got this right:
You are suggesting that as soon as a user selects a file using the menu option you want me to insert a place holder to the document and save it. And then start the file upload process. Once the file is uploaded just replace the link.
is that what you are suggesting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, here's the existing code:
rich-markdown-editor/src/components/LinkToolbar.tsx
Lines 71 to 88 in 7b03109
A "placeholder" link is created with a temporary url while the document is being created. Once it's uploaded the href is swapped out. We could also target the temporary link in css to maybe fade it out or similar while the upload is happening.