From 0609a70544618c6658894e71094d3a8f25697dc4 Mon Sep 17 00:00:00 2001 From: Alex Turchyn Date: Tue, 15 Sep 2026 17:13:50 +0300 Subject: [PATCH] upload native files via the page form --- app/javascript/application.js | 2 ++ app/javascript/elements/native_upload.js | 45 ++++++++++++++++++++++++ app/views/shared/_native_navbar.html.erb | 8 +++++ 3 files changed, 55 insertions(+) create mode 100644 app/javascript/elements/native_upload.js diff --git a/app/javascript/application.js b/app/javascript/application.js index 6f2dbdd21..acc47a64e 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -13,6 +13,7 @@ import NativeAction from './elements/native_action' import NativeSearch from './elements/native_search' import NativeModal from './elements/native_modal' import NativeEvent from './elements/native_event' +import NativeUpload from './elements/native_upload' import FormPrompt from './elements/form_prompt' import InfiniteScroll from './elements/infinite_scroll' import ModalButton from './elements/modal_button' @@ -129,6 +130,7 @@ safeRegisterElement('native-action', NativeAction) safeRegisterElement('native-search', NativeSearch) safeRegisterElement('native-modal', NativeModal) safeRegisterElement('native-event', NativeEvent) +safeRegisterElement('native-upload', NativeUpload) safeRegisterElement('form-prompt', FormPrompt) safeRegisterElement('infinite-scroll', InfiniteScroll) safeRegisterElement('modal-button', ModalButton) diff --git a/app/javascript/elements/native_upload.js b/app/javascript/elements/native_upload.js new file mode 100644 index 000000000..f781b8567 --- /dev/null +++ b/app/javascript/elements/native_upload.js @@ -0,0 +1,45 @@ +import { convertImagesInInput } from './convert_upload' + +export default class extends HTMLElement { + connectedCallback () { + this.files = [] + + document.addEventListener('turbo:submit-end', this.onSubmitEnd) + } + + disconnectedCallback () { + document.removeEventListener('turbo:submit-end', this.onSubmitEnd) + } + + get form () { + return this.querySelector('form') + } + + append (index, name, type, base64) { + const file = this.files[index] ||= { name, type, chunks: [] } + + file.chunks.push(Uint8Array.from(atob(base64), (char) => char.charCodeAt(0))) + } + + async submit (url) { + const dataTransfer = new DataTransfer() + + this.files.forEach(({ name, type, chunks }) => dataTransfer.items.add(new File(chunks, name, { type }))) + this.files = [] + + const input = this.form.querySelector('[type="file"]') + + input.files = dataTransfer.files + + await convertImagesInInput(input) + + this.form.action = url + this.form.requestSubmit() + } + + onSubmitEnd = (e) => { + if (e.detail.formSubmission?.formElement !== this.form) return + + window.webkit?.messageHandlers?.native?.postMessage({ type: 'upload', success: e.detail.success }) + } +} diff --git a/app/views/shared/_native_navbar.html.erb b/app/views/shared/_native_navbar.html.erb index 878315910..68933e888 100644 --- a/app/views/shared/_native_navbar.html.erb +++ b/app/views/shared/_native_navbar.html.erb @@ -11,6 +11,14 @@ <% end %> + <% if can?(:create, ::Template) %> + + <% end %> <% if can?(:create, ::Template) && controller_name.in?(%w[templates_dashboard submissions_dashboard template_folders templates_archived submissions_archived]) %> <% folder_name = @template_folder&.full_name %> ">