From 7cf56429245be32c26dfef78f08274c6394659c8 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Fri, 15 Dec 2023 19:53:14 +0100 Subject: [PATCH 1/8] Add request files to view when attached --- .../request_form/request_form.html.erb | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/app/components/request_form/request_form.html.erb b/app/components/request_form/request_form.html.erb index 53851737c..258d3376d 100644 --- a/app/components/request_form/request_form.html.erb +++ b/app/components/request_form/request_form.html.erb @@ -60,6 +60,34 @@ class="RequestForm-filenamesList" > + <% if @request.files.attached? %> +
+ <%= c 'heading', style: :gamma do %> + <%= t('.attached_images') %> + <% end %> + +
+ <% end %> <% end %> <%= c 'field', object: @request, attr: :schedule_send_for do |field| %> From a1d31dacf1806162675be9284746a0c97b1706bd Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Sat, 16 Dec 2023 11:16:26 +0100 Subject: [PATCH 2/8] Add image preview for attached request files --- .../request_form/request_form.html.erb | 1 + app/components/request_form/request_form.js | 53 ++++++++++++++++--- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/app/components/request_form/request_form.html.erb b/app/components/request_form/request_form.html.erb index 258d3376d..198403115 100644 --- a/app/components/request_form/request_form.html.erb +++ b/app/components/request_form/request_form.html.erb @@ -3,6 +3,7 @@ data-controller="request-form" data-request-form-members-count-message-value="<%= t('components.request_form.members_count_message').to_json %>" data-request-form-preview-fallback-value="<%= t('components.request_form.preview_fallback') %>" + data-request-form-request-files-url-value="<%= @request.files.map { |file| url_for(file) } %>" >
diff --git a/app/components/request_form/request_form.js b/app/components/request_form/request_form.js index 20ad3519b..ca85f06a2 100644 --- a/app/components/request_form/request_form.js +++ b/app/components/request_form/request_form.js @@ -18,6 +18,7 @@ export default class extends Controller { static values = { membersCountMessage: String, previewFallback: String, + requestFilesUrl: Array, }; connect() { @@ -47,11 +48,17 @@ export default class extends Controller { this.previewTarget.innerHTML = this.setMessage(); } - if (event?.target?.files?.length) { + if (event?.target?.files?.length || this.requestFilesUrlValue.length) { this.previewTarget.innerHTML = ''; this.messageTarget.removeAttribute('required'); - this.addImagePreview(event.target.files, this.setCaption()); } + if (event?.target?.files?.length) + this.addImagePreview(event.target.files, this.setCaption()); + if (this.requestFilesUrlValue.length) + this.addAttachedRequestFilesPreview( + this.requestFilesUrlValue, + this.setCaption() + ); } updateMembersCount(event) { @@ -89,7 +96,8 @@ export default class extends Controller { const figure = document.createElement('figure'); const div = document.createElement('div'); div.classList.add('RequestForm-imagePreviewWrapper'); - this.removeExistingPreview(); + this.removeExistingImagePreview(); + this.removeExistingFilesname(); for (let i = 0; i < files.length; i++) { let file = files.item(i); @@ -114,8 +122,33 @@ export default class extends Controller { img.classList.add('RequestForm-firstImageInOddNumber'); } div.appendChild(img); - this.setImageAttributes(img, file); + this.setImageAttributes(img, URL.createObjectURL(file)); } + + this.addPreview(figure, div, message); + } + + addAttachedRequestFilesPreview(urls, message) { + const figure = document.createElement('figure'); + const div = document.createElement('div'); + div.classList.add('RequestForm-imagePreviewWrapper'); + + this.removeExistingImagePreview(); + + urls.forEach((url, i) => { + const img = document.createElement('img'); + img.classList.add('RequestForm-imagePreview'); + if (urls.length % 2 == 1 && i == 0) { + img.classList.add('RequestForm-firstImageInOddNumber'); + } + div.appendChild(img); + this.setImageAttributes(img, url); + }); + + this.addPreview(figure, div, message); + } + + addPreview(figure, div, message) { figure.appendChild(div); const figcaption = document.createElement('figcaption'); figcaption.setAttribute('id', 'caption'); @@ -127,7 +160,7 @@ export default class extends Controller { firstFigcaption.innerHTML = message; } - removeExistingPreview() { + removeExistingImagePreview() { const existingFigure = document.getElementById('file-preview'); if (existingFigure) existingFigure.remove(); const chatPreviewBubbles = document.querySelectorAll( @@ -138,14 +171,17 @@ export default class extends Controller { element.remove(); } }); + } + + removeExistingFilesname() { const listItems = document.querySelectorAll( '.RequestForm-filenamesListItem' ); listItems.forEach(listItem => listItem.remove()); } - setImageAttributes(img, file) { - img.setAttribute('src', URL.createObjectURL(file)); + setImageAttributes(img, url) { + img.setAttribute('src', url); img.setAttribute('width', 100); img.setAttribute('width', 100); } @@ -163,7 +199,8 @@ export default class extends Controller { this.imageInputTarget.files = dt.files; event.target.parentNode.remove(); if (this.imageInputTarget.files.length == 0) { - this.removeExistingPreview(); + this.removeExistingImagePreview(); + this.removeExistingFilesname(); this.filenamesTarget.parentNode.classList.add( 'RequestForm-filenamesWrapper--hidden' ); From afe2c034183653ca50d9bede0483381edad96b50 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Sat, 16 Dec 2023 11:51:00 +0100 Subject: [PATCH 3/8] Fix spec by adding condition that request is planned --- app/components/request_form/request_form.html.erb | 7 ++----- spec/system/requests/sending_images_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/components/request_form/request_form.html.erb b/app/components/request_form/request_form.html.erb index 198403115..013a67c60 100644 --- a/app/components/request_form/request_form.html.erb +++ b/app/components/request_form/request_form.html.erb @@ -61,15 +61,12 @@ class="RequestForm-filenamesList" >
- <% if @request.files.attached? %> + <% if @request.planned? && @request.files.attached? %>
<%= c 'heading', style: :gamma do %> <%= t('.attached_images') %> <% end %> -
    +
      <% @request.files.each_with_index do |file, index| %>
    • Date: Mon, 8 Jan 2024 12:57:04 +0100 Subject: [PATCH 4/8] Add replace/remove image update feature --- .../request_form/request_form.html.erb | 37 ++++++++++++++----- app/components/request_form/request_form.js | 34 ++++++++++++++++- app/controllers/requests_controller.rb | 1 + 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/app/components/request_form/request_form.html.erb b/app/components/request_form/request_form.html.erb index 013a67c60..42905ff8a 100644 --- a/app/components/request_form/request_form.html.erb +++ b/app/components/request_form/request_form.html.erb @@ -28,13 +28,26 @@ class: 'RequestForm-insertPlaceholderButton', type: 'button', data: { action: 'request-form#insertImage' } do %> - <%= field.file_field(:request, - :files, - data: { request_form_target: 'imageInput' }, - multiple: true, - accept: 'image/*' + <% if @request.planned? && @request.files.attached? %> + <% @request.files.each do |file| %> + <%= field.hidden_field(:request, + :files, + data: { request_form_target: 'imageInputAttachedFile' }, + multiple: true, + value: file.signed_id - ) %> + ) %> + <% end %> + <% else %> + <%= field.file_field(:request, + :files, + data: { request_form_target: 'imageInput' }, + multiple: true, + hidden: true, + accept: 'image/*' + + ) %> + <% end %> <%= t('.attach_image_to_message') %> <% end %> <% end %> @@ -66,8 +79,11 @@ <%= c 'heading', style: :gamma do %> <%= t('.attached_images') %> <% end %> -
        - <% @request.files.each_with_index do |file, index| %> +
          + <% @request.files.each do |file| %>
        • <%= file.blob[:filename] %>

- <% if @request.planned? && @request.files.attached? %> -
- <%= c 'heading', style: :gamma do %> - <%= t('.attached_images') %> - <% end %> -
    + > + <% if @request.planned? && @request.files.attached? %> <% @request.files.each do |file| %>
  • <% end %> -
-
- <% end %> + <% end %> + + <% end %> <%= c 'field', object: @request, attr: :schedule_send_for do |field| %> diff --git a/app/components/request_form/request_form.js b/app/components/request_form/request_form.js index 0ed3fe14a..a224f6861 100644 --- a/app/components/request_form/request_form.js +++ b/app/components/request_form/request_form.js @@ -14,7 +14,6 @@ export default class extends Controller { 'submitButton', 'characterCounter', 'modal', - 'attachedFiles', 'imageInputAttachedFile', ]; static values = { @@ -55,11 +54,15 @@ export default class extends Controller { } if (event?.target?.files?.length) this.addImagePreview(event.target.files, this.setCaption()); - if (this.requestFilesUrlValue.length) + if (this.requestFilesUrlValue.length) { + this.filenamesTarget.parentNode.classList.remove( + 'RequestForm-filenamesWrapper--hidden' + ); this.addAttachedRequestFilesPreview( this.requestFilesUrlValue, this.setCaption() ); + } } updateMembersCount(event) { @@ -90,17 +93,19 @@ export default class extends Controller { } insertImage() { - if (this.hasImageInputTarget) this.imageInputTarget.click(); - if (this.hasImageInputAttachedFileTarget) - this.imageInputAttachedFileTarget.click(); + this.imageInputTarget.click(); } addImagePreview(files, message) { - const figure = document.createElement('figure'); - const div = document.createElement('div'); - div.classList.add('RequestForm-imagePreviewWrapper'); - this.removeExistingImagePreview(); - this.removeExistingFilesname(); + let figure = document.getElementById('file-preview'); + let div; + if (figure) { + div = figure.firstChild; + } else { + figure = document.createElement('figure'); + div = document.createElement('div'); + div.classList.add('RequestForm-imagePreviewWrapper'); + } for (let i = 0; i < files.length; i++) { let file = files.item(i); @@ -132,11 +137,15 @@ export default class extends Controller { } addAttachedRequestFilesPreview(urls, message) { - const figure = document.createElement('figure'); - const div = document.createElement('div'); - div.classList.add('RequestForm-imagePreviewWrapper'); - - this.removeExistingImagePreview(); + let figure = document.getElementById('file-preview'); + let div; + if (figure) { + div = figure.firstChild; + } else { + figure = document.createElement('figure'); + div = document.createElement('div'); + div.classList.add('RequestForm-imagePreviewWrapper'); + } urls.forEach((url, i) => { const img = document.createElement('img'); @@ -201,9 +210,12 @@ export default class extends Controller { this.imageInputTarget.files = dt.files; event.target.parentNode.remove(); - if (this.imageInputTarget.files.length == 0) { - this.removeExistingImagePreview(); - this.removeExistingFilesname(); + this.removeExistingImagePreview(); + + if ( + this.imageInputTarget.files.length == 0 && + this.imageInputAttachedFileTargets.length == 0 + ) { this.filenamesTarget.parentNode.classList.add( 'RequestForm-filenamesWrapper--hidden' ); @@ -211,6 +223,10 @@ export default class extends Controller { this.messageTarget.setAttribute('required', true); } else { this.addImagePreview(this.imageInputTarget.files, this.setCaption()); + this.addAttachedRequestFilesPreview( + this.requestFilesUrlValue, + this.setCaption() + ); } } @@ -219,21 +235,25 @@ export default class extends Controller { const id = event.target.dataset.requestFormImageIdValue; const url = event.target.dataset.requestFormImageUrlValue; - this.requestFilesUrlValue = this.requestFilesUrlValue.filter(u => u == url); + this.requestFilesUrlValue = this.requestFilesUrlValue.filter(u => u != url); const hiddenInputs = this.imageInputAttachedFileTargets; const inputToDelete = hiddenInputs.find( image => image.getAttribute('value') == id ); inputToDelete.remove(); - if (this.imageInputAttachedFileTargets.length == 0) { - this.removeExistingImagePreview(); - this.removeExistingFilesname(); - this.attachedFilesTarget.parentNode.classList.add( + this.removeExistingImagePreview(); + + if ( + this.imageInputAttachedFileTargets.length == 0 && + this.imageInputTarget.files.length == 0 + ) { + this.filenamesTarget.parentNode.classList.add( 'RequestForm-filenamesWrapper--hidden' ); this.previewTarget.innerHTML = this.setMessage(); this.messageTarget.setAttribute('required', true); } else { + this.addImagePreview(this.imageInputTarget.files, this.setCaption()); this.addAttachedRequestFilesPreview( this.requestFilesUrlValue, this.setCaption() @@ -259,28 +279,32 @@ export default class extends Controller { } updateFilesname(index, file) { - const listItem = document.createElement('li'); - listItem.setAttribute('id', `image-filename-${file.name}`); - listItem.classList.add('RequestForm-filenamesListItem'); - - const paragraph = document.createElement('p'); - paragraph.innerText = file.name; - paragraph.classList.add('RequestForm-filename'); - listItem.appendChild(paragraph); - - const removeButton = document.createElement('button'); - removeButton.innerText = 'x'; - removeButton.setAttribute('data-action', 'request-form#removeImage'); - removeButton.setAttribute('data-request-form-image-index-value', index); - removeButton.setAttribute('type', 'button'); - removeButton.classList.add('Button'); - removeButton.classList.add('RequestForm-removeListItemButton'); - listItem.appendChild(removeButton); - - this.filenamesTarget.appendChild(listItem); - this.filenamesTarget.parentNode.classList.remove( - 'RequestForm-filenamesWrapper--hidden' - ); + let listItem = document.getElementById(`image-filename-${file.name}`); + + if (!listItem) { + listItem = document.createElement('li'); + listItem.setAttribute('id', `image-filename-${file.name}`); + listItem.classList.add('RequestForm-filenamesListItem'); + + const paragraph = document.createElement('p'); + paragraph.innerText = file.name; + paragraph.classList.add('RequestForm-filename'); + listItem.appendChild(paragraph); + + const removeButton = document.createElement('button'); + removeButton.innerText = 'x'; + removeButton.setAttribute('data-action', 'request-form#removeImage'); + removeButton.setAttribute('data-request-form-image-index-value', index); + removeButton.setAttribute('type', 'button'); + removeButton.classList.add('Button'); + removeButton.classList.add('RequestForm-removeListItemButton'); + listItem.appendChild(removeButton); + + this.filenamesTarget.appendChild(listItem); + this.filenamesTarget.parentNode.classList.remove( + 'RequestForm-filenamesWrapper--hidden' + ); + } } updateCharacterCounter() { From 5d382fe74374f905b77e5fed2164d209c57eecdf Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Mon, 22 Jan 2024 13:25:03 +0100 Subject: [PATCH 6/8] DRY out set up image preview code --- app/components/request_form/request_form.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/app/components/request_form/request_form.js b/app/components/request_form/request_form.js index a224f6861..015bc6c6c 100644 --- a/app/components/request_form/request_form.js +++ b/app/components/request_form/request_form.js @@ -96,7 +96,7 @@ export default class extends Controller { this.imageInputTarget.click(); } - addImagePreview(files, message) { + setUpImagePreview() { let figure = document.getElementById('file-preview'); let div; if (figure) { @@ -106,6 +106,11 @@ export default class extends Controller { div = document.createElement('div'); div.classList.add('RequestForm-imagePreviewWrapper'); } + return [figure, div]; + } + + addImagePreview(files, message) { + const [figure, div] = this.setUpImagePreview(); for (let i = 0; i < files.length; i++) { let file = files.item(i); @@ -137,15 +142,7 @@ export default class extends Controller { } addAttachedRequestFilesPreview(urls, message) { - let figure = document.getElementById('file-preview'); - let div; - if (figure) { - div = figure.firstChild; - } else { - figure = document.createElement('figure'); - div = document.createElement('div'); - div.classList.add('RequestForm-imagePreviewWrapper'); - } + const [figure, div] = this.setUpImagePreview(); urls.forEach((url, i) => { const img = document.createElement('img'); From 38b17b4986de9655f50ab80f8e923ab2bc4b76f1 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 23 Jan 2024 10:08:58 +0100 Subject: [PATCH 7/8] DRY out update preivew after remove event, fix duplicate image in preview --- app/components/request_form/request_form.js | 44 +++++++++------------ 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/app/components/request_form/request_form.js b/app/components/request_form/request_form.js index 015bc6c6c..b11949b3b 100644 --- a/app/components/request_form/request_form.js +++ b/app/components/request_form/request_form.js @@ -100,11 +100,12 @@ export default class extends Controller { let figure = document.getElementById('file-preview'); let div; if (figure) { - div = figure.firstChild; + div = document.getElementById('image-preview-wrapper'); } else { figure = document.createElement('figure'); div = document.createElement('div'); div.classList.add('RequestForm-imagePreviewWrapper'); + div.setAttribute('id', 'image-preview-wrapper'); } return [figure, div]; } @@ -145,13 +146,17 @@ export default class extends Controller { const [figure, div] = this.setUpImagePreview(); urls.forEach((url, i) => { - const img = document.createElement('img'); - img.classList.add('RequestForm-imagePreview'); - if (urls.length % 2 == 1 && i == 0) { - img.classList.add('RequestForm-firstImageInOddNumber'); + const existingImage = document.getElementById(`image-${url}`); + if (!existingImage) { + const img = document.createElement('img'); + img.setAttribute('id', `image-${url}`); + img.classList.add('RequestForm-imagePreview'); + if (urls.length % 2 == 1 && i == 0) { + img.classList.add('RequestForm-firstImageInOddNumber'); + } + div.appendChild(img); + this.setImageAttributes(img, url); } - div.appendChild(img); - this.setImageAttributes(img, url); }); this.addPreview(figure, div, message); @@ -208,23 +213,7 @@ export default class extends Controller { this.imageInputTarget.files = dt.files; event.target.parentNode.remove(); this.removeExistingImagePreview(); - - if ( - this.imageInputTarget.files.length == 0 && - this.imageInputAttachedFileTargets.length == 0 - ) { - this.filenamesTarget.parentNode.classList.add( - 'RequestForm-filenamesWrapper--hidden' - ); - this.previewTarget.innerHTML = this.setMessage(); - this.messageTarget.setAttribute('required', true); - } else { - this.addImagePreview(this.imageInputTarget.files, this.setCaption()); - this.addAttachedRequestFilesPreview( - this.requestFilesUrlValue, - this.setCaption() - ); - } + this.updatePreviewAfterRemoveEvent(); } removeAttachedImage(event) { @@ -239,10 +228,13 @@ export default class extends Controller { ); inputToDelete.remove(); this.removeExistingImagePreview(); + this.updatePreviewAfterRemoveEvent(); + } + updatePreviewAfterRemoveEvent() { if ( - this.imageInputAttachedFileTargets.length == 0 && - this.imageInputTarget.files.length == 0 + this.imageInputTarget.files.length == 0 && + this.imageInputAttachedFileTargets.length == 0 ) { this.filenamesTarget.parentNode.classList.add( 'RequestForm-filenamesWrapper--hidden' From f0d33f1e535995b87e96dbe49ce403cdbe466ac1 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 23 Jan 2024 10:52:16 +0100 Subject: [PATCH 8/8] Fix caption preview --- app/components/request_form/request_form.js | 28 +++++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/app/components/request_form/request_form.js b/app/components/request_form/request_form.js index b11949b3b..1ff63481a 100644 --- a/app/components/request_form/request_form.js +++ b/app/components/request_form/request_form.js @@ -107,6 +107,10 @@ export default class extends Controller { div.classList.add('RequestForm-imagePreviewWrapper'); div.setAttribute('id', 'image-preview-wrapper'); } + + figure.appendChild(div); + this.previewTarget.parentNode.appendChild(figure); + return [figure, div]; } @@ -139,7 +143,7 @@ export default class extends Controller { this.setImageAttributes(img, URL.createObjectURL(file)); } - this.addPreview(figure, div, message); + this.addPreview(figure, message); } addAttachedRequestFilesPreview(urls, message) { @@ -159,19 +163,21 @@ export default class extends Controller { } }); - this.addPreview(figure, div, message); + this.addPreview(figure, message); } - addPreview(figure, div, message) { - figure.appendChild(div); - const figcaption = document.createElement('figcaption'); - figcaption.setAttribute('id', 'caption'); - figure.setAttribute('id', 'file-preview'); - figure.appendChild(figcaption); + addPreview(figure, message) { + let figcaption = document.getElementById('caption'); - this.previewTarget.parentNode.appendChild(figure); - const firstFigcaption = figure.querySelector('figcaption'); - firstFigcaption.innerHTML = message; + if (!figcaption) { + figcaption = document.createElement('figcaption'); + figcaption.setAttribute('id', 'caption'); + figure.setAttribute('id', 'file-preview'); + figure.appendChild(figcaption); + } + + figure.appendChild(figcaption); + figcaption.innerHTML = message; } removeExistingImagePreview() {