Skip to content

Commit

Permalink
finish updating upload logic
Browse files Browse the repository at this point in the history
  • Loading branch information
leafo committed Jun 1, 2024
1 parent cdec359 commit 149488b
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 62 deletions.
5 changes: 1 addition & 4 deletions static/coffee/main/edit_submission.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
import $ from "main/jquery"

import "main/react/edit_submission"
import {Upload} from "main/upload"

import {createRoot} from "react-dom/client"

import {Uploader} from "main/react/edit_submission"

export class EditSubmission
Expand All @@ -25,8 +23,7 @@ export class EditSubmission
setup_uploader: =>
container = @el.find ".file_uploader"
createRoot(container[0]).render Uploader {
uploads: @opts.uploads && (new Upload(u) for u in @opts.uploads)
uploads: @opts.uploads
uploader_opts: @opts.uploader_opts
widget: @
}

81 changes: 61 additions & 20 deletions static/coffee/main/react/edit_submission.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {R, fragment, classNames} from "./_react"

import * as React from 'react'
import * as ReactDOM from 'react-dom'
import {div, input, textarea, button, span, img, p, dialog, h2} from 'react-dom-factories'
import {div, input, textarea, button, span, img, p, dialog, h2, strong} from 'react-dom-factories'

import $ from "main/jquery"
import {with_markdown, is_mobile, format_bytes, with_csrf} from "main/util"
Expand Down Expand Up @@ -191,19 +191,18 @@ export PastedFileDialog = P "PastedFileDialog", {

export Uploader = P "Uploader", {
getInitialState: ->
initial_uploads = @props.uploads || []

{
uploads: @props.uploads || []
uploads: initial_uploads.map (u) => new Upload u
upload_manager: new UploadManager @props.uploader_opts
}

push_upload: (upload) ->
@setState {
uploads: @state.uploads.concat(upload)
@setState (state) => {
uploads: state.uploads.concat(upload)
}

# TODO: remove the use of forceUpdate
upload.on_update = => @forceUpdate()

handle_upload: (e) ->
e.preventDefault()
@state.upload_manager.pick_files @push_upload
Expand Down Expand Up @@ -268,7 +267,7 @@ export Uploader = P "Uploader", {
file: @state.pasted_file

on_accept: =>
@state.upload_manager.upload_file @state.pasted_file, @push_upload
@push_upload @state.upload_manager.upload_file @state.pasted_file
@setState {
pasted_file: null
}
Expand Down Expand Up @@ -310,7 +309,7 @@ export Uploader = P "Uploader", {
@setState dragging_over: false

for file in e.dataTransfer?.files
@state.upload_manager.upload_file file, @push_upload
@push_upload @state.upload_manager.upload_file file
},
P.UploadList { uploads: @state.uploads }

Expand All @@ -325,7 +324,7 @@ export Uploader = P "Uploader", {
}, "Add file(s)"

unless is_mobile()
p className: "upload_tip", "TIP: you can also drag and drop a file(s) here to upload"
p className: "upload_tip", "TIP: you can also drag and drop a file(s) or paste an image to upload"
}

export UploadList = P "UploadList", {
Expand All @@ -335,7 +334,7 @@ export UploadList = P "UploadList", {
render_uploads: ->
@props.uploads.map (upload, idx) =>
P.UploadRow {
key: upload.data.id
key: upload._key
upload: upload
position: idx
first: idx == 0
Expand All @@ -344,6 +343,44 @@ export UploadList = P "UploadList", {
}

export UploadRow = P "UploadRow", {
getInitialState: ->
{
status: if @props.upload.data.ready
"ready"
else
"pending"
}

componentDidMount: ->
@props.upload.upload_deferred.progress (event, args...) =>
switch event
when "start_upload"
@setState {
status: "uploading"
progress_percent: 0
}
when "finsh_upload"
@setState {
progress_percent: 100
}
when "progress"
[loaded, total] = args
@setState {
progress_percent: loaded / total * 100
}

@props.upload.upload_deferred.done (res...) =>
@setState {
just_uploaded: true
status: "ready"
}

@props.upload.upload_deferred.fail (err) =>
if err.errors
@setState {
errors: err.errors
}

handle_delete: (e) ->
e.preventDefault()
if confirm "Are you sure you want to remove this file? This can not be undone."
Expand All @@ -358,7 +395,7 @@ export UploadRow = P "UploadRow", {
@trigger "upload:move_down", @props.position

render: ->
upload_tools = unless @props.upload.uploading
upload_tools = if @state.status == "ready"
div className: "upload_tools",
unless @props.first and @props.last
button {
Expand All @@ -378,18 +415,22 @@ export UploadRow = P "UploadRow", {
title: "Move down"
}, ArrowDownIcon(), span className: "screenreader_only", "Move down"


upload_status = if msg = @props.upload.current_error
div className: "upload_error", msg
else if @props.upload.success
upload_status = if msg = @state.errors
div className: "upload_error",
strong {}, "Upload failed:"
" "
@state.errors
else if @state.just_uploaded
div className: "upload_success", "Success"
else if @props.upload.uploading
progress = @props.upload.progress_percent || 0
else if @state.status == "uploading"
progress = @state.progress_percent || 0
div className: "upload_progress",
div className: "upload_progress_inner", style: { width: "#{progress}%" }

div className: "file_upload",
input type: "hidden", name: "upload[#{@props.upload.data.id}][position]", value: "#{@props.position}"
if id = @props.upload.data.id
input type: "hidden", name: "upload[#{id}][position]", value: "#{@props.position}"

upload_tools

div {},
Expand All @@ -398,7 +439,7 @@ export UploadRow = P "UploadRow", {
span className: "file_size", "(#{format_bytes @props.upload.data.size})"
upload_status

unless @props.upload.uploading
if @state.status == "ready"
div className: "upload_tools",
button { type: "button", className: "delete_btn", onClick: @handle_delete }, "Delete"
}
Expand Down
76 changes: 39 additions & 37 deletions static/coffee/main/upload.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export create_video_thumbnail = (video) ->
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)

data_url = canvas.toDataURL("image/jpeg", 0.6)
console.log data_url

{
data_url
Expand Down Expand Up @@ -146,13 +145,18 @@ export xhr_upload = (file, opts) ->


export class Upload
next_id = 0

constructor: (@data) ->
# unique identifier for react
@_key = "upload_#{next_id += 1}"
@upload_deferred = $.Deferred()

start_upload: (upload_url, save_url) ->
throw "missing file" unless @data.file

@uploading = true
@on_update?()
@notify "start_upload"

d = wrap_errors $.when(upload_url).then (action, upload_params) =>
xhr_upload(@data.file, {
Expand All @@ -161,10 +165,11 @@ export class Upload
})
.progress (status, loaded, total) =>
if status == "progress"
@progress? loaded, total
@notify "progress", loaded, total

.then (upload_result) =>
@notify "finish_upload"
@uploading = false
@on_update?()

if save_url
$.when(save_url).then (url, params) =>
Expand All @@ -179,25 +184,18 @@ export class Upload

d.fail (error) =>
@uploading = false
@on_update?()
@set_error error

d.then (res) =>
@save_upload res
res

progress: (loaded, total) =>
@progress_percent = loaded/total * 100
@on_update?()
@current_error = error
@upload_deferred.reject error

save_upload: (res) =>
@success = true
@on_update?()
d.done (res) =>
@success = true
@upload_deferred.resolve res

set_error: (msg) =>
@current_error = msg
@on_update?()
d

# notify the upload deferred of any in-progress updates
notify: (args...) =>
@upload_deferred.notify args...

# manages the lifecycle of uploads
export class UploadManager
Expand All @@ -216,7 +214,7 @@ export class UploadManager

@input.on "change", =>
if file = @input[0].files[0]
d.resolve @upload_file file, on_upload
upload = @upload_file file

@input.click()

Expand All @@ -231,21 +229,31 @@ export class UploadManager

@input.on "change", =>
for file in @input[0].files
@upload_file file, on_upload
upload = @upload_file file
on_upload? upload

@input.click()

# returns a deferred representing the upload
# on_upload: called when the Upload object is created and started
upload_file: (file, on_upload) ->
upload_file: (file) ->
throw "missing prepare url" unless @opts.prepare_url

upload = new Upload {
filename: file.name
size: file.size
type: file.type
file: file
}

data = with_csrf {
"upload[filename]": file.name
"upload[size]": file.size
}

d = get_image_dimensions(file).then (width, height, thumbnail) =>
save_upload = $.Deferred()

prepare_upload = get_image_dimensions(file).then (width, height, thumbnail) =>
max_size = @opts.max_size
if max_size? and file.size > max_size
return $.Deferred().reject "#{file.name} is greater than max size of #{format_bytes max_size}"
Expand All @@ -254,13 +262,9 @@ export class UploadManager
if res.errors
return $.Deferred().reject res

upload = new Upload {
filename: file.name
size: file.size
type: file.type
file: file
id: res.id
}
# there should be a better place for this
upload.data.id = res.id
upload.on_update?()

save_params = {
width, height
Expand All @@ -271,12 +275,10 @@ export class UploadManager
save_params["thumbnail[width]"] = thumbnail.width
save_params["thumbnail[height]"] = thumbnail.height

upload_action = $.when res.url, res.post_params
save_action = $.when res.save_url, save_params

upload.start_upload upload_action, save_action
on_upload upload, d
save_upload.resolve $.when res.save_url, save_params

d
$.when res.url, res.post_params

upload.start_upload prepare_upload, save_upload
return upload

1 change: 1 addition & 0 deletions views/admin/uploads.moon
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class AdminUploads extends require "widgets.admin.page"
a href: @url_for(object),
"#{object.__class.__name} #{object.id}"
}
"filename"
"ready"
"deleted"
{"type", Uploads.types}
Expand Down
5 changes: 4 additions & 1 deletion views/edit_submission.moon
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ TagInput = require "widgets.tag_input"

date = require "date"

shapes = require "helpers.shapes"

class EditSubmission extends require "widgets.page"
@needs: {"submission", "uploads"}

Expand All @@ -20,7 +22,8 @@ class EditSubmission extends require "widgets.page"

js_init: =>
super {
uploads: @uploads and [{
uploads: @uploads and shapes.to_json_array\transform [{
ready: true
id: u.id
filename: u.filename
size: u.size
Expand Down

0 comments on commit 149488b

Please sign in to comment.