Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pluto ux process file drop #707

Merged
merged 33 commits into from
Dec 30, 2020
Merged
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
70522da
Add Support for file drag & drop
Nov 22, 2020
05ad525
Interoperability with Drop Ruler
Nov 22, 2020
8a48f6b
Save file and get path in the front-end
Nov 23, 2020
0fb00ea
Working POC
Nov 23, 2020
b5f5ea0
Add 'raw' before path string literals
Nov 24, 2020
7ca9d56
Move code templates to Julia
Nov 24, 2020
773eda8
Change Image sample to use Images
Nov 24, 2020
88820f9
Use TableIOInterface for Files -> Table conversions
Nov 25, 2020
5fb007c
Move drop handler out of Cell.js
Nov 25, 2020
e708577
Update the code for images & text to use let block
Nov 25, 2020
43e8fd1
Make file transfer !!!25%!!! FASTER dropping base64 usage
Nov 25, 2020
e2eb8fb
Use correct is_extension_supported
Nov 26, 2020
7f21590
Handle non-empty cells
Nov 27, 2020
21941bd
Rename hook, var for debounce ms
Nov 29, 2020
54a5f0f
Prevent error message while saving
Nov 30, 2020
7c64753
Make JavaScripty names more Plutonic
Nov 30, 2020
4da1b55
update the handling of Juia files
Nov 30, 2020
de9c46c
Move assets folder along with notebook
Dec 3, 2020
2c286b3
Don't run file event when moving cells
Dec 5, 2020
218f21f
Save files with the same name with incremental numbers
Dec 5, 2020
d2706cd
make warning message less aggressive
Dec 5, 2020
81d81be
🦆 Fix expression equality again
fonsp Dec 9, 2020
fdf8bcb
Revert "🦆 Fix expression equality again"
fonsp Dec 9, 2020
ad61515
Fix generated path code by lungben
Dec 11, 2020
dbd4f3b
Add statistics
Dec 11, 2020
e62adb5
Merge branch 'master' of github.com:fonsp/Pluto.jl into PlutoUX-Proce…
Dec 22, 2020
62aaee4
Benjamin's compat suggestion
Dec 22, 2020
15b3dfd
Merge branch 'master' of github.com:fonsp/Pluto.jl into PlutoUX-Proce…
Dec 30, 2020
e2527a3
Use the latest dralbase
Dec 30, 2020
20c0c8c
fix 1 bug, handle drops on body
Dec 30, 2020
4c5e9e7
fix typo
Dec 30, 2020
c0d5e0b
Fix frontend tests thanks)
Dec 30, 2020
7c69e92
don't change whitespace!
Dec 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use the latest dralbase
Also, we now support file-drops to cells with code (adds cell after it)
Panagiotis Georgakopoulos committed Dec 30, 2020
commit e2527a37c4d85954b682112e6b5cb47876e0d6de
6 changes: 2 additions & 4 deletions frontend/components/Cell.js
Original file line number Diff line number Diff line change
@@ -37,9 +37,8 @@ export const Cell = ({

// cm_forced_focus is null, except when a line needs to be highlighted because it is part of a stack trace
const [cm_forced_focus, set_cm_forced_focus] = useState(null)
const { saving_file, drag_active, event_handler, inactive_handler } = useDropHandler(pluto_actions, on_change, cell_id)
const has_code = code?.length > 0
const handler = !has_code ? event_handler : inactive_handler
const { saving_file, drag_active, event_handler } = useDropHandler()
const handler = event_handler
const localTimeRunning = 10e5 * useMillisSinceTruthy(running)
useEffect(() => {
const focusListener = (e) => {
@@ -81,7 +80,6 @@ export const Cell = ({
code_differs: class_code_differs,
code_folded: class_code_folded,
drop_target: drag_active,
drop_invalid: has_code && !saving_file,
saving_file: saving_file,
})}
id=${cell_id}
10 changes: 2 additions & 8 deletions frontend/components/CellInput.js
Original file line number Diff line number Diff line change
@@ -50,7 +50,8 @@ export const CellInput = ({
on_change,
on_update_doc_query,
on_focus_neighbor,
on_drag_drop_events, cell_id,
on_drag_drop_events,
cell_id,
notebook_id,
}) => {
let pluto_actions = useContext(PlutoContext)
@@ -454,17 +455,10 @@ export const CellInput = ({
// }
// }, [remote_code.timestamp])


useEffect(() => {
cm_ref.current.options.disableInput = disable_input
}, [disable_input])

useEffect(() => {
if (cm_ref.current.value !== local_code) {
cm_ref.current.setValue(remote_code)
}
}, [local_code, remote_code])

useEffect(() => {
if (cm_ref.current.value !== local_code && local_code === remote_code) {
cm_ref.current.setValue(remote_code)
19 changes: 10 additions & 9 deletions frontend/components/Editor.js
Original file line number Diff line number Diff line change
@@ -141,14 +141,15 @@ export class Editor extends Component {
send: (...args) => this.client.send(...args),
update_notebook: (...args) => this.update_notebook(...args),
set_doc_query: (query) => this.setState({ desired_doc_query: query }),
set_local_cell: (cell_id, new_val) => {
this.setState(
set_local_cell: (cell_id, new_val, callback) => {
return this.setState(
immer((state) => {
state.cell_inputs_local[cell_id] = {
code: new_val,
}
state.selected_cells = []
})
}),
callback
)
},
focus_on_neighbor: (cell_id, delta, line = delta === -1 ? Infinity : -1, ch) => {
@@ -286,24 +287,24 @@ export class Editor extends Component {
notebook.cell_order = [...before, ...cell_ids, ...after]
})
},
add_remote_cell_at: async (index) => {
add_remote_cell_at: async (index, code = "") => {
let id = uuidv4()
this.setState({ last_created_cell: id })
await update_notebook((notebook) => {
notebook.cell_inputs[id] = {
cell_id: id,
code: "",
code,
code_folded: false,
}
notebook.cell_order = [...notebook.cell_order.slice(0, index), id, ...notebook.cell_order.slice(index, Infinity)]
})
await this.client.send("run_multiple_cells", { cells: [id] }, { notebook_id: this.state.notebook.notebook_id })
return id
},
add_remote_cell: async (cell_id, before_or_after) => {
add_remote_cell: async (cell_id, before_or_after, code) => {
const index = this.state.notebook.cell_order.indexOf(cell_id)
const delta = before_or_after == "before" ? 0 : 1

await this.actions.add_remote_cell_at(index + delta)
return await this.actions.add_remote_cell_at(index + delta, code)
},
confirm_delete_multiple: async (verb, cell_ids) => {
if (cell_ids.length <= 1 || confirm(`${verb} ${cell_ids.length} cells?`)) {
@@ -408,7 +409,7 @@ export class Editor extends Component {
},
true
)
}
},
}

// these are update message that are _not_ a response to a `send(*, *, {create_promise: true})`
58 changes: 23 additions & 35 deletions frontend/components/useDropHandler.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState, useMemo } from "../imports/Preact.js"
import { PlutoContext } from "../common/PlutoContext.js"
import { useState, useMemo, useContext } from "../imports/Preact.js"

const MAGIC_TIMEOUT = 500
const DEBOUNCE_MAGIC_MS = 250
@@ -15,44 +16,21 @@ const prepareFile = (file) =>
fr.readAsArrayBuffer(file)
})

export const useDropHandler = (requests, on_change, cell_id) => {
export const useDropHandler = () => {
let pluto_actions = useContext(PlutoContext)
const [saving_file, set_saving_file] = useState(false)
const [drag_active, set_drag_active_fast] = useState(false)
const set_drag_active = useMemo(() => _.debounce(set_drag_active_fast, DEBOUNCE_MAGIC_MS), [set_drag_active_fast])
const inactive_handler = useMemo(
() => (ev) => {
if (ev.dataTransfer.types[0] === "text/pluto-cell") return
switch (ev.type) {
case "drop":
ev.preventDefault() // don't file open
break
case "dragover":
ev.preventDefault()
ev.dataTransfer.dropEffect = "none"
set_drag_active(true)
setTimeout(() => set_drag_active(false), MAGIC_TIMEOUT)
break
case "dragenter":
set_drag_active_fast(true)
break
case "dragleave":
set_drag_active(false)
break
default:
break
}
},
[set_drag_active, set_drag_active_fast]
)
const event_handler = useMemo(() => {
const uploadAndCreateCodeTemplate = async (file) => {

const eventFactory = useMemo(() => {
const uploadAndCreateCodeTemplate = async (file, drop_cell_id) => {
if (!(file instanceof File)) return " # File can't be read"
set_saving_file(true)
const {
message: { success, code },
} = await prepareFile(file).then(
(preparedObj) => {
return requests.write_file(cell_id, preparedObj)
return pluto_actions.write_file(drop_cell_id, preparedObj)
},
() => alert("Pluto can't save this file 😥")
)
@@ -73,14 +51,23 @@ export const useDropHandler = (requests, on_change, cell_id) => {
case "cmdrop":
case "drop":
ev.preventDefault() // don't file open
const cell_element = ev.path.find((el) => el.tagName === "PLUTO-CELL")
const drop_cell_id = cell_element?.id || document.querySelector("pluto-cell:last-child")?.id
const drop_cell_value = cell_element?.querySelector(".CodeMirror")?.CodeMirror?.getValue()
console.log(cell_element, drop_cell_id, drop_cell_value)
set_drag_active(false)
if (!ev.dataTransfer.files.length) {
return
}
uploadAndCreateCodeTemplate(ev.dataTransfer.files[0]).then((code) => {
uploadAndCreateCodeTemplate(ev.dataTransfer.files[0], drop_cell_id).then((code) => {
if (code) {
on_change(code)
requests.change_remote_cell(cell_id, code)
if (drop_cell_value?.length > 0) {
console.log("Add remote cell", code)
pluto_actions.add_remote_cell(drop_cell_id, "after", code)
} else {
console.log("update this cell", drop_cell_id, code)
pluto_actions.set_local_cell(drop_cell_id, code, () => pluto_actions.set_and_run_multiple([drop_cell_id]))
}
}
})
break
@@ -99,7 +86,8 @@ export const useDropHandler = (requests, on_change, cell_id) => {
default:
}
}
}, [set_drag_active, set_drag_active_fast, set_saving_file, requests, cell_id, on_change])

}, [set_drag_active, set_drag_active_fast, set_saving_file, pluto_actions])
const event_handler = eventFactory
const inactive_handler = eventFactory
return { saving_file, drag_active, event_handler, inactive_handler }
}
16 changes: 8 additions & 8 deletions src/webserver/Dynamic.jl
Original file line number Diff line number Diff line change
@@ -369,35 +369,35 @@ responses[:reshow_cell] = (session::ServerSession, body, notebook::Notebook, cel
putnotebookupdates!(session, notebook, clientupdate_cell_output(notebook, cell))
end

responses[:write_file] = (session::ServerSession, body, notebook::Notebook, cell::Cell; initiator::Union{Initiator,Missing}=missing) -> let
path = notebook.path
responses[:write_file] = function (🙋::ClientRequest)
path = 🙋.notebook.path
reldir = "$(path |> basename).assets"
dir = joinpath(path |> dirname, reldir)
file_noext = reduce(*, split(body["name"], ".")[1:end - 1])
extension = split(body["name"], ".")[end]
file_noext = reduce(*, split(🙋.body["name"], ".")[1:end - 1])
extension = split(🙋.body["name"], ".")[end]
save_path = numbered_until_new(joinpath(dir, file_noext); sep=" ", suffix=".$(extension)", create_file=false)

if !ispath(dir)
mkpath(dir)
end
success = try
io = open(save_path, "w")
write(io, body["file"])
write(io, 🙋.body["file"])
close(io)
true
catch e
false
end

code = get_template_code(basename(save_path), reldir, body["file"])
code = get_template_code(basename(save_path), reldir, 🙋.body["file"])

msg = UpdateMessage(:write_file_reply,
Dict(
:success => success,
:code => code
), notebook, nothing, initiator)
), 🙋.notebook, nothing, 🙋.initiator)

putclientupdates!(session, initiator, msg)
putclientupdates!(🙋.session, 🙋.initiator, msg)
end

get_template_code = (filename, directory, iofilecontents) -> begin