From 9c2735863c2f63d0c333627ac81626a1431055f1 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 26 Feb 2026 18:15:55 -0300 Subject: [PATCH 01/25] feat(helpers): add shared reactive initialActionCode ref Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- src/helpers/ActionMapping.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/helpers/ActionMapping.ts b/src/helpers/ActionMapping.ts index 14e7154da0..5257a04aa2 100644 --- a/src/helpers/ActionMapping.ts +++ b/src/helpers/ActionMapping.ts @@ -3,6 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +import { ref } from '@vue/reactivity' + interface ActionCodes { REDIRECT: number CREATE_ACCOUNT: number @@ -53,6 +55,14 @@ export const ACTION_CODE_TO_ROUTE: Readonly = Object.freeze({ [ACTION_CODES.INCOMPLETE_SETUP]: 'Incomplete', }) +/** + * Shared reactive ref for the initial action code injected by the server + * (#initial-state-libresign-action). Written once by router.ts beforeEach, + * read by App.vue. Lives here (not in router.ts) to avoid App.vue triggering + * the router module's side effects (createRouter, generateUrl) on import. + */ +export const initialActionCode = ref(0) + export const REQUIREMENT_TO_MODAL: Readonly = Object.freeze({ identificationDocuments: 'uploadDocuments', emailCode: 'emailToken', From 67f03987ae329f477d48358f0d41f1687a102f8b Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 26 Feb 2026 18:15:55 -0300 Subject: [PATCH 02/25] feat(composables): add useFileListWidth composable Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- src/composables/useFileListWidth.ts | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/composables/useFileListWidth.ts diff --git a/src/composables/useFileListWidth.ts b/src/composables/useFileListWidth.ts new file mode 100644 index 0000000000..0209189768 --- /dev/null +++ b/src/composables/useFileListWidth.ts @@ -0,0 +1,64 @@ +/*! + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2026 LibreCode coop and LibreCode contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { computed, readonly, ref } from '@vue/reactivity' + +/** The element we observe */ +let element: HTMLElement | undefined + +/** The current width of the element */ +const width = ref(0) + +const isWide = computed(() => width.value >= 1024) +const isMedium = computed(() => width.value >= 512 && width.value < 1024) +const isNarrow = computed(() => width.value < 512) + +const observer = new ResizeObserver(([el]) => { + if (!el) { + return + } + + const contentBoxSize = el.contentBoxSize?.[0] + if (contentBoxSize) { + // use the newer `contentBoxSize` property if available + width.value = contentBoxSize.inlineSize + } else { + // fall back to `contentRect` + width.value = el.contentRect.width + } +}) + +/** + * Update the observed element if needed and reconfigure the observer + */ +function updateObserver() { + const el = document.querySelector('#app-content-vue') ?? document.body + if (el !== element) { + // if already observing: stop observing the old element + if (element) { + observer.unobserve(element) + } + // observe the new element + observer.observe(el) + element = el + } +} + +/** + * Get the reactive width of the file list + */ +export function useFileListWidth() { + // Update the observer in setup context so we already have an initial value + updateObserver() + + return { + width: readonly(width), + + isWide, + isMedium, + isNarrow, + } +} From bc2905284fb7455ede87a7896fe56cc3abab98fc Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 26 Feb 2026 18:15:55 -0300 Subject: [PATCH 03/25] feat(utils): add timePresets utility with getTimePresets and getTimePresetRange Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- src/utils/timePresets.js | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/utils/timePresets.js diff --git a/src/utils/timePresets.js b/src/utils/timePresets.js new file mode 100644 index 0000000000..129a40a76e --- /dev/null +++ b/src/utils/timePresets.js @@ -0,0 +1,68 @@ +/** + * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { t } from '@nextcloud/l10n' + +const startOfToday = () => (new Date()).setHours(0, 0, 0, 0) +const endOfToday = () => (new Date()).setHours(23, 59, 59, 999) + +/** + * Returns the list of time preset definitions (id, label, start, end). + * Called as a function so that dates are always computed at the moment of use. + * + * @return {Array<{id: string, label: string, start: number, end: number}>} + */ +export function getTimePresets() { + const today = startOfToday() + const todayEnd = endOfToday() + + return [ + { + id: 'today', + label: t('libresign', 'Today'), + start: today, + end: todayEnd, + }, + { + id: 'last-7', + label: t('libresign', 'Last 7 days'), + start: today - (7 * 24 * 60 * 60 * 1000), + end: todayEnd, + }, + { + id: 'last-30', + label: t('libresign', 'Last 30 days'), + start: today - (30 * 24 * 60 * 60 * 1000), + end: todayEnd, + }, + { + id: 'this-year', + label: t('libresign', 'This year ({year})', { year: (new Date()).getFullYear() }), + start: new Date(today).setMonth(0, 1), + end: todayEnd, + }, + { + id: 'last-year', + label: t('libresign', 'Last year ({year})', { year: (new Date()).getFullYear() - 1 }), + start: new Date(today).setFullYear(new Date().getFullYear() - 1, 0, 1), + end: new Date(today).setMonth(0, 1), + }, + ] +} + +/** + * Returns the { start, end } range in milliseconds for the given preset id, + * or null if the id is not recognised. + * + * @param {string} presetId + * @return {{ start: number, end: number } | null} + */ +export function getTimePresetRange(presetId) { + if (!presetId) { + return null + } + const preset = getTimePresets().find(p => p.id === presetId) + return preset ? { start: preset.start, end: preset.end } : null +} From b2828bf524994af747c8b6dad0d68b205008be0b Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 26 Feb 2026 18:15:55 -0300 Subject: [PATCH 04/25] refactor(FileListFilter): replace NcActions with NcPopover-based layout Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../FileListFilter/FileListFilter.vue | 72 ++++++++++++------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/src/views/FilesList/FileListFilter/FileListFilter.vue b/src/views/FilesList/FileListFilter/FileListFilter.vue index 272aafdc63..a5cea01cef 100644 --- a/src/views/FilesList/FileListFilter/FileListFilter.vue +++ b/src/views/FilesList/FileListFilter/FileListFilter.vue @@ -3,38 +3,44 @@ - SPDX-License-Identifier: AGPL-3.0-or-later -->