mirror of
https://github.com/docusealco/docuseal
synced 2026-11-04 14:42:32 -05:00
adjust native
This commit is contained in:
1 parent
6f31bd7af8
commit
66758d45bc
21 files changed
+379
-64
No files matched your search
@@ -13,7 +13,7 @@ class ApplicationController < ActionController::Base
|
|||||||
before_action :maybe_redirect_to_setup, unless: :signed_in?
|
before_action :maybe_redirect_to_setup, unless: :signed_in?
|
||||||
before_action :authenticate_user!, unless: :devise_controller?
|
before_action :authenticate_user!, unless: :devise_controller?
|
||||||
|
|
||||||
before_action :set_csp, if: -> { request.get? && !request.headers['HTTP_X_TURBO'] && !turbo_infinite_scroll? }
|
before_action :set_csp, if: -> { request.get? && !request.headers['HTTP_X_TURBO_REQUEST_ID'] && !turbo_infinite_scroll? }
|
||||||
|
|
||||||
helper_method :button_title,
|
helper_method :button_title,
|
||||||
:current_account,
|
:current_account,
|
||||||
|
|||||||
@@ -110,16 +110,16 @@ class TemplatesDashboardController < ApplicationController
|
|||||||
|
|
||||||
pinned_count = (show_default_in_grid ? 1 : 0) + (show_shared_in_grid ? 1 : 0)
|
pinned_count = (show_default_in_grid ? 1 : 0) + (show_shared_in_grid ? 1 : 0)
|
||||||
|
|
||||||
pagy = Pagy::Offset.new(count: folders_count + pinned_count,
|
pagy, = pagy(template_folders, count: folders_count + pinned_count,
|
||||||
page: show_inline_folders ? 1 : [params[:page].to_s.to_i, 1].max,
|
page: show_inline_folders ? 1 : [params[:page].to_s.to_i, 1].max,
|
||||||
limit: FOLDERS_PER_PAGE,
|
limit: FOLDERS_PER_PAGE,
|
||||||
raise_range_error: true)
|
raise_range_error: true)
|
||||||
|
|
||||||
show_default_folder = show_default_in_grid && pagy.page == 1
|
show_default_folder = show_default_in_grid && pagy.page == 1
|
||||||
show_shared_folder = show_shared_in_grid && pagy.page == 1
|
show_shared_folder = show_shared_in_grid && pagy.page == 1
|
||||||
|
|
||||||
folder_offset = pagy.page == 1 ? 0 : pagy.offset - pinned_count
|
folder_offset = pagy.page == 1 ? 0 : pagy.offset - pinned_count
|
||||||
folder_limit = pagy.page == 1 ? FOLDERS_PER_PAGE - pinned_count : FOLDERS_PER_PAGE
|
folder_limit = pagy.page == 1 ? pagy.limit - pinned_count : pagy.limit
|
||||||
|
|
||||||
template_folders = template_folders.offset(folder_offset).limit(folder_limit)
|
template_folders = template_folders.offset(folder_offset).limit(folder_limit)
|
||||||
|
|
||||||
|
|||||||
@@ -10,22 +10,12 @@ class TestingAccountsController < ApplicationController
|
|||||||
|
|
||||||
impersonate_user(Accounts.find_or_create_testing_user(true_user.account))
|
impersonate_user(Accounts.find_or_create_testing_user(true_user.account))
|
||||||
|
|
||||||
redirect_after_toggle
|
redirect_back(fallback_location: root_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
stop_impersonating_user
|
stop_impersonating_user
|
||||||
|
|
||||||
redirect_after_toggle
|
redirect_back(fallback_location: root_path)
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def redirect_after_toggle
|
|
||||||
if turbo_native_app?
|
|
||||||
redirect_to root_path
|
|
||||||
else
|
|
||||||
redirect_back(fallback_location: root_path)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import '@hotwired/turbo-rails'
|
import '@hotwired/turbo-rails'
|
||||||
|
import './elements/native_bridge'
|
||||||
|
|
||||||
import { createApp, reactive } from 'vue'
|
import { createApp, reactive } from 'vue'
|
||||||
import TemplateBuilder from './template_builder/builder'
|
import TemplateBuilder from './template_builder/builder'
|
||||||
@@ -97,14 +98,31 @@ if (window.webkit?.messageHandlers?.native) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
document.addEventListener('turbo:before-fetch-request', (event) => {
|
document.addEventListener('native:pull-to-refresh', (event) => {
|
||||||
const { session } = window.Turbo
|
const { session } = window.Turbo
|
||||||
const visit = session.navigator.currentVisit
|
const url = new URL(document.baseURI)
|
||||||
const limit = document.getElementById('infinite_scroll')?.dataset.limit
|
|
||||||
|
|
||||||
if (limit && window.scrollY > 0 && event.target === document.documentElement && visit?.location.href === session.view.lastRenderedLocation.href && session.view.isPageRefresh(visit)) {
|
url.searchParams.delete('limit')
|
||||||
event.detail.url.searchParams.set('limit', limit)
|
|
||||||
}
|
session.history.replace(url, session.history.restorationIdentifier)
|
||||||
|
session.view.lastRenderedLocation = url
|
||||||
|
|
||||||
|
event.detail.result = new Promise((resolve) => {
|
||||||
|
document.addEventListener('turbo:render', () => resolve(true), { once: true })
|
||||||
|
document.addEventListener('turbo:fetch-request-error', () => resolve(true), { once: true })
|
||||||
|
|
||||||
|
session.refresh(document.baseURI)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener('native:refresh', () => {
|
||||||
|
window.Turbo.session.refresh(document.baseURI)
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener('native:turbo-stream', (e) => {
|
||||||
|
if (!e.detail.body?.trimStart().startsWith('<turbo-stream')) return
|
||||||
|
|
||||||
|
window.Turbo.renderStreamMessage(e.detail.body)
|
||||||
})
|
})
|
||||||
|
|
||||||
document.addEventListener('turbo:visit', (e) => {
|
document.addEventListener('turbo:visit', (e) => {
|
||||||
@@ -118,8 +136,10 @@ if (window.webkit?.messageHandlers?.native) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('turbo:before-fetch-request', (event) => {
|
document.addEventListener('turbo:before-fetch-response', (event) => {
|
||||||
event.detail.fetchOptions.headers['X-Turbo'] = 'true'
|
if (event.detail.fetchResponse.header('content-disposition')?.includes('attachment')) {
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
document.addEventListener('turbo:submit-end', async (event) => {
|
document.addEventListener('turbo:submit-end', async (event) => {
|
||||||
const resp = event.detail?.formSubmission?.result?.fetchResponse?.response
|
const resp = event.detail?.formSubmission?.result?.fetchResponse?.response
|
||||||
@@ -232,6 +252,7 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
|||||||
document.addEventListener('template-builder:select-submitter', this.onSelectSubmitter)
|
document.addEventListener('template-builder:select-submitter', this.onSelectSubmitter)
|
||||||
document.addEventListener('template-builder:sync-custom-fields', this.onSyncCustomFields)
|
document.addEventListener('template-builder:sync-custom-fields', this.onSyncCustomFields)
|
||||||
document.addEventListener('template-builder:draw-field', this.onDrawField)
|
document.addEventListener('template-builder:draw-field', this.onDrawField)
|
||||||
|
document.addEventListener('native:builder-view', this.onBuilderView)
|
||||||
document.addEventListener('turbo:before-cache', this.onBeforeCache)
|
document.addEventListener('turbo:before-cache', this.onBeforeCache)
|
||||||
|
|
||||||
this.appElem = document.createElement('div')
|
this.appElem = document.createElement('div')
|
||||||
@@ -395,6 +416,23 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBuilderView = (event) => {
|
||||||
|
const root = document.documentElement.cloneNode(true)
|
||||||
|
const builder = root.querySelector('template-builder')
|
||||||
|
const style = document.createElement('style')
|
||||||
|
|
||||||
|
builder.innerHTML = ''
|
||||||
|
builder.setAttribute('data-native-view', event.detail.view)
|
||||||
|
|
||||||
|
root.querySelector('meta[name="viewport"]')?.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no')
|
||||||
|
|
||||||
|
style.textContent = 'html { overflow-x: hidden; overscroll-behavior-x: none; } html, body { max-width: 100%; }'
|
||||||
|
|
||||||
|
root.querySelector('head').appendChild(style)
|
||||||
|
|
||||||
|
event.detail.result = '<!DOCTYPE html>' + root.outerHTML
|
||||||
|
}
|
||||||
|
|
||||||
onBeforeCache = () => {
|
onBeforeCache = () => {
|
||||||
this.app?.unmount()
|
this.app?.unmount()
|
||||||
this.appElem?.remove()
|
this.appElem?.remove()
|
||||||
@@ -409,6 +447,7 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
|||||||
document.removeEventListener('template-builder:select-submitter', this.onSelectSubmitter)
|
document.removeEventListener('template-builder:select-submitter', this.onSelectSubmitter)
|
||||||
document.removeEventListener('template-builder:sync-custom-fields', this.onSyncCustomFields)
|
document.removeEventListener('template-builder:sync-custom-fields', this.onSyncCustomFields)
|
||||||
document.removeEventListener('template-builder:draw-field', this.onDrawField)
|
document.removeEventListener('template-builder:draw-field', this.onDrawField)
|
||||||
|
document.removeEventListener('native:builder-view', this.onBuilderView)
|
||||||
document.removeEventListener('turbo:before-cache', this.onBeforeCache)
|
document.removeEventListener('turbo:before-cache', this.onBeforeCache)
|
||||||
|
|
||||||
this.app?.unmount()
|
this.app?.unmount()
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import { renderStreamMessage } from '@hotwired/turbo'
|
import { renderStreamMessage, session } from '@hotwired/turbo'
|
||||||
|
|
||||||
export default class extends HTMLElement {
|
export default class extends HTMLElement {
|
||||||
connectedCallback () {
|
connectedCallback () {
|
||||||
if (!this.dataset.src) return
|
if (this.dataset.limit) this.replaceLimitParam()
|
||||||
|
|
||||||
|
if (!this.dataset.src) {
|
||||||
|
this.remove()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.observer = new IntersectionObserver((entries) => {
|
this.observer = new IntersectionObserver((entries) => {
|
||||||
if (entries.some((entry) => entry.isIntersecting)) {
|
if (entries.some((entry) => entry.isIntersecting)) {
|
||||||
@@ -17,6 +23,15 @@ export default class extends HTMLElement {
|
|||||||
this.observer?.disconnect()
|
this.observer?.disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replaceLimitParam () {
|
||||||
|
const url = new URL(window.location.href)
|
||||||
|
|
||||||
|
url.searchParams.set('limit', this.dataset.limit)
|
||||||
|
|
||||||
|
session.history.replace(url, session.history.restorationIdentifier)
|
||||||
|
session.view.lastRenderedLocation = url
|
||||||
|
}
|
||||||
|
|
||||||
load () {
|
load () {
|
||||||
if (this.loading || !this.dataset.src) return
|
if (this.loading || !this.dataset.src) return
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
const handlers = window.webkit?.messageHandlers
|
||||||
|
|
||||||
|
if (handlers?.native && window === window.top) {
|
||||||
|
handlers.native.postMessage({ type: 'reset' })
|
||||||
|
|
||||||
|
const reportPathConfiguration = () => {
|
||||||
|
const element = document.getElementById('native_path_configuration')
|
||||||
|
|
||||||
|
if (element) handlers.native.postMessage({ type: 'path-configuration', json: element.textContent })
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('turbo:load', reportPathConfiguration)
|
||||||
|
|
||||||
|
reportPathConfiguration()
|
||||||
|
|
||||||
|
const reportFlash = (element) => {
|
||||||
|
if (!element) return
|
||||||
|
|
||||||
|
if (element.dataset.notice) handlers.flash.postMessage({ style: 'notice', message: element.dataset.notice })
|
||||||
|
if (element.dataset.alert) handlers.flash.postMessage({ style: 'alert', message: element.dataset.alert })
|
||||||
|
|
||||||
|
element.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
const reportPageFlash = () => reportFlash(document.getElementById('native_flash'))
|
||||||
|
|
||||||
|
new MutationObserver(reportPageFlash).observe(document.documentElement, { childList: true, subtree: true })
|
||||||
|
|
||||||
|
document.addEventListener('turbo:load', reportPageFlash)
|
||||||
|
|
||||||
|
document.addEventListener('native:flash', (event) => {
|
||||||
|
reportFlash(new DOMParser().parseFromString(event.detail.html, 'text/html').getElementById('native_flash'))
|
||||||
|
})
|
||||||
|
|
||||||
|
reportPageFlash()
|
||||||
|
|
||||||
|
document.addEventListener('click', (event) => {
|
||||||
|
if (document.querySelector('native-modal')) return
|
||||||
|
|
||||||
|
const link = event.target.closest?.('a[data-turbo-frame="modal"], a[data-turbo-frame="drawer"]')
|
||||||
|
|
||||||
|
if (!link?.href) return
|
||||||
|
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
|
||||||
|
handlers.modal.postMessage({ action: 'open', url: link.href, modal: link.closest('[data-native-modal]')?.dataset.nativeModal || '' })
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
document.addEventListener('click', (event) => {
|
||||||
|
const link = event.target.closest?.('a[href]')
|
||||||
|
|
||||||
|
if (!link) return
|
||||||
|
|
||||||
|
const title = link.dataset.nativeTitle || link.closest('[data-native-title]')?.dataset.nativeTitle || link.innerText.trim()
|
||||||
|
|
||||||
|
if (!title) return
|
||||||
|
|
||||||
|
const actions = link.closest('[data-native-actions]')?.dataset.nativeActions
|
||||||
|
|
||||||
|
handlers.native.postMessage({ type: 'title', url: link.href, title, actions: actions ? actions.split(',') : null })
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
document.addEventListener('native:action', (event) => {
|
||||||
|
document.querySelector(`[data-native-id="${event.detail.id}"]`)?.click()
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener('native:title-bottom', (event) => {
|
||||||
|
const rect = document.querySelector('h1, h2')?.getBoundingClientRect()
|
||||||
|
|
||||||
|
event.detail.result = rect?.height ? rect.bottom - document.documentElement.getBoundingClientRect().top : null
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,10 +1,36 @@
|
|||||||
export default class extends HTMLElement {
|
export default class extends HTMLElement {
|
||||||
connectedCallback () {
|
connectedCallback () {
|
||||||
document.addEventListener('native:search', this.onSearch)
|
document.addEventListener('native:search', this.onSearch)
|
||||||
|
document.addEventListener('turbo:before-cache', this.onBeforeCache)
|
||||||
|
this.addEventListener('touchstart', this.onTouchStart, { passive: true })
|
||||||
|
this.addEventListener('scroll', this.onScroll, { capture: true, passive: true })
|
||||||
|
|
||||||
|
const scroller = this.querySelector('[data-scroll-top]')
|
||||||
|
|
||||||
|
if (scroller) scroller.scrollTop = scroller.dataset.scrollTop
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback () {
|
disconnectedCallback () {
|
||||||
document.removeEventListener('native:search', this.onSearch)
|
document.removeEventListener('native:search', this.onSearch)
|
||||||
|
document.removeEventListener('turbo:before-cache', this.onBeforeCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeCache = () => {
|
||||||
|
if (this.scroller) this.scroller.dataset.scrollTop = this.scroller.scrollTop
|
||||||
|
}
|
||||||
|
|
||||||
|
onTouchStart = () => {
|
||||||
|
this.isTouching = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onScroll = (event) => {
|
||||||
|
this.scroller = event.target
|
||||||
|
|
||||||
|
if (!this.isTouching) return
|
||||||
|
|
||||||
|
this.isTouching = false
|
||||||
|
|
||||||
|
window.webkit?.messageHandlers?.native?.postMessage({ type: 'dismiss-keyboard' })
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearch = (event) => {
|
onSearch = (event) => {
|
||||||
|
|||||||
@@ -5,20 +5,36 @@ export default class extends HTMLElement {
|
|||||||
this.files = []
|
this.files = []
|
||||||
|
|
||||||
document.addEventListener('turbo:submit-end', this.onSubmitEnd)
|
document.addEventListener('turbo:submit-end', this.onSubmitEnd)
|
||||||
|
document.addEventListener('native:upload-start', this.onUploadStart)
|
||||||
|
document.addEventListener('native:upload-chunk', this.onUploadChunk)
|
||||||
|
document.addEventListener('native:upload-submit', this.onUploadSubmit)
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback () {
|
disconnectedCallback () {
|
||||||
document.removeEventListener('turbo:submit-end', this.onSubmitEnd)
|
document.removeEventListener('turbo:submit-end', this.onSubmitEnd)
|
||||||
|
document.removeEventListener('native:upload-start', this.onUploadStart)
|
||||||
|
document.removeEventListener('native:upload-chunk', this.onUploadChunk)
|
||||||
|
document.removeEventListener('native:upload-submit', this.onUploadSubmit)
|
||||||
}
|
}
|
||||||
|
|
||||||
get form () {
|
get form () {
|
||||||
return this.querySelector('form')
|
return this.querySelector('form')
|
||||||
}
|
}
|
||||||
|
|
||||||
append (index, name, type, base64) {
|
onUploadStart = (event) => {
|
||||||
|
this.files = []
|
||||||
|
|
||||||
|
event.detail.result = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onUploadChunk = ({ detail: { index, name, type, chunk } }) => {
|
||||||
const file = this.files[index] ||= { name, type, chunks: [] }
|
const file = this.files[index] ||= { name, type, chunks: [] }
|
||||||
|
|
||||||
file.chunks.push(Uint8Array.from(atob(base64), (char) => char.charCodeAt(0)))
|
file.chunks.push(Uint8Array.from(atob(chunk), (char) => char.charCodeAt(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
onUploadSubmit = (event) => {
|
||||||
|
event.detail.result = this.submit(event.detail.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
async submit (url) {
|
async submit (url) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import './elements/native_bridge'
|
||||||
|
|
||||||
import { createApp, reactive } from 'vue'
|
import { createApp, reactive } from 'vue'
|
||||||
|
|
||||||
import Form from './submission_form/form'
|
import Form from './submission_form/form'
|
||||||
|
|||||||
@@ -16,13 +16,10 @@
|
|||||||
v-if="editable"
|
v-if="editable"
|
||||||
class="flex justify-between w-full"
|
class="flex justify-between w-full"
|
||||||
>
|
>
|
||||||
<div
|
<div class="flex flex-col w-[26px] max-md:w-7">
|
||||||
style="width: 26px"
|
|
||||||
class="flex flex-col"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
v-if="item.conditions?.length"
|
v-if="item.conditions?.length"
|
||||||
class="btn border-gray-300 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors p-0 document-control-button"
|
class="btn border-gray-300 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors p-0 document-control-button max-md:h-7 max-md:min-h-7"
|
||||||
@click.stop="openConditionsModal"
|
@click.stop="openConditionsModal"
|
||||||
>
|
>
|
||||||
<IconRouteAltLeft
|
<IconRouteAltLeft
|
||||||
@@ -31,7 +28,7 @@
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div class="max-md:[&_.btn]:h-7 max-md:[&_.btn]:min-h-7 max-md:[&_.btn]:text-sm">
|
||||||
<ReplaceButton
|
<ReplaceButton
|
||||||
v-if="withReplaceButton"
|
v-if="withReplaceButton"
|
||||||
:template-id="template.id"
|
:template-id="template.id"
|
||||||
@@ -55,8 +52,7 @@
|
|||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="btn border-gray-300 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors document-control-button px-0"
|
class="btn border-gray-300 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content transition-colors document-control-button px-0 w-6 h-6 max-md:w-7 max-md:h-7 max-md:min-h-7"
|
||||||
style="width: 24px; height: 24px"
|
|
||||||
@click.stop
|
@click.stop
|
||||||
>
|
>
|
||||||
<IconDotsVertical
|
<IconDotsVertical
|
||||||
@@ -141,15 +137,13 @@
|
|||||||
:class="{ 'opacity-0 group-hover:opacity-100': !withVisibleControls }"
|
:class="{ 'opacity-0 group-hover:opacity-100': !withVisibleControls }"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="btn border-gray-300 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors document-control-button"
|
class="btn border-gray-300 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content transition-colors document-control-button w-6 h-6 max-md:w-7 max-md:h-7 max-md:min-h-7 max-md:text-sm"
|
||||||
style="width: 24px; height: 24px"
|
|
||||||
@click.stop="$emit('up', item)"
|
@click.stop="$emit('up', item)"
|
||||||
>
|
>
|
||||||
↑
|
↑
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn border-gray-300 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors document-control-button"
|
class="btn border-gray-300 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content transition-colors document-control-button w-6 h-6 max-md:w-7 max-md:h-7 max-md:min-h-7 max-md:text-sm"
|
||||||
style="width: 24px; height: 24px"
|
|
||||||
@click.stop="$emit('down', item)"
|
@click.stop="$emit('down', item)"
|
||||||
>
|
>
|
||||||
↓
|
↓
|
||||||
|
|||||||
@@ -4,4 +4,5 @@
|
|||||||
<%= render 'shared/meta' %>
|
<%= render 'shared/meta' %>
|
||||||
<% if turbo_native_app? %>
|
<% if turbo_native_app? %>
|
||||||
<meta name="turbo-cache-control" content="no-preview">
|
<meta name="turbo-cache-control" content="no-preview">
|
||||||
|
<style>native-action { display: none !important; }</style>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"strings": {
|
||||||
|
"search": <%== t('search').to_json %>,
|
||||||
|
"create": <%== t('create').to_json %>,
|
||||||
|
"documents": <%== t('documents').to_json %>,
|
||||||
|
"fields": <%== t('fields').to_json %>,
|
||||||
|
"more": <%== t('more').to_json %>,
|
||||||
|
"close": <%== t('close').to_json %>,
|
||||||
|
"cancel": <%== t('cancel').to_json %>,
|
||||||
|
"ok": <%== t('ok').to_json %>,
|
||||||
|
"failed_to_download_files": <%== t('failed_to_download_files').to_json %>,
|
||||||
|
"failed_to_upload_files": <%== t('failed_to_upload_files').to_json %>,
|
||||||
|
"failed_to_save_the_scan": <%== t('failed_to_save_the_scan').to_json %>,
|
||||||
|
"failed_to_scan_the_document": <%== t('failed_to_scan_the_document').to_json %>,
|
||||||
|
"document_scanning_is_not_available_on_this_device": <%== t('document_scanning_is_not_available_on_this_device').to_json %>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"patterns": [".*"],
|
||||||
|
"properties": {
|
||||||
|
"query_string_presentation": "replace"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patterns": ["^/search($|\\?)"],
|
||||||
|
"properties": {
|
||||||
|
"pull_to_refresh_enabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patterns": ["^/settings", "^/reports"],
|
||||||
|
"properties": {
|
||||||
|
"large_title": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patterns": ["^/settings$", "^/settings/personalization$"],
|
||||||
|
"properties": {
|
||||||
|
"large_title": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patterns": ["^/templates($|\\?)"],
|
||||||
|
"properties": {
|
||||||
|
"native_tab_bar": true,
|
||||||
|
"tab_title": <%== t('templates').to_json %>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patterns": ["^/submissions($|\\?)"],
|
||||||
|
"properties": {
|
||||||
|
"native_tab_bar": true,
|
||||||
|
"tab_title": <%== t('submissions').to_json %>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patterns": ["^/folders/\\d+"],
|
||||||
|
"properties": {
|
||||||
|
"native_actions": ["menu"],
|
||||||
|
"native_tab_bar": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patterns": ["^/templates/archived($|\\?)", "^/templates/shared($|\\?)", "^/submissions/archived($|\\?)"],
|
||||||
|
"properties": {
|
||||||
|
"native_tab_bar": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patterns": ["^/templates/\\d+(/archived)?($|\\?)"],
|
||||||
|
"properties": {
|
||||||
|
"native_actions": ["menu"],
|
||||||
|
"large_title": true,
|
||||||
|
"native_tab_bar": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patterns": ["^/templates/\\d+/edit"],
|
||||||
|
"properties": {
|
||||||
|
"native_actions": ["check", "menu"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patterns": ["^/submissions/\\d+($|\\?)"],
|
||||||
|
"properties": {
|
||||||
|
"native_actions": ["menu"],
|
||||||
|
"native_tabs": <%== [t('document'), t('recipients')].to_json %>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -28,7 +28,9 @@
|
|||||||
<open-modal src="<%= url_for(**url_params, params: modal_uri.query_values) %>"></open-modal>
|
<open-modal src="<%= url_for(**url_params, params: modal_uri.query_values) %>"></open-modal>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<turbo-frame id="modal"></turbo-frame>
|
<% unless content_for?(:native_modal) %>
|
||||||
|
<turbo-frame id="modal"></turbo-frame>
|
||||||
|
<% end %>
|
||||||
<turbo-frame id="drawer"></turbo-frame>
|
<turbo-frame id="drawer"></turbo-frame>
|
||||||
<%= render turbo_native_app? ? 'shared/native_navbar' : 'shared/navbar' %>
|
<%= render turbo_native_app? ? 'shared/native_navbar' : 'shared/navbar' %>
|
||||||
<% if flash.present? %><%= render 'shared/flash' %><% end %>
|
<% if flash.present? %><%= render 'shared/flash' %><% end %>
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
<%= render 'shared/turbo_modal', title: t('reveal_api_key') do %>
|
<%= render 'shared/turbo_modal', title: t('reveal_api_key') do %>
|
||||||
<%= form_tag settings_reveal_access_token_path, enctype: 'multipart/form-data', data: { turbo_frame: :_top } do %>
|
<native-event data-on="submit" data-name="native:turbo-stream" class="block">
|
||||||
<div class="form-control">
|
<%= form_tag settings_reveal_access_token_path, enctype: 'multipart/form-data', data: { turbo_frame: :_top } do %>
|
||||||
<%= label_tag :password, t('enter_your_password_to_reveal_the_api_key'), class: 'label' %>
|
<div class="form-control">
|
||||||
<%= password_field_tag :password, nil, class: 'base-input', autocomplete: 'current-password', required: true, autofocus: true, placeholder: t('password') %>
|
<%= label_tag :password, t('enter_your_password_to_reveal_the_api_key'), class: 'label' %>
|
||||||
<% if local_assigns[:error_message].present? %>
|
<%= password_field_tag :password, nil, class: 'base-input', autocomplete: 'current-password', required: true, autofocus: true, placeholder: t('password') %>
|
||||||
<span class="label-text-alt text-red-400 mt-1"><%= local_assigns[:error_message] %></span>
|
<% if local_assigns[:error_message].present? %>
|
||||||
<% end %>
|
<span class="label-text-alt text-red-400 mt-1"><%= local_assigns[:error_message] %></span>
|
||||||
</div>
|
<% end %>
|
||||||
<div class="form-control mt-4">
|
</div>
|
||||||
<%= submit_tag t('submit'), class: 'base-button' %>
|
<div class="form-control mt-4">
|
||||||
</div>
|
<%= submit_tag t('submit'), class: 'base-button' %>
|
||||||
<% end %>
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</native-event>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
<infinite-scroll id="infinite_scroll" class="<%= pagy.next ? 'col-span-full flex justify-center py-2' : 'hidden' %>" data-limit="<%= pagy.page * pagy.limit %>" data-src="<%= url_for(params: request.query_parameters.merge(local_assigns[:query_params].to_h).merge('page' => pagy.next)) if pagy.next %>">
|
<% if pagy.next || pagy.page > 1 %>
|
||||||
<%= svg_icon('loader', class: 'w-6 h-6 animate-spin') if pagy.next %>
|
<infinite-scroll id="infinite_scroll" class="col-span-full flex justify-center py-2" data-limit="<%= pagy.page * pagy.limit if pagy.page > 1 %>" data-src="<%= url_for(params: request.query_parameters.merge(local_assigns[:query_params].to_h).merge('page' => pagy.next)) if pagy.next %>">
|
||||||
</infinite-scroll>
|
<%= svg_icon('loader', class: 'w-6 h-6 animate-spin') %>
|
||||||
|
</infinite-scroll>
|
||||||
|
<% end %>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<script type="application/json" id="native_path_configuration"><%= render partial: 'layouts/native_path_configuration', formats: :json %></script>
|
||||||
<% if signed_in? %>
|
<% if signed_in? %>
|
||||||
<native-action data-placement="profile" data-icon="user">
|
<native-action data-placement="profile" data-icon="user">
|
||||||
<%= link_to t('profile'), settings_profile_index_path %>
|
<%= link_to t('profile'), settings_profile_index_path %>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<% if turbo_native_app? && !turbo_frame_request? %>
|
<% if turbo_native_ios? || (turbo_native_app? && !turbo_frame_request?) %>
|
||||||
|
<% content_for(:native_modal, 'true') %>
|
||||||
<% content_for(:html_title, local_assigns[:title]) if local_assigns[:title] %>
|
<% content_for(:html_title, local_assigns[:title]) if local_assigns[:title] %>
|
||||||
<turbo-frame id="modal">
|
<turbo-frame id="modal">
|
||||||
<native-modal class="block max-w-lg mx-auto pb-6" data-close-after-submit="<%= local_assigns.key?(:close_after_submit) ? local_assigns[:close_after_submit] : true %>">
|
<native-modal class="block max-w-lg mx-auto pb-6" data-close-after-submit="<%= local_assigns.key?(:close_after_submit) ? local_assigns[:close_after_submit] : true %>">
|
||||||
|
|||||||
@@ -387,6 +387,6 @@
|
|||||||
</toggle-visible>
|
</toggle-visible>
|
||||||
<% end %>
|
<% end %>
|
||||||
</main>
|
</main>
|
||||||
<% unless request.headers['HTTP_X_TURBO'] %>
|
<% unless request.headers['HTTP_X_TURBO_REQUEST_ID'] %>
|
||||||
<%= render 'scripts/autosize_field' %>
|
<%= render 'scripts/autosize_field' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<% else %>
|
<% else %>
|
||||||
<div class="flex-1 min-w-0 md:flex-none">
|
<div class="flex-1 min-w-0 md:flex-none">
|
||||||
<%= render 'shared/clipboard_copy', text: submit_form_url(slug: submitter.slug, host: form_link_host), class: 'btn btn-sm max-sm:normal-case btn-neutral text-white md:w-36 flex z-[1]', icon_class: 'w-6 h-6 max-sm:w-5 max-sm:h-5 text-white', copy_title: t('copy_link').length < 10 ? t('copy_link') : t('copy'), copy_title_md: t('link'), copied_title_md: t('copied') %>
|
<%= render 'shared/clipboard_copy', text: submit_form_url(slug: submitter.slug, host: form_link_host), class: 'btn btn-sm max-sm:normal-case btn-neutral text-white md:w-36 flex z-[1]', icon_class: 'w-6 h-6 max-sm:w-[18px] max-sm:h-[18px] text-white', copy_title: t('copy_link').length < 10 ? t('copy_link') : t('copy'), copy_title_md: t('link'), copied_title_md: t('copied') %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -52,9 +52,11 @@
|
|||||||
</ol>
|
</ol>
|
||||||
<% unless webhook_event.status == 'pending' %>
|
<% unless webhook_event.status == 'pending' %>
|
||||||
<div class="absolute right-4 top-3">
|
<div class="absolute right-4 top-3">
|
||||||
<toggle-classes data-classes="btn-disabled">
|
<native-event data-on="submit" data-name="native:turbo-stream" data-dismiss="false">
|
||||||
<%= button_to button_title(title: t('resend'), disabled_with: t('awaiting'), icon: svg_icon('rotate', class: 'w-4 h-4'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), resend_settings_webhook_event_path(webhook_url.id, webhook_event.uuid), form: { id: button_uuid = SecureRandom.uuid }, params: { button_id: button_uuid }, class: 'btn btn-neutral btn-sm text-white', method: :post %>
|
<toggle-classes data-classes="btn-disabled">
|
||||||
</toggle-classes>
|
<%= button_to button_title(title: t('resend'), disabled_with: t('awaiting'), icon: svg_icon('rotate', class: 'w-4 h-4'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), resend_settings_webhook_event_path(webhook_url.id, webhook_event.uuid), form: { id: button_uuid = SecureRandom.uuid }, params: { button_id: button_uuid }, class: 'btn btn-neutral btn-sm text-white', method: :post %>
|
||||||
|
</toggle-classes>
|
||||||
|
</native-event>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
@@ -1013,6 +1013,14 @@ en: &en
|
|||||||
phone_2fa_verification_text_message: Phone 2FA verification text message.
|
phone_2fa_verification_text_message: Phone 2FA verification text message.
|
||||||
notification_email_sent_to_the_sender_user_and_bcc_addresses_once_the_submission_is_completed_by_all_parties: Notification email sent to the sender user and BCC addresses once the submission is completed by all parties.
|
notification_email_sent_to_the_sender_user_and_bcc_addresses_once_the_submission_is_completed_by_all_parties: Notification email sent to the sender user and BCC addresses once the submission is completed by all parties.
|
||||||
completed_documents_copy_email_sent_to_recipients_upon_completion_containing_their_documents: Completed documents copy email sent to recipients upon completion containing their documents.
|
completed_documents_copy_email_sent_to_recipients_upon_completion_containing_their_documents: Completed documents copy email sent to recipients upon completion containing their documents.
|
||||||
|
fields: Fields
|
||||||
|
more: More
|
||||||
|
ok: OK
|
||||||
|
failed_to_download_files: Failed to download files
|
||||||
|
failed_to_upload_files: Failed to upload files
|
||||||
|
failed_to_save_the_scan: Failed to save the scan
|
||||||
|
failed_to_scan_the_document: Failed to scan the document
|
||||||
|
document_scanning_is_not_available_on_this_device: Document scanning is not available on this device
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Your email address has been successfully confirmed.
|
confirmed: Your email address has been successfully confirmed.
|
||||||
@@ -2146,6 +2154,14 @@ es: &es
|
|||||||
open_file_from: 'Abrir archivo desde'
|
open_file_from: 'Abrir archivo desde'
|
||||||
cancel: Cancelar
|
cancel: Cancelar
|
||||||
open: Abrir
|
open: Abrir
|
||||||
|
fields: Campos
|
||||||
|
more: Más
|
||||||
|
ok: Aceptar
|
||||||
|
failed_to_download_files: No se pudieron descargar los archivos
|
||||||
|
failed_to_upload_files: No se pudieron subir los archivos
|
||||||
|
failed_to_save_the_scan: No se pudo guardar el escaneo
|
||||||
|
failed_to_scan_the_document: No se pudo escanear el documento
|
||||||
|
document_scanning_is_not_available_on_this_device: El escaneo de documentos no está disponible en este dispositivo
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Tu dirección de correo electrónico ha sido confirmada correctamente.
|
confirmed: Tu dirección de correo electrónico ha sido confirmada correctamente.
|
||||||
@@ -3293,6 +3309,14 @@ it: &it
|
|||||||
open_file_from: 'Aprire file da'
|
open_file_from: 'Aprire file da'
|
||||||
cancel: Annulla
|
cancel: Annulla
|
||||||
open: Apri
|
open: Apri
|
||||||
|
fields: Campi
|
||||||
|
more: Altro
|
||||||
|
ok: OK
|
||||||
|
failed_to_download_files: Impossibile scaricare i file
|
||||||
|
failed_to_upload_files: Impossibile caricare i file
|
||||||
|
failed_to_save_the_scan: Impossibile salvare la scansione
|
||||||
|
failed_to_scan_the_document: Impossibile scansionare il documento
|
||||||
|
document_scanning_is_not_available_on_this_device: La scansione dei documenti non è disponibile su questo dispositivo
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Il tuo indirizzo email è stato confermato con successo.
|
confirmed: Il tuo indirizzo email è stato confermato con successo.
|
||||||
@@ -4423,6 +4447,14 @@ fr: &fr
|
|||||||
open_file_from: 'Ouvrir le fichier depuis'
|
open_file_from: 'Ouvrir le fichier depuis'
|
||||||
cancel: Annuler
|
cancel: Annuler
|
||||||
open: Ouvrir
|
open: Ouvrir
|
||||||
|
fields: Champs
|
||||||
|
more: Plus
|
||||||
|
ok: OK
|
||||||
|
failed_to_download_files: Impossible de télécharger les fichiers
|
||||||
|
failed_to_upload_files: Impossible de téléverser les fichiers
|
||||||
|
failed_to_save_the_scan: Impossible d’enregistrer la numérisation
|
||||||
|
failed_to_scan_the_document: Impossible de numériser le document
|
||||||
|
document_scanning_is_not_available_on_this_device: La numérisation de documents n’est pas disponible sur cet appareil
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Votre adresse e-mail a été confirmée avec succès.
|
confirmed: Votre adresse e-mail a été confirmée avec succès.
|
||||||
@@ -5569,6 +5601,14 @@ pt: &pt
|
|||||||
open_file_from: 'Abrir arquivo de'
|
open_file_from: 'Abrir arquivo de'
|
||||||
cancel: Cancelar
|
cancel: Cancelar
|
||||||
open: Abrir
|
open: Abrir
|
||||||
|
fields: Campos
|
||||||
|
more: Mais
|
||||||
|
ok: OK
|
||||||
|
failed_to_download_files: Não foi possível baixar os arquivos
|
||||||
|
failed_to_upload_files: Não foi possível enviar os arquivos
|
||||||
|
failed_to_save_the_scan: Não foi possível salvar a digitalização
|
||||||
|
failed_to_scan_the_document: Não foi possível digitalizar o documento
|
||||||
|
document_scanning_is_not_available_on_this_device: A digitalização de documentos não está disponível neste dispositivo
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Seu endereço de e-mail foi confirmado com sucesso.
|
confirmed: Seu endereço de e-mail foi confirmado com sucesso.
|
||||||
@@ -6715,6 +6755,14 @@ de: &de
|
|||||||
open_file_from: 'Datei öffnen von'
|
open_file_from: 'Datei öffnen von'
|
||||||
cancel: Abbrechen
|
cancel: Abbrechen
|
||||||
open: Öffnen
|
open: Öffnen
|
||||||
|
fields: Felder
|
||||||
|
more: Mehr
|
||||||
|
ok: OK
|
||||||
|
failed_to_download_files: Dateien konnten nicht heruntergeladen werden
|
||||||
|
failed_to_upload_files: Dateien konnten nicht hochgeladen werden
|
||||||
|
failed_to_save_the_scan: Scan konnte nicht gespeichert werden
|
||||||
|
failed_to_scan_the_document: Dokument konnte nicht gescannt werden
|
||||||
|
document_scanning_is_not_available_on_this_device: Das Scannen von Dokumenten ist auf diesem Gerät nicht verfügbar
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Ihre E-Mail-Adresse wurde erfolgreich bestätigt.
|
confirmed: Ihre E-Mail-Adresse wurde erfolgreich bestätigt.
|
||||||
@@ -8385,6 +8433,14 @@ nl: &nl
|
|||||||
open_file_from: 'Bestand openen van'
|
open_file_from: 'Bestand openen van'
|
||||||
cancel: Annuleren
|
cancel: Annuleren
|
||||||
open: Openen
|
open: Openen
|
||||||
|
fields: Velden
|
||||||
|
more: Meer
|
||||||
|
ok: OK
|
||||||
|
failed_to_download_files: Bestanden konden niet worden gedownload
|
||||||
|
failed_to_upload_files: Bestanden konden niet worden geüpload
|
||||||
|
failed_to_save_the_scan: Scan kon niet worden opgeslagen
|
||||||
|
failed_to_scan_the_document: Document kon niet worden gescand
|
||||||
|
document_scanning_is_not_available_on_this_device: Documenten scannen is niet beschikbaar op dit apparaat
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Uw e-mailadres is succesvol bevestigd.
|
confirmed: Uw e-mailadres is succesvol bevestigd.
|
||||||
|
|||||||
Reference in new issue
Block a user