1515 <div v-else class =" visible-elements-container" >
1616 <div class =" sign-details" >
1717 <div class =" modal_name" >
18- <NcChip :text =" statusLabel"
18+ <NcChip :text =" statusLabel ?? '' "
1919 :variant =" isDraft ? 'warning' : 'primary'"
20- :aria-label =" t('libresign', 'Document status: {status}', { status: statusLabel })"
20+ :aria-label =" t('libresign', 'Document status: {status}', { status: statusLabel ?? '' })"
2121 no-close />
2222 <h2 class =" name" >{{ document.name }}</h2 >
2323 </div >
7373 :file-names =" pdfFileNames"
7474 :signers =" document.signers"
7575 @pdf-editor:end-init =" updateSigners"
76- @pdf-editor:on-delete-signer =" onDeleteSigner " >
76+ @pdf-editor:on-delete-signer =" handleDeleteSigner " >
7777 </PdfEditor >
7878 </div >
7979 </NcModal >
@@ -86,9 +86,12 @@ import axios from '@nextcloud/axios'
8686import { getCapabilities } from ' @nextcloud/capabilities'
8787import { showSuccess , showError } from ' @nextcloud/dialogs'
8888import { subscribe , unsubscribe } from ' @nextcloud/event-bus'
89+ import type { Event as NextcloudEvent , EventHandler } from ' @nextcloud/event-bus'
8990import { loadState } from ' @nextcloud/initial-state'
9091import { generateOcsUrl } from ' @nextcloud/router'
9192import { computed , getCurrentInstance , nextTick , onBeforeUnmount , onMounted , ref } from ' vue'
93+ import type { ComponentPublicInstance } from ' vue'
94+ import type { PDFElementObject } from ' @libresign/pdf-elements'
9295
9396import NcButton from ' @nextcloud/vue/components/NcButton'
9497import NcChip from ' @nextcloud/vue/components/NcChip'
@@ -103,56 +106,54 @@ import { FILE_STATUS } from '../../constants.js'
103106import { useFilesStore } from ' ../../store/files.js'
104107import {
105108 aggregateVisibleElementsByFiles ,
109+ type DocumentData ,
110+ type FileData ,
106111 findFileById ,
107112 getFileSigners ,
108113 getFileUrl ,
109114 getVisibleElementsFromDocument ,
110115 idsMatch ,
116+ type Signer as VisibleElementsSigner ,
117+ type VisibleElement ,
111118} from ' ../../services/visibleElementsService'
112-
113- type VisibleElementCoordinate = {
114- page: number
115- width: number
116- height: number
117- left: number
118- top: number
119- }
120-
121- type VisibleElementPayload = {
122- type: ' signature'
123- elementId? : string
124- fileId? : number
125- signRequestId? : number | string
126- coordinates: VisibleElementCoordinate
127- }
119+ import type { NextcloudCapabilities } from ' ../../types/capabilities'
128120
129121type SignerIdentifyMethod = {
130122 method: string
131123 value: string
132124}
133125
134- type FileSigner = {
135- signRequestId? : number | string
126+ type FileSigner = VisibleElementsSigner & {
136127 identifyMethods? : SignerIdentifyMethod []
137- [key : string ]: unknown
138128}
139129
140- type DocumentFile = {
130+ type VisibleElementPayload = VisibleElement & {
131+ type: ' signature'
132+ elementId? : string
133+ fileId? : number
134+ signRequestId? : number | string
135+ coordinates: {
136+ page: number
137+ width: number
138+ height: number
139+ left: number
140+ top: number
141+ }
142+ }
143+
144+ type DocumentFile = FileData & {
141145 id: number
142146 name: string
143147 metadata? : {
144148 extension? : string
145149 p? : number
146150 d? : Array <{ h? : number }>
147151 }
148- file? : unknown
149- files? : Array <{ file? : unknown }>
150152 visibleElements? : VisibleElementPayload [] | null
151153 signers? : FileSigner []
152- [key : string ]: unknown
153154}
154155
155- type DocumentModel = {
156+ type DocumentModel = DocumentData & {
156157 id? : number
157158 uuid? : string
158159 name? : string
@@ -162,8 +163,7 @@ type DocumentModel = {
162163 settings? : { signerFileUuid? : string }
163164 files? : DocumentFile []
164165 visibleElements? : VisibleElementPayload []
165- signers? : Array <Record <string , unknown >>
166- [key : string ]: unknown
166+ signers? : FileSigner []
167167}
168168
169169type FilePageInfo = {
@@ -173,13 +173,15 @@ type FilePageInfo = {
173173 fileName: string
174174}
175175
176+ type PdfInput = string | Blob | ArrayBuffer | ArrayBufferView | Record <string , unknown >
177+
176178type PdfObjectSigner = {
177179 element? : { elementId? : string }
178180 identifyMethods? : SignerIdentifyMethod []
179181 [key : string ]: unknown
180182}
181183
182- type PdfObject = {
184+ type PdfObject = PDFElementObject & {
183185 signer? : PdfObjectSigner
184186 pageNumber: number
185187 x: number
@@ -196,7 +198,7 @@ type PdfElementsRef = {
196198 isAddingMode? : boolean
197199}
198200
199- type PdfEditorRef = {
201+ type PdfEditorRef = ComponentPublicInstance & {
200202 $refs? : { pdfElements? : PdfElementsRef }
201203 startAddingSigner? : (signer : Record <string , unknown >, size : { width: number ; height: number }) => boolean
202204 cancelAdding? : () => void
@@ -213,32 +215,68 @@ type FilesStore = {
213215 saveOrUpdateSignatureRequest: (payload : { visibleElements: VisibleElementPayload [] }) => Promise <SaveResponse >
214216}
215217
218+ const normalizeVisibleElements = (elements : VisibleElement []): VisibleElementPayload [] =>
219+ elements .flatMap ((element ) => {
220+ if (element .type !== ' signature' || ! element .coordinates ) {
221+ return []
222+ }
223+
224+ const page = Number (element .coordinates .page )
225+ const left = Number (element .coordinates .left )
226+ const top = Number (element .coordinates .top )
227+ const width = Number ((element .coordinates as VisibleElementPayload [' coordinates' ]).width )
228+ const height = Number ((element .coordinates as VisibleElementPayload [' coordinates' ]).height )
229+
230+ if (! [page , left , top , width , height ].every (Number .isFinite )) {
231+ return []
232+ }
233+
234+ return [{
235+ type: ' signature' ,
236+ elementId: typeof element .elementId === ' string' ? element .elementId : undefined ,
237+ fileId: typeof element .fileId === ' number' ? element .fileId : Number (element .fileId ),
238+ signRequestId: element .signRequestId ,
239+ coordinates: {
240+ page ,
241+ left ,
242+ top ,
243+ width ,
244+ height ,
245+ },
246+ } satisfies VisibleElementPayload ]
247+ })
248+
216249defineOptions ({
217250 name: ' VisibleElements' ,
218251})
219252
220- const filesStore = useFilesStore () as FilesStore
253+ const filesStore = useFilesStore () as unknown as FilesStore
221254const instance = getCurrentInstance ()
222255const pdfEditor = ref <PdfEditorRef | null >(null )
223256const canRequestSign = ref (loadState (' libresign' , ' can_request_sign' , false ))
224257const modal = ref (false )
225258const loading = ref (false )
226259const signerSelected = ref <Record <string , unknown > | null >(null )
227- const width = ref (getCapabilities ().libresign .config [' sign-elements' ][' full-signature-width' ])
228- const height = ref (getCapabilities ().libresign .config [' sign-elements' ][' full-signature-height' ])
260+ const capabilities = getCapabilities () as NextcloudCapabilities
261+ const width = ref (capabilities .libresign ?.config ?.[' sign-elements' ]?.[' full-signature-width' ] ?? 180 )
262+ const height = ref (capabilities .libresign ?.config ?.[' sign-elements' ]?.[' full-signature-height' ] ?? 60 )
229263const filePagesMap = ref <Record <number , FilePageInfo >>({})
230264const elementsLoaded = ref (false )
231265
232- const document = computed (() => filesStore .getFile ())
266+ const document = computed <DocumentModel >(() => filesStore .getFile ())
267+ const documentFiles = computed <DocumentFile []>(() => Array .isArray (document .value .files ) ? document .value .files as DocumentFile [] : [])
233268const status = computed (() => Number (document .value ?.status ?? - 1 ))
234269const isDraft = computed (() => status .value === FILE_STATUS .DRAFT )
235- const canSave = computed (() => [FILE_STATUS .DRAFT , FILE_STATUS .ABLE_TO_SIGN , FILE_STATUS .PARTIAL_SIGNED ].includes (status .value ))
270+ const canSave = computed (() => ( [FILE_STATUS .DRAFT , FILE_STATUS .ABLE_TO_SIGN , FILE_STATUS .PARTIAL_SIGNED ] as number []) .includes (status .value ))
236271const canSign = computed (() => status .value === FILE_STATUS .ABLE_TO_SIGN && (document .value ?.settings ?.signerFileUuid ?? ' ' ).length > 0 )
237272const variantOfSaveButton = computed (() => canSave .value ? ' primary' : ' secondary' )
238273const variantOfSignButton = computed (() => canSave .value ? ' secondary' : ' primary' )
239274const statusLabel = computed (() => document .value .statusText )
240- const pdfFiles = computed (() => (document .value .files || []).map (file => getFileUrl (file )).filter (Boolean ))
241- const pdfFileNames = computed (() => (document .value .files || []).map (file => ` ${file .name }.${file .metadata ?.extension || ' pdf' } ` ))
275+ const pdfFiles = computed <PdfInput []>(() => documentFiles .value .flatMap ((file ) => {
276+ const fileUrl = getFileUrl (file )
277+ return fileUrl ? [fileUrl ] : []
278+ }))
279+ const pdfFileNames = computed (() => documentFiles .value .map (file => ` ${file .name }.${file .metadata ?.extension || ' pdf' } ` ))
242280const documentNameWithExtension = computed (() => {
243281 const currentDocument = document .value
244282 if (! currentDocument .metadata ?.extension ) {
@@ -260,13 +298,13 @@ async function showModal() {
260298 if (! canRequestSign .value ) {
261299 return
262300 }
263- if (getCapabilities ()? .libresign ?.config ?.[' sign-elements' ]?.[' is-available' ] === false ) {
301+ if (capabilities .libresign ?.config ?.[' sign-elements' ]?.[' is-available' ] === false ) {
264302 return
265303 }
266304 modal .value = true
267305 filesStore .loading = true
268306
269- if (! document .value . files || document . value . files .length === 0 ) {
307+ if (documentFiles .value .length === 0 ) {
270308 await fetchFiles ()
271309 }
272310
@@ -282,34 +320,36 @@ async function fetchFiles() {
282320 },
283321 })
284322 const childFiles = response ?.data ?.ocs ?.data ?.data || []
285- document .value .files = Array .isArray (childFiles ) ? childFiles : []
323+ document .value .files = Array .isArray (childFiles ) ? childFiles as DocumentFile [] : []
286324
287- const allVisibleElements = aggregateVisibleElementsByFiles (document .value . files )
325+ const allVisibleElements = aggregateVisibleElementsByFiles (documentFiles .value )
288326 if (allVisibleElements .length > 0 ) {
289- document .value .visibleElements = allVisibleElements
327+ document .value .visibleElements = normalizeVisibleElements ( allVisibleElements )
290328 return
291329 }
292330
293331 const nestedDocumentElements = getVisibleElementsFromDocument (document .value )
294332 if (nestedDocumentElements .length > 0 ) {
295- document .value .visibleElements = nestedDocumentElements
333+ document .value .visibleElements = normalizeVisibleElements ( nestedDocumentElements )
296334 }
297335}
298336
299337function buildFilePagesMap() {
300338 filePagesMap .value = {}
301339
302- const filesToProcess = document .value .files || []
303- if (! Array .isArray (filesToProcess )) {
304- return
305- }
340+ const filesToProcess = documentFiles .value
306341
307342 let currentPage = 1
308343 filesToProcess .forEach ((file , index ) => {
309344 const pageCount = file .metadata ?.p || 0
345+ const fileId = typeof file .id === ' number' ? file .id : Number (file .id )
346+ if (! Number .isFinite (fileId )) {
347+ currentPage += pageCount
348+ return
349+ }
310350 for (let pageIndex = 0 ; pageIndex < pageCount ; pageIndex ++ ) {
311351 filePagesMap .value [currentPage + pageIndex ] = {
312- id: file . id ,
352+ id: fileId ,
313353 fileIndex: index ,
314354 startPage: currentPage ,
315355 fileName: file .name ,
@@ -327,13 +367,13 @@ function closeModal() {
327367}
328368
329369function getPageHeightForFile(fileId : number , page : number ) {
330- const filesToSearch = document .value . files || []
370+ const filesToSearch = documentFiles .value
331371 const fileInfo = filesToSearch .find (file => file .id === fileId )
332372 return fileInfo ?.metadata ?.d ?.[page - 1 ]?.h
333373}
334374
335375async function updateSigners() {
336- const filesToProcess = document .value . files || []
376+ const filesToProcess = documentFiles .value
337377 if (elementsLoaded .value || filesToProcess .length === 0 ) {
338378 return
339379 }
@@ -419,7 +459,7 @@ function stopAddSigner() {
419459 signerSelected .value = null
420460}
421461
422- async function onDeleteSigner(object : { signer ? : { element ? : { elementId ? : string } } } ) {
462+ async function onDeleteSigner(object : PdfObject ) {
423463 if (! object ?.signer ?.element ?.elementId ) {
424464 return
425465 }
@@ -429,6 +469,10 @@ async function onDeleteSigner(object: { signer?: { element?: { elementId?: strin
429469 }))
430470}
431471
472+ function handleDeleteSigner(object : unknown ) {
473+ void onDeleteSigner (object as PdfObject )
474+ }
475+
432476async function goToSign() {
433477 const uuid = document .value .settings ?.signerFileUuid
434478 if (await save ()) {
@@ -456,14 +500,22 @@ async function save() {
456500 }
457501}
458502
503+ const handleShowVisibleElements = (() => {
504+ void showModal ()
505+ }) as EventHandler <NextcloudEvent >
506+
507+ const handleSelectSigner = ((event : NextcloudEvent ) => {
508+ onSelectSigner ((event as CustomEvent <Record <string , unknown >>).detail )
509+ }) as EventHandler <NextcloudEvent >
510+
459511function buildVisibleElements() {
460512 const visibleElements: VisibleElementPayload [] = []
461- const currentFiles = document .value . files || []
513+ const currentFiles = documentFiles .value
462514 const pdfElements = getPdfElements ()
463515 const numDocuments = currentFiles .length
464516
465517 for (let docIndex = 0 ; docIndex < numDocuments ; docIndex ++ ) {
466- const objects = pdfElements ?.getAllObjects (docIndex ) || []
518+ const objects = ( pdfElements ?.getAllObjects (docIndex ) || []) as PdfObject []
467519 objects .forEach ((object ) => {
468520 if (! object .signer ) return
469521
@@ -506,9 +558,11 @@ function buildVisibleElements() {
506558 if (! fileInfo || ! Array .isArray (fileInfo .signers )) {
507559 return
508560 }
509- const envIdMethods = (object .signer .identifyMethods || []).map ((method ) => ` ${method .method }:${method .value } ` ).sort ().join (' |' )
561+ const envIdentifyMethods = Array .isArray (object .signer .identifyMethods ) ? object .signer .identifyMethods as SignerIdentifyMethod [] : []
562+ const envIdMethods = envIdentifyMethods .map ((method ) => ` ${method .method }:${method .value } ` ).sort ().join (' |' )
510563 const candidate = fileInfo .signers .find ((signer ) => {
511- const childIdMethods = (signer .identifyMethods || []).map ((method ) => ` ${method .method }:${method .value } ` ).sort ().join (' |' )
564+ const childIdentifyMethods = Array .isArray (signer .identifyMethods ) ? signer .identifyMethods : []
565+ const childIdMethods = childIdentifyMethods .map ((method : SignerIdentifyMethod ) => ` ${method .method }:${method .value } ` ).sort ().join (' |' )
512566 return childIdMethods === envIdMethods
513567 })
514568 if (! candidate ?.signRequestId ) {
@@ -524,13 +578,13 @@ function buildVisibleElements() {
524578}
525579
526580onMounted (() => {
527- subscribe (' libresign:show-visible-elements' , showModal )
528- subscribe (' libresign:visible-elements-select-signer' , onSelectSigner )
581+ subscribe (' libresign:show-visible-elements' , handleShowVisibleElements )
582+ subscribe (' libresign:visible-elements-select-signer' , handleSelectSigner )
529583})
530584
531585onBeforeUnmount (() => {
532- unsubscribe (' libresign:show-visible-elements' , showModal )
533- unsubscribe (' libresign:visible-elements-select-signer' , onSelectSigner )
586+ unsubscribe (' libresign:show-visible-elements' , handleShowVisibleElements )
587+ unsubscribe (' libresign:visible-elements-select-signer' , handleSelectSigner )
534588})
535589
536590defineExpose ({
0 commit comments