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
99 changes: 87 additions & 12 deletions src/components/PdfEditor/PdfEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ type PdfElementsInstance = {
selectedDocIndex?: number
autoFitZoom?: boolean
}
type PdfElementsRuntimeInstance = PdfElementsInstance & {
handleMouseMove?: (event: { type: string, touches: Array<{ clientX: number, clientY: number }> }) => void
finishAdding?: () => void
previewElement?: Record<string, unknown> | null
previewVisible?: boolean
}

defineOptions({
name: 'PdfEditor',
Expand Down Expand Up @@ -155,6 +161,7 @@ const pdfElements = ref<PdfElementsInstance | null>(null)
const pendingAddedObjectCount = ref<number | null>(null)

let pendingAddCheckTimer: ReturnType<typeof setTimeout> | null = null
let pendingAddCheckRetries = 0

const ignoreClickOutsideSelectors = computed(() => ['.action-item__popper', '.action-item'])

Expand Down Expand Up @@ -271,6 +278,7 @@ function clearPendingAddCheck() {
clearTimeout(pendingAddCheckTimer)
pendingAddCheckTimer = null
}
pendingAddCheckRetries = 0
pendingAddedObjectCount.value = null
}

Expand All @@ -283,11 +291,29 @@ function checkSignerAdded() {
pendingAddCheckTimer = null
const isAddingMode = pdfElements.value?.isAddingMode === true
const objectsAfter = getTotalObjectsCount()
pendingAddedObjectCount.value = null

if (!isAddingMode && objectsAfter > objectsBefore) {
if (objectsAfter > objectsBefore) {
clearPendingAddCheck()
emit('pdf-editor:signer-added')
return
}

// Fallback: once add mode ends, unblock the UI even if the object count
// comparison was not conclusive due timing/reactivity.
if (!isAddingMode) {
clearPendingAddCheck()
emit('pdf-editor:signer-added')
return
}

// Poll while the external component still processes placement.
if (pendingAddCheckRetries < 300) {
pendingAddCheckRetries++
pendingAddCheckTimer = setTimeout(checkSignerAdded, 100)
return
}

clearPendingAddCheck()
}

function scheduleSignerAddedCheck() {
Expand All @@ -300,6 +326,43 @@ function scheduleSignerAddedCheck() {
pendingAddCheckTimer = setTimeout(checkSignerAdded, 0)
}

function handleDocumentTouchEnd(event: Event) {
if (pendingAddedObjectCount.value === null) {
return
}

const instance = pdfElements.value as PdfElementsRuntimeInstance | null
const touchEvent = event as TouchEvent
const touchPoint = touchEvent.changedTouches?.[0]
if (!instance || !touchPoint) {
scheduleSignerAddedCheck()
return
}

// Work around mobile tap placement timing in pdf-elements: touchend has no
// touches[0], so preview may never become visible on first tap.
if (instance.isAddingMode && instance.previewElement && !instance.previewVisible && instance.handleMouseMove) {
touchEvent.preventDefault?.()
touchEvent.stopImmediatePropagation?.()

instance.handleMouseMove({
type: 'touchmove',
touches: [{ clientX: touchPoint.clientX, clientY: touchPoint.clientY }],
})
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (instance.isAddingMode) {
instance.finishAdding?.()
}
scheduleSignerAddedCheck()
})
})
return
}

scheduleSignerAddedCheck()
}

