Create new assets when saving a photo
Instead of selecting existing assets, the user can now type names for
new assets in the capture form. Saving the photo creates each named
asset and links it to the file via asset_attachments in a single
transaction. Blank names are ignored. Alpine.js tracks the list of
name inputs and clears them on discard.
diff --git a/lib/views/capture.rb b/lib/views/capture.rb
index a1506ad..2fbbfcd 100644
--- a/lib/views/capture.rb
+++ b/lib/views/capture.rb
@@ -7,10 +7,6 @@ module Domus
class Capture < Phlex::HTML
ICONS_DIR = File.expand_path("../../public/icons", __dir__)
- def initialize(assets: [])
- @assets = assets
- end
-
def view_template
doctype
html(lang: "en") do
@@ -123,16 +119,28 @@ module Domus
end
div(class: "save-form") do
- unless @assets.empty?
- div(class: "asset-list") do
- p(class: "asset-list-label") { plain "Add to assets (optional)" }
- @assets.each do |asset|
- label(class: "asset-item") do
- input(type: "checkbox", name: "asset_ids[]", value: asset[:id])
- plain asset[:name]
- end
+ div(class: "asset-inputs") do
+ p(class: "asset-inputs-label") { plain "Create assets (optional)" }
+ template("x-for": "(_, i) in assetNames", ":key": "i") do
+ div(class: "asset-input-row") do
+ input(
+ type: "text",
+ name: "asset_names[]",
+ placeholder: "Asset name",
+ "@input": "assetNames[i] = $event.target.value"
+ )
+ button(
+ type: "button",
+ class: "btn-remove-asset",
+ "@click": "assetNames.splice(i, 1)"
+ ) { plain "×" }
end
end
+ button(
+ type: "button",
+ class: "btn btn-ghost btn-small",
+ "@click": "assetNames.push('')"
+ ) { plain "+ Add asset" }
end
div(class: "btn-row") do
diff --git a/lib/web.rb b/lib/web.rb
index ad1a1a9..f1cff2c 100644
--- a/lib/web.rb
+++ b/lib/web.rb
@@ -35,8 +35,7 @@ module Domus
r.root do
r.get do
- assets = db[:assets].order(:name).all
- Views::Capture.new(assets:).call
+ Views::Capture.new.call
end
end
@@ -66,7 +65,7 @@ module Domus
raise ClientError, "That image format isn't supported." unless IMAGE_EXTENSIONS.include?(ext)
raise ClientError, "That image is too large (25 MB max)." if upload[:tempfile].size > MAX_UPLOAD_BYTES
- asset_ids = Array(params["asset_ids"]).flatten.map(&:to_i).reject(&:zero?)
+ asset_names = Array(params["asset_names"]).flatten.map(&:strip).reject(&:empty?)
db.transaction do
file_id = db[:files].insert(extension: ext, created_at: Time.now)
@@ -76,7 +75,8 @@ module Domus
FileUtils.cp(upload[:tempfile].path, dest)
now = Time.now
- asset_ids.each do |asset_id|
+ asset_names.each do |name|
+ asset_id = db[:assets].insert(name: name, created_at: now)
db[:asset_attachments].insert(asset_id: asset_id, file_id: file_id, created_at: now)
end
end
diff --git a/public/capture.js b/public/capture.js
index 311a547..ee7a55f 100644
--- a/public/capture.js
+++ b/public/capture.js
@@ -4,6 +4,7 @@ function captureApp() {
preview: null,
dragging: false,
activeRef: null,
+ assetNames: [],
handleFile(file, ref) {
if (!file) return;
@@ -34,6 +35,7 @@ function captureApp() {
this.state = 'capture';
this.preview = null;
this.activeRef = null;
+ this.assetNames = [];
this.$refs.fileInput.disabled = false;
this.$refs.cameraInput.disabled = false;
this.$refs.fileInput.value = '';
diff --git a/test/test_app.rb b/test/test_app.rb
index 14b9873..dd1fd41 100644
--- a/test/test_app.rb
+++ b/test/test_app.rb
@@ -65,43 +65,43 @@ class TestApp < Minitest::Test
assert_equal 0, domus.db[:files].count
end
- def test_root_shows_assets
- domus.db[:assets].insert(name: "Camera", created_at: Time.now)
-
- get "/"
-
- assert_equal 200, last_response.status
- assert_includes last_response.body, "Camera"
- end
-
- def test_upload_with_asset_ids_creates_attachments
- asset_id = domus.db[:assets].insert(name: "Laptop", created_at: Time.now)
-
- post "/files", "file" => upload("photo.png", "image/png", "bytes"), "asset_ids[]" => asset_id.to_s
+ def test_upload_with_asset_name_creates_asset_and_attachment
+ post "/files", "file" => upload("photo.png", "image/png", "bytes"), "asset_names[]" => "Laptop"
assert_equal 302, last_response.status
+ asset = domus.db[:assets].first
+ refute_nil asset
+ assert_equal "Laptop", asset[:name]
file = domus.db[:files].first
attachment = domus.db[:asset_attachments].first
refute_nil attachment
- assert_equal asset_id, attachment[:asset_id]
+ assert_equal asset[:id], attachment[:asset_id]
assert_equal file[:id], attachment[:file_id]
end
- def test_upload_with_multiple_asset_ids_creates_all_attachments
- id1 = domus.db[:assets].insert(name: "Camera", created_at: Time.now)
- id2 = domus.db[:assets].insert(name: "Laptop", created_at: Time.now)
-
+ def test_upload_with_multiple_asset_names_creates_all
post "/files", "file" => upload("photo.png", "image/png", "bytes"),
- "asset_ids[]" => [id1.to_s, id2.to_s]
+ "asset_names[]" => ["Camera", "Laptop"]
assert_equal 302, last_response.status
+ assert_equal 2, domus.db[:assets].count
assert_equal 2, domus.db[:asset_attachments].count
end
- def test_upload_without_asset_ids_creates_no_attachments
+ def test_upload_with_blank_asset_names_ignored
+ post "/files", "file" => upload("photo.png", "image/png", "bytes"),
+ "asset_names[]" => ["", " "]
+
+ assert_equal 302, last_response.status
+ assert_equal 0, domus.db[:assets].count
+ assert_equal 0, domus.db[:asset_attachments].count
+ end
+
+ def test_upload_without_asset_names_creates_no_assets
post "/files", "file" => upload("photo.png", "image/png", "bytes")
assert_equal 302, last_response.status
+ assert_equal 0, domus.db[:assets].count
assert_equal 0, domus.db[:asset_attachments].count
end