feat: upload a script PDF from the script pane
Backend: POST /api/projects/<id>/script/ (owner/collaborator) saves the script FileField and returns the refreshed detail. Frontend: when a project has no script, the script pane shows a PDF file picker that uploads on selection; the returned detail carries the new URL so the pane renders it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
9c4b56dbc6
commit
b05b440922
6 changed files with 46 additions and 2 deletions
|
|
@ -9,5 +9,6 @@ urlpatterns = [
|
|||
path("projects/", views.projects),
|
||||
path("projects/<int:pk>/", views.project),
|
||||
path("projects/<int:pk>/scene/", views.scene),
|
||||
path("projects/<int:pk>/script/", views.script),
|
||||
path("projects/<int:pk>/revisions/", views.revisions),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -174,6 +174,22 @@ def scene(request, pk):
|
|||
return HttpResponseNotAllowed(["GET", "PUT"])
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def script(request, pk):
|
||||
"""POST (owner/collaborator) — upload or replace the project's script PDF."""
|
||||
if request.method != "POST":
|
||||
return HttpResponseNotAllowed(["POST"])
|
||||
if (resp := _auth(request)):
|
||||
return resp
|
||||
p = _editable(request, pk)
|
||||
f = request.FILES.get("script")
|
||||
if not f:
|
||||
return JsonResponse({"detail": "script file is required"}, status=400)
|
||||
p.script = f
|
||||
p.save(update_fields=["script", "updated"])
|
||||
return JsonResponse(_meta(request, p))
|
||||
|
||||
|
||||
def revisions(request, pk):
|
||||
"""GET /api/projects/<pk>/revisions/ — the save history (who/when/what)."""
|
||||
if (resp := _auth(request)):
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
:net-error nil ; backend unreachable (fetch-me / logout)
|
||||
:projects-error nil ; project-list fetch failure
|
||||
:save-error nil ; annotation save/delete failure
|
||||
:script-error nil ; script PDF upload failure
|
||||
:fps (/ 24000 1001)
|
||||
|
||||
;; the whole scene graph (see tl.scene). Seeded from OTIO at load.
|
||||
|
|
|
|||
|
|
@ -253,6 +253,22 @@
|
|||
|
||||
(rf/reg-event-db ::set-pane (fn [db [_ p]] (assoc-in db [:view :pane] p)))
|
||||
|
||||
;; upload (or replace) the project's script PDF; on success the refreshed detail
|
||||
;; carries the new script URL so the script pane renders it.
|
||||
(rf/reg-event-fx ::upload-script
|
||||
(fn [{:keys [db]} [_ file]]
|
||||
(let [id (get-in db [:project :id])
|
||||
fd (doto (js/FormData.) (.append "script" file))]
|
||||
{:db (assoc db :script-error nil)
|
||||
:http-xhrio (api/upload (str "/api/projects/" id "/script/") fd
|
||||
{:on-success [::script-uploaded]
|
||||
:on-failure [::script-upload-error]})})))
|
||||
(rf/reg-event-db ::script-uploaded
|
||||
(fn [db [_ detail]] (assoc db :project detail :script-error nil)))
|
||||
(rf/reg-event-db ::script-upload-error
|
||||
(fn [db [_ failure]]
|
||||
(assoc db :script-error (api/error-message failure "Script upload failed."))))
|
||||
|
||||
;; Script highlighting is an edit-mode sub-task: "Add script annotation" arms it
|
||||
;; for the annotation being edited (the draft) and jumps to the Script tab; the
|
||||
;; rects land on the draft's :script and ride its save. ✓ keeps, ✕ restores the
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
(rf/reg-sub ::net-error (fn [db] (:net-error db)))
|
||||
(rf/reg-sub ::projects-error (fn [db] (:projects-error db)))
|
||||
(rf/reg-sub ::save-error (fn [db] (:save-error db)))
|
||||
(rf/reg-sub ::script-error (fn [db] (:script-error db)))
|
||||
(rf/reg-sub ::load-error (fn [db] (get-in db [:load :error])))
|
||||
(rf/reg-sub ::authed? (fn [db] (boolean (get-in db [:auth :authenticated]))))
|
||||
(rf/reg-sub ::username (fn [db] (get-in db [:auth :username])))
|
||||
|
|
|
|||
|
|
@ -851,6 +851,8 @@
|
|||
anns @(rf/subscribe [::subs/annotations])
|
||||
hl @(rf/subscribe [::subs/highlighting])
|
||||
active @(rf/subscribe [::subs/active-annotation])
|
||||
authed? @(rf/subscribe [::subs/authed?])
|
||||
script-err @(rf/subscribe [::subs/script-error])
|
||||
armed (:gid hl)
|
||||
hl-name (some #(when (= (:id %) armed) (:name %)) anns)]
|
||||
(when (and url (exists? js/pdfjsLib) (not= url @loaded))
|
||||
|
|
@ -877,8 +879,15 @@
|
|||
[:button.hl-ok {:title "Done" :on-click #(rf/dispatch [::events/finish-highlighting])} "✓"]
|
||||
[:button.hl-cancel {:title "Cancel" :on-click #(rf/dispatch [::events/cancel-highlighting])} "✕"]]]
|
||||
[:div.script-hint
|
||||
(if url "Playing follows the script to each note. Edit an annotation → “Add script annotation” to link a passage."
|
||||
"No script uploaded for this project.")])
|
||||
(cond
|
||||
url "Playing follows the script to each note. Edit an annotation → “Add script annotation” to link a passage."
|
||||
authed? [:span.script-upload
|
||||
[:label.file-row "Upload a script (PDF) "
|
||||
[:input {:type "file" :accept ".pdf,application/pdf"
|
||||
:on-change #(when-let [f (-> % .-target .-files (aget 0))]
|
||||
(rf/dispatch [::events/upload-script f]))}]]
|
||||
(when script-err [:span.err " " script-err])]
|
||||
:else "No script uploaded for this project.")])
|
||||
[:div.script-pages {:ref #(reset! el %)}
|
||||
(if (seq @pages)
|
||||
(doall (for [[n {:keys [pg w h]}] @pages]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue