Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions app/controllers/submit_form_take_photo_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

class SubmitFormTakePhotoController < ApplicationController
layout false

around_action :with_browser_locale, only: %i[show]
skip_before_action :authenticate_user!
skip_authorization_check

def show
@submitter = Submitter.find_by!(slug: params[:slug])

return redirect_to submit_form_completed_path(@submitter.slug) if @submitter.completed_at?

if @submitter.submission.template.archived_at? || @submitter.submission.archived_at?
return redirect_to submit_form_path(@submitter.slug)
end

render :show
end
end
142 changes: 142 additions & 0 deletions app/javascript/photo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
window.customElements.define('file-photo', class extends HTMLElement {
connectedCallback () {

this.clearButton.addEventListener('click', (e) => {
e.preventDefault()
this.valueInput.value = null
this.inputFile.click()
})

this.inputFile.addEventListener('change', (e) => {
e.preventDefault()
this.updateSubmitButtonVisibility()
this.uploadFiles(this.inputFile.files)
})

this.form.addEventListener('submit', (e) => {
e.preventDefault();
this.submitButton.disabled = true
fetch(this.form.action, {
method: 'PUT',
body: new FormData(this.form)
}).then((response) => {
this.form.classList.add('hidden')
this.success.classList.remove('hidden')
return response
}).finally(() => {
this.submitButton.disabled = false
})
})

}

toggleLoading = (e) => {
this.updateSubmitButtonVisibility()
if (e && e.target && !e.target.contains(this)) {
return
}
this.loading.classList.toggle('hidden')
this.icon.classList.toggle('hidden')
this.classList.toggle('opacity-50')
}

async uploadFiles (files) {
this.toggleLoading()
return await Promise.all(
Array.from(files).map(async (file) => {
const formData = new FormData()
if (file.type === 'image/bmp') {
file = await this.convertBmpToPng(file)
}

formData.append('file', file)
formData.append('submitter_slug', this.dataset.slug)
formData.append('name', 'attachments')

return fetch('/api/attachments', {
method: 'POST',
body: formData
}).then(resp => resp.json()).then((data) => {
return data
})
})).then((result) => {
this.valueInput.value = result[0].uuid
return result[0]
}).finally(() => {
this.toggleLoading()
})
}

convertBmpToPng (bmpFile) {
return new Promise((resolve, reject) => {
const reader = new FileReader()

reader.onload = function (event) {
const img = new Image()

img.onload = function () {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')

canvas.width = img.width
canvas.height = img.height
ctx.drawImage(img, 0, 0)
canvas.toBlob(function (blob) {
const newFile = new File([blob], bmpFile.name.replace(/\.\w+$/, '.png'), { type: 'image/png' })
resolve(newFile)
}, 'image/png')
}

img.src = event.target.result
}
reader.onerror = reject
reader.readAsDataURL(bmpFile)
})
}

updateSubmitButtonVisibility () {
if (!this.valueInput.value) {
this.submitButton.style.display = 'none'
this.placeholderButton.style.display = 'block'
} else {
this.submitButton.style.display = 'block'
this.placeholderButton.style.display = 'none'
}
}

get submitButton () {
return this.querySelector('button[type="submit"]')
}

get clearButton () {
return this.querySelector('button[aria-label="Clear"]')
}

get placeholderButton () {
return this.querySelector('button[disabled]')
}

get valueInput () {
return this.querySelector('input[name^="values"]')
}

get inputFile () {
return this.querySelector('input[id="file"]')
}

get icon () {
return this.querySelector('#file-photo-icon')
}

get loading () {
return this.querySelector('#file-photo-loading')
}

get form () {
return this.querySelector('form')
}

get success () {
return this.querySelector('#success')
}
})
53 changes: 36 additions & 17 deletions app/javascript/submission_form/dropzone.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<template>
<div
id="dropzone"
class="flex h-32 w-full"
class="flex w-full"
:class="{'h-20': onlyWithCamera, 'h-32': !onlyWithCamera}"
@dragover.prevent
@drop.prevent="onDropFiles"
>
Expand All @@ -13,10 +14,15 @@
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center">
<div class="flex flex-col items-center">
<IconInnerShadowTop
v-if="isLoading"
class="animate-spin"
:width="30"
:height="30"
v-if="isLoading"
class="animate-spin"
:width="30"
:height="30"
/>
<IconCamera
v-else-if="onlyWithCamera"
:width="30"
:height="30"
/>
<IconCloudUpload
v-else
Expand All @@ -29,7 +35,7 @@
>
{{ message }}
</div>
<div class="text-xs">
<div class="text-xs" v-if="!onlyWithCamera">
<span class="font-medium">{{ t('click_to_upload') }}</span> {{ t('or_drag_and_drop_files') }}
</div>
</div>
Expand All @@ -39,6 +45,7 @@
ref="input"
:multiple="multiple"
:accept="accept"
:capture="onlyWithCamera === true ? `camera` : null"
type="file"
class="hidden"
@change="onSelectFiles"
Expand All @@ -48,11 +55,13 @@
</template>

<script>
import { IconCloudUpload, IconInnerShadowTop } from '@tabler/icons-vue'
import { IconCamera, IconCloudUpload, IconInnerShadowTop } from '@tabler/icons-vue'
import field from "../template_builder/field.vue";

export default {
name: 'FileDropzone',
components: {
IconCamera,
IconCloudUpload,
IconInnerShadowTop
},
Expand All @@ -71,6 +80,11 @@ export default {
required: false,
default: false
},
onlyWithCamera: {
type: Boolean,
required: false,
default: false
},
accept: {
type: String,
required: false,
Expand All @@ -89,24 +103,29 @@ export default {
}
},
computed: {
field() {
return field
},
inputId () {
return 'el' + Math.random().toString(32).split('.')[1]
}
},
methods: {
onDropFiles (e) {
const files = Array.from(e.dataTransfer.files).filter((f) => {
if (this.accept === 'image/*') {
return f.type.startsWith('image')
if(!this.onlyWithCamera){
const files = Array.from(e.dataTransfer.files).filter((f) => {
if (this.accept === 'image/*') {
return f.type.startsWith('image')
} else {
return true
}
})

if (this.accept === 'image/*' && !files.length) {
alert(this.t('please_upload_an_image_file'))
} else {
return true
this.uploadFiles(files)
}
})

if (this.accept === 'image/*' && !files.length) {
alert(this.t('please_upload_an_image_file'))
} else {
this.uploadFiles(files)
}
},
onSelectFiles (e) {
Expand Down
Loading