function startAddingSigner(signer: SignerSummaryRecord | SignerDetailRecord | null | undefined, size: { width?: number, height?: number }) {
if (!pdfElements.value || !size?.width || !size?.height) {
return false
Expand All @@ -319,6 +382,11 @@ function startAddingSigner(signer: SignerSummaryRecord | SignerDetailRecord | nu
signer: signerPayload,
})
pendingAddedObjectCount.value = getTotalObjectsCount()
pendingAddCheckRetries = 0
if (pendingAddCheckTimer !== null) {
clearTimeout(pendingAddCheckTimer)
}
pendingAddCheckTimer = setTimeout(checkSignerAdded, 100)

return true
}
Expand Down Expand Up @@ -383,15 +451,11 @@ async function waitForPageRender(docIndex: number, pageIndex: number) {

onMounted(() => {
ensurePdfWorker()
document.addEventListener('mouseup', scheduleSignerAddedCheck)
document.addEventListener('touchend', scheduleSignerAddedCheck)
document.addEventListener('keyup', scheduleSignerAddedCheck)
document.addEventListener('touchend', handleDocumentTouchEnd)
})

onBeforeUnmount(() => {
document.removeEventListener('mouseup', scheduleSignerAddedCheck)
document.removeEventListener('touchend', scheduleSignerAddedCheck)
document.removeEventListener('keyup', scheduleSignerAddedCheck)
document.removeEventListener('touchend', handleDocumentTouchEnd)
clearPendingAddCheck()
})

Expand Down Expand Up @@ -433,21 +497,32 @@ defineExpose({
}

.action-btn {
border: none;
background: transparent;
color: #ffffff;
border: 1px solid #cbd5e1;
background: #f8fafc;
color: #0f172a;
padding: 4px;
min-height: 0;
min-width: 0;
border-radius: 4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
transition: background 120ms ease;

&:hover {
background: rgba(255, 255, 255, 0.1);
background: #e2e8f0;
}

:deep(svg),
:deep(.icon-vue),
:deep(.material-design-icon),
:deep([class*='icon']) {
color: currentColor;
fill: currentColor;
stroke: currentColor;
opacity: 1;
}
}

Expand Down
36 changes: 22 additions & 14 deletions src/components/Request/VisibleElements.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,22 @@
</template>
</Signer>
</ul>
<NcButton v-if="canSave"
:variant="variantOfSaveButton"
:wide="true"
:class="{ disabled: signerSelected }"
@click="save()">
{{ t('libresign', 'Save') }}
</NcButton>

<NcButton v-if="canSign"
:variant="variantOfSignButton"
:wide="true"
@click="goToSign">
{{ t('libresign', 'Sign') }}
</NcButton>
<div class="sign-details__actions">
<NcButton v-if="canSave"
:variant="variantOfSaveButton"
:wide="true"
:class="{ disabled: signerSelected }"
@click="save()">
{{ t('libresign', 'Save') }}
</NcButton>

<NcButton v-if="canSign"
:variant="variantOfSignButton"
:wide="true"
@click="goToSign">
{{ t('libresign', 'Sign') }}
</NcButton>
</div>
</div>
<PdfEditor v-if="!filesStore.loading && pdfFiles.length > 0"
ref="pdfEditor"
Expand Down Expand Up @@ -947,6 +949,12 @@ defineExpose({
margin: 3px 3px 1em 3px;
}
}
&__actions {
position: sticky;
bottom: 0;
background-color: var(--color-main-background);
padding-top: 4px;
}
.disabled {
pointer-events: none;
visibility: hidden;
Expand Down
19 changes: 18 additions & 1 deletion src/components/RightSidebar/RequestSignatureTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,18 @@ function getValidationFileUuid() {
return null
}

function getSignRouteUuid() {
const file = filesStore.getFile()
const signer = file?.signers?.find((row: EditableRequestSigner) => row.me) || file?.signers?.[0]
const fromFile = [file?.signUuid, signer?.sign_uuid]
.find((value): value is string => typeof value === 'string' && value.length > 0)
const fromSettings = typeof file?.settings?.signerFileUuid === 'string' && file.settings.signerFileUuid.length > 0
? file.settings.signerFileUuid
: null
const fromState = loadState<string | null>('libresign', 'sign_request_uuid', null)
return fromFile || fromSettings || (typeof fromState === 'string' && fromState.length > 0 ? fromState : null)
}

function validationFile() {
const targetUuid = getValidationFileUuid()
if (!targetUuid) {
Expand Down Expand Up @@ -1067,7 +1079,11 @@ async function sign() {
return
}

const uuid = 'signUuid' in file ? file.signUuid : null
const uuid = getSignRouteUuid()
if (!uuid) {
showError(t('libresign', 'Signer request not found'))
return
}
if (props.useModal) {
const absoluteUrl = generateUrl('/apps/libresign/p/sign/{uuid}/pdf', { uuid })
const route = router.resolve({ name: 'SignPDFExternal', params: { uuid } })
Expand Down Expand Up @@ -1293,6 +1309,7 @@ defineExpose({
isSignElementsAvailable,
closeModal,
getValidationFileUuid,
getSignRouteUuid,
validationFile,
addSigner,
editSigner,
Expand Down
16 changes: 11 additions & 5 deletions src/store/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -714,13 +714,18 @@ const _filesStore = defineStore('files', () => {
const isSigned = (signer) => Array.isArray(signer.signed)
? signer.signed.length > 0
: !!signer.signed
const signerFileUuid = typeof selectedFile?.settings?.signerFileUuid === 'string'
? selectedFile.settings.signerFileUuid
: ''
const mySigners = selectedFile?.signers?.filter(signer => signer.me) || []
if (isFullSigned(selectedFile)
|| selectedFile.status <= 0
|| mySigners.length === 0
|| mySigners.some((signer) => isSigned(signer))) {
return false
}
if (mySigners.length === 0) {
return signerFileUuid.length > 0
}

const flow = selectedFile?.signatureFlow
const isOrderedNumeric = flow === 'ordered_numeric' || flow === 2
Expand Down Expand Up @@ -867,10 +872,11 @@ const _filesStore = defineStore('files', () => {
}
const coordinates = element.coordinates && typeof element.coordinates === 'object'
? {
x: element.coordinates.x,
y: element.coordinates.y,
w: element.coordinates.w,
h: element.coordinates.h,
page: element.coordinates.page,
top: element.coordinates.top,
left: element.coordinates.left,
width: element.coordinates.width,
height: element.coordinates.height,
}
: undefined
return {
Expand Down
Loading