Skip to content
Merged
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
2 changes: 1 addition & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
</template>

<script setup lang="ts">
import { ref, computed, defineOptions } from 'vue'
import { ref, computed } from 'vue'

defineOptions({ name: 'LibreSign' })

Expand Down
2 changes: 1 addition & 1 deletion src/ExternalApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</template>

<script setup lang="ts">
import { computed, defineOptions } from 'vue'
import { computed } from 'vue'

defineOptions({ name: 'LibreSignExternal' })

Expand Down
110 changes: 110 additions & 0 deletions src/composables/useFileEntry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { computed } from '@vue/reactivity'

import { useSidebarStore } from '../store/sidebar.js'

type FileEntryMetadata = {
extension?: string
}

export type FileEntrySource = {
id: number
name: string
nodeType?: string
created_at?: number | string | Date | null
metadata?: FileEntryMetadata
[key: string]: unknown
}

type FileEntryStore = {
selectFile: (id: number) => void
}

type ActionsMenuStore = {
opened: number | null
}

type FileEntryProps = {
source: FileEntrySource
}

export function useFileEntry(
props: FileEntryProps,
options: {
actionsMenuStore: ActionsMenuStore
filesStore: FileEntryStore
},
) {
const sidebarStore = useSidebarStore()

const mtime = computed(() => new Date(props.source?.created_at || Date.now()))
const openedMenu = computed({
get: () => options.actionsMenuStore.opened === props.source.id,
set: (opened: boolean) => {
options.actionsMenuStore.opened = opened ? props.source.id : null
},
})
const mtimeOpacity = computed(() => {
const maxOpacityTime = 31 * 24 * 60 * 60 * 1000
const timestamp = mtime.value?.getTime?.()

if (!timestamp) {
return {}
}

const ratio = Math.round(Math.min(100, 100 * (maxOpacityTime - (Date.now() - timestamp)) / maxOpacityTime))
if (ratio < 0) {
return {}
}

return {
color: `color-mix(in srgb, var(--color-main-text) ${ratio}%, var(--color-text-maxcontrast))`,
}
})
const fileExtension = computed(() => {
if (props.source.nodeType === 'envelope') {
return ''
}
return props.source.metadata?.extension ? `.${props.source.metadata.extension}` : '.pdf'
})

function onRightClick(event: MouseEvent) {
if (openedMenu.value) {
return
}

options.actionsMenuStore.opened = props.source.id
event.preventDefault()
event.stopPropagation()

const target = event.currentTarget as HTMLElement | null
const root = target?.closest('.app-content') as HTMLElement | null
if (!root) {
return
}

const contentRect = root.getBoundingClientRect()
root.style.setProperty('--mouse-pos-x', `${Math.max(0, event.clientX - contentRect.left - 200)}px`)
root.style.setProperty('--mouse-pos-y', `${Math.max(0, event.clientY - contentRect.top)}px`)
}

function openDetailsIfAvailable(event: Event) {
event.preventDefault()
event.stopPropagation()
options.filesStore.selectFile(props.source.id)
sidebarStore.activeRequestSignatureTab()
}

return {
mtime,
openedMenu,
mtimeOpacity,
fileExtension,
onRightClick,
openDetailsIfAvailable,
}
}
12 changes: 0 additions & 12 deletions src/mixins/isTouchDevice.js

This file was deleted.

104 changes: 0 additions & 104 deletions src/mixins/signingOrderMixin.js

This file was deleted.

6 changes: 6 additions & 0 deletions src/tests/composables/useIsTouchDevice.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@ describe('useIsTouchDevice composable', () => {

expect(isTouchDevice.value).toBe(expected)
})

it('keeps the same value across repeated reads', () => {
const { isTouchDevice } = useIsTouchDevice()

expect(isTouchDevice.value).toBe(isTouchDevice.value)
})
})
107 changes: 107 additions & 0 deletions src/tests/composables/useSigningOrder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,121 @@ describe('useSigningOrder composable', () => {
recalculateSigningOrders(signers, 0)
expect(signers[0].signingOrder).toBe(1)
})

it('increments following signers when inserting at start', () => {
const signers: Signer[] = [
{ signingOrder: 0 },
{ signingOrder: 1 },
{ signingOrder: 2 },
]

recalculateSigningOrders(signers, 0)

expect(signers[0].signingOrder).toBe(1)
expect(signers[1].signingOrder).toBe(2)
expect(signers[2].signingOrder).toBe(3)
})

it('assigns next order when adding at end', () => {
const signers: Signer[] = [
{ signingOrder: 1 },
{ signingOrder: 2 },
]

recalculateSigningOrders(signers, 1)

expect(signers[1].signingOrder).toBe(2)
})

it('handles reordering with original orders when moving forward', () => {
const signers: Signer[] = [
{ signingOrder: 1 },
{ signingOrder: 2 },
{ signingOrder: 3 },
]

recalculateSigningOrders(signers, 2, [1, 2, 3], 0)

expect(signers[2].signingOrder).toBeGreaterThanOrEqual(1)
})

it('handles reordering with original orders when moving backward', () => {
const signers: Signer[] = [
{ signingOrder: 1 },
{ signingOrder: 2 },
{ signingOrder: 3 },
]

recalculateSigningOrders(signers, 1, [1, 2, 3], 2)

expect(signers[1].signingOrder).toBeGreaterThanOrEqual(1)
})

it('handles equal previous and next orders', () => {
const signers: Signer[] = [
{ signingOrder: 1 },
{ signingOrder: 1 },
{ signingOrder: 1 },
]

recalculateSigningOrders(signers, 1)

expect(signers[1].signingOrder).toBeGreaterThanOrEqual(1)
})

it('handles descending orders', () => {
const signers: Signer[] = [
{ signingOrder: 3 },
{ signingOrder: 2 },
{ signingOrder: 1 },
]

recalculateSigningOrders(signers, 1)

expect(signers[1].signingOrder).toBeGreaterThanOrEqual(1)
})
})

describe('normalizeSigningOrders', () => {
it('handles empty signers array', () => {
const signers: Signer[] = []

normalizeSigningOrders(signers)

expect(signers).toEqual([])
})

it('normalizes to start at 1', () => {
const signers: Signer[] = [{ signingOrder: 5 }, { signingOrder: 6 }]
normalizeSigningOrders(signers)
expect(signers[0].signingOrder).toBe(1)
expect(signers[1].signingOrder).toBe(2)
})

it('handles negative orders', () => {
const signers: Signer[] = [
{ signingOrder: -1 },
{ signingOrder: 0 },
]

normalizeSigningOrders(signers)

expect(signers[0].signingOrder).toBe(1)
expect(signers[1].signingOrder).toBe(2)
})

it('closes gaps in the sequence', () => {
const signers: Signer[] = [
{ signingOrder: 1 },
{ signingOrder: 5 },
{ signingOrder: 10 },
]

normalizeSigningOrders(signers)

expect(signers[0].signingOrder).toBe(1)
expect(signers[1].signingOrder).toBe(2)
expect(signers[2].signingOrder).toBe(3)
})
})
})
Loading
Loading