From 8537fa8a2a0ce412bc7f157e76f223dbf7376f91 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Sun, 26 Apr 2026 12:38:30 +0200 Subject: [PATCH] Refactor browser extension and mobile app to use const for trash retention duration (#1967) --- .../background/VaultMessageHandler.ts | 7 ++++--- .../popup/pages/items/RecentlyDeleted.tsx | 20 ++++++++----------- .../src/i18n/locales/en.json | 4 ++-- .../src/utils/VaultMergeService.ts | 6 ++++-- .../src/utils/constants/vault.ts | 6 ++++++ apps/mobile-app/app/(tabs)/items/deleted.tsx | 11 +++++----- apps/mobile-app/constants/vault.ts | 6 ++++++ apps/mobile-app/i18n/locales/en.json | 4 ++-- 8 files changed, 38 insertions(+), 26 deletions(-) create mode 100644 apps/browser-extension/src/utils/constants/vault.ts create mode 100644 apps/mobile-app/constants/vault.ts diff --git a/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts b/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts index 9d454f828..7bb4ff26d 100644 --- a/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts +++ b/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts @@ -2,6 +2,7 @@ import * as OTPAuth from 'otpauth'; import { storage } from 'wxt/utils/storage'; +import { TRASH_RETENTION_DAYS } from '@/utils/constants/vault'; import type { EncryptionKeyDerivationParams } from '@/utils/dist/core/models/metadata'; import { FieldKey, ItemTypes, createSystemField, type Item } from '@/utils/dist/core/models/vault'; import type { Vault, VaultResponse, VaultPostResponse } from '@/utils/dist/core/models/webapi'; @@ -762,7 +763,7 @@ export async function handleClearPersistedFormValues(): Promise { /** * Upload a new version of the vault to the server using the provided sqlite client. - * Prunes expired trash items (older than 30 days) before uploading. + * Prunes expired trash items before uploading. */ async function uploadNewVaultToServer(sqliteClient: SqliteClient) : Promise { let updatedVaultData = sqliteClient.exportToBase64(); @@ -775,11 +776,11 @@ async function uploadNewVaultToServer(sqliteClient: SqliteClient) : Promise 0) { console.info(`[VaultSync] Pruned expired items from trash (${pruneResult.statementCount} statements)`); updatedVaultData = pruneResult.prunedVaultBase64; diff --git a/apps/browser-extension/src/entrypoints/popup/pages/items/RecentlyDeleted.tsx b/apps/browser-extension/src/entrypoints/popup/pages/items/RecentlyDeleted.tsx index 3ed31c4d0..8d06b1d32 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/items/RecentlyDeleted.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/items/RecentlyDeleted.tsx @@ -2,12 +2,14 @@ import React, { useState, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import ConfirmDeleteModal from '@/entrypoints/popup/components/Dialogs/ConfirmDeleteModal'; +import ItemIcon from '@/entrypoints/popup/components/Items/ItemIcon'; import LoadingSpinner from '@/entrypoints/popup/components/LoadingSpinner'; import PageTitle from '@/entrypoints/popup/components/PageTitle'; import { useDb } from '@/entrypoints/popup/context/DbContext'; import { useHeaderButtons } from '@/entrypoints/popup/context/HeaderButtonsContext'; import { useVaultMutate } from '@/entrypoints/popup/hooks/useVaultMutate'; +import { TRASH_RETENTION_DAYS } from '@/utils/constants/vault'; import type { Item } from '@/utils/dist/core/models/vault'; import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; @@ -15,10 +17,10 @@ import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; /** * Calculate days remaining until permanent deletion. * @param deletedAt - ISO timestamp when item was deleted - * @param retentionDays - Number of days to retain (default 30) + * @param retentionDays - Number of days to retain (defaults to TRASH_RETENTION_DAYS) * @returns Number of days remaining, or 0 if already expired */ -const getDaysRemaining = (deletedAt: string, retentionDays: number = 30): number => { +const getDaysRemaining = (deletedAt: string, retentionDays: number = TRASH_RETENTION_DAYS): number => { const deletedDate = new Date(deletedAt); const expiryDate = new Date(deletedDate.getTime() + retentionDays * 24 * 60 * 60 * 1000); const now = new Date(); @@ -162,19 +164,19 @@ const RecentlyDeleted: React.FC = () => { {items.length === 0 ? (

{t('recentlyDeleted.noItems')}

-

{t('recentlyDeleted.noItemsDescription')}

+

{t('recentlyDeleted.noItemsDescription', { days: TRASH_RETENTION_DAYS })}

) : ( <>

- {t('recentlyDeleted.description')} + {t('recentlyDeleted.description', { days: TRASH_RETENTION_DAYS })}

    {items.map(item => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const deletedAt = (item as any).DeletedAt; - const daysRemaining = deletedAt ? getDaysRemaining(deletedAt) : 30; + const daysRemaining = deletedAt ? getDaysRemaining(deletedAt) : TRASH_RETENTION_DAYS; return (
  • @@ -183,13 +185,7 @@ const RecentlyDeleted: React.FC = () => { {/* Item card content (simplified) */}
    - {item.Logo && ( - - )} + {item.Name || t('items.untitled')} diff --git a/apps/browser-extension/src/i18n/locales/en.json b/apps/browser-extension/src/i18n/locales/en.json index 66d152881..0ec8fb96a 100644 --- a/apps/browser-extension/src/i18n/locales/en.json +++ b/apps/browser-extension/src/i18n/locales/en.json @@ -507,8 +507,8 @@ "recentlyDeleted": { "title": "Recently Deleted", "noItems": "No deleted items", - "noItemsDescription": "Items you delete will appear here for 30 days before being permanently removed.", - "description": "These items will be permanently deleted after 30 days. You can restore them or delete them immediately.", + "noItemsDescription": "Items you delete will appear here for {{days}} days before being permanently removed.", + "description": "These items will be permanently deleted after {{days}} days. You can restore them or delete them immediately.", "restore": "Restore", "deletePermanently": "Delete Permanently", "emptyAll": "Empty All", diff --git a/apps/browser-extension/src/utils/VaultMergeService.ts b/apps/browser-extension/src/utils/VaultMergeService.ts index 711913953..26c8ce624 100644 --- a/apps/browser-extension/src/utils/VaultMergeService.ts +++ b/apps/browser-extension/src/utils/VaultMergeService.ts @@ -1,6 +1,8 @@ import initSqlJs, { Database, SqlJsStatic, SqlValue } from 'sql.js'; import { browser } from 'wxt/browser'; +import { TRASH_RETENTION_DAYS } from '@/utils/constants/vault'; + import init, { getSyncableTableNames, mergeVaults, pruneVault } from './dist/core/rust/aliasvault_core.js'; /** @@ -244,10 +246,10 @@ export class VaultMergeService { * 5. Export pruned database * * @param vaultBase64 - The vault as base64 SQLite - * @param retentionDays - Number of days to keep items in trash (default: 30) + * @param retentionDays - Number of days to keep items in trash (defaults to TRASH_RETENTION_DAYS) * @returns PruneResult with the pruned vault as base64 */ - public async prune(vaultBase64: string, retentionDays: number = 30): Promise { + public async prune(vaultBase64: string, retentionDays: number = TRASH_RETENTION_DAYS): Promise { try { // Initialize Rust WASM await this.initRust(); diff --git a/apps/browser-extension/src/utils/constants/vault.ts b/apps/browser-extension/src/utils/constants/vault.ts new file mode 100644 index 000000000..8b8a854c0 --- /dev/null +++ b/apps/browser-extension/src/utils/constants/vault.ts @@ -0,0 +1,6 @@ +/** + * Number of days that soft-deleted items stay in the "Recently Deleted" trash + * before being permanently pruned during vault sync. Mirrors the server-side + * default exposed via Config.TrashRetentionDays in the Blazor client. + */ +export const TRASH_RETENTION_DAYS = 30; diff --git a/apps/mobile-app/app/(tabs)/items/deleted.tsx b/apps/mobile-app/app/(tabs)/items/deleted.tsx index d95a0195c..c880be4a5 100644 --- a/apps/mobile-app/app/(tabs)/items/deleted.tsx +++ b/apps/mobile-app/app/(tabs)/items/deleted.tsx @@ -21,15 +21,16 @@ import { ItemIcon } from '@/components/items/ItemIcon'; import { ThemedContainer } from '@/components/themed/ThemedContainer'; import { ThemedScrollView } from '@/components/themed/ThemedScrollView'; import { ThemedText } from '@/components/themed/ThemedText'; +import { TRASH_RETENTION_DAYS } from '@/constants/vault'; import { useDb } from '@/context/DbContext'; /** * Calculate days remaining until permanent deletion. * @param deletedAt - ISO timestamp when item was deleted - * @param retentionDays - Number of days to retain (default 30) + * @param retentionDays - Number of days to retain (defaults to TRASH_RETENTION_DAYS) * @returns Number of days remaining, or 0 if already expired */ -const getDaysRemaining = (deletedAt: string, retentionDays: number = 30): number => { +const getDaysRemaining = (deletedAt: string, retentionDays: number = TRASH_RETENTION_DAYS): number => { const deletedDate = new Date(deletedAt); const expiryDate = new Date(deletedDate.getTime() + retentionDays * 24 * 60 * 60 * 1000); const now = new Date(); @@ -268,7 +269,7 @@ export default function RecentlyDeletedScreen(): React.ReactNode { * Render an item card. */ const renderItem = (item: ItemWithDeletedAt): React.ReactElement => { - const daysRemaining = item.DeletedAt ? getDaysRemaining(item.DeletedAt) : 30; + const daysRemaining = item.DeletedAt ? getDaysRemaining(item.DeletedAt) : TRASH_RETENTION_DAYS; return ( @@ -343,7 +344,7 @@ export default function RecentlyDeletedScreen(): React.ReactNode { - {t('items.recentlyDeleted.description')} + {t('items.recentlyDeleted.description', { days: TRASH_RETENTION_DAYS })} {items.map(renderItem)} @@ -353,7 +354,7 @@ export default function RecentlyDeletedScreen(): React.ReactNode { {t('items.recentlyDeleted.noItems')} - {t('items.recentlyDeleted.noItemsDescription')} + {t('items.recentlyDeleted.noItemsDescription', { days: TRASH_RETENTION_DAYS })} )} diff --git a/apps/mobile-app/constants/vault.ts b/apps/mobile-app/constants/vault.ts new file mode 100644 index 000000000..8b8a854c0 --- /dev/null +++ b/apps/mobile-app/constants/vault.ts @@ -0,0 +1,6 @@ +/** + * Number of days that soft-deleted items stay in the "Recently Deleted" trash + * before being permanently pruned during vault sync. Mirrors the server-side + * default exposed via Config.TrashRetentionDays in the Blazor client. + */ +export const TRASH_RETENTION_DAYS = 30; diff --git a/apps/mobile-app/i18n/locales/en.json b/apps/mobile-app/i18n/locales/en.json index e5671b820..86125b42a 100644 --- a/apps/mobile-app/i18n/locales/en.json +++ b/apps/mobile-app/i18n/locales/en.json @@ -518,8 +518,8 @@ "recentlyDeleted": { "title": "Recently Deleted", "noItems": "No deleted items", - "noItemsDescription": "Items you delete will appear here for 30 days before being permanently removed.", - "description": "These items will be permanently deleted after 30 days. You can restore them or delete them immediately.", + "noItemsDescription": "Items you delete will appear here for {{days}} days before being permanently removed.", + "description": "These items will be permanently deleted after {{days}} days. You can restore them or delete them immediately.", "restore": "Restore", "deletePermanently": "Delete Permanently", "emptyAll": "Empty All",