mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-19 16:16:45 +00:00
feat(sync): extend auto-upload interval range and improve hints
This commit is contained in:
@@ -339,7 +339,18 @@ import {
|
||||
} from "@/lib/settings/settingsSearch";
|
||||
import { findSettingsSearchHighlightTarget } from "@/lib/settings/settingsSearchHighlight";
|
||||
import { LOCALE_OPTIONS } from "@/lib/app/localeOptions";
|
||||
import { DEFAULT_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES, DEFAULT_WEB_DAV_REMOTE_PATH, normalizedWebDavAutoUploadInterval, readSyncMethod, writeWebDavAutoUploadFields, writeWebDavBackupSelection, writeSyncMethod, type SyncMethod } from "@/lib/webdav/webdavAutoUploadConfig";
|
||||
import {
|
||||
DEFAULT_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES,
|
||||
DEFAULT_WEB_DAV_REMOTE_PATH,
|
||||
MAX_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES,
|
||||
formatWebDavAutoUploadInterval,
|
||||
normalizedWebDavAutoUploadInterval,
|
||||
readSyncMethod,
|
||||
writeWebDavAutoUploadFields,
|
||||
writeWebDavBackupSelection,
|
||||
writeSyncMethod,
|
||||
type SyncMethod,
|
||||
} from "@/lib/webdav/webdavAutoUploadConfig";
|
||||
import { apiUrl, webPath } from "@/lib/common/webPath";
|
||||
import { DEFAULT_DATA_GRID_FONT_FAMILY, DEFAULT_UI_FONT_FAMILY, normalizeCustomFontFamilyInput, readableFontFamily, SYSTEM_UI_FONT_FAMILY } from "@/lib/app/appFonts";
|
||||
import { buildFontFamilyOptions, displayFontFamily, isPresetFontFamily, loadSystemFontNames } from "@/lib/app/fontFamilyOptions";
|
||||
@@ -4460,6 +4471,15 @@ const webdavSecretsPassphrase = ref("");
|
||||
const webdavHasSavedSecretsPassphrase = ref(false);
|
||||
const webdavAutoUploadEnabled = ref(localStorage.getItem("dbx-webdav-auto-upload-enabled") === "true");
|
||||
const webdavAutoUploadIntervalMinutes = ref(Number(localStorage.getItem("dbx-webdav-auto-upload-interval-minutes") || String(DEFAULT_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES)));
|
||||
const webdavAutoUploadIntervalHint = computed(() => {
|
||||
const formatted = formatWebDavAutoUploadInterval(webdavAutoUploadIntervalMinutes.value, locale.value, {
|
||||
minute: t("settings.syncAutoUploadUnitMinute"),
|
||||
hour: t("settings.syncAutoUploadUnitHour"),
|
||||
day: t("settings.syncAutoUploadUnitDay"),
|
||||
});
|
||||
const key = formatted.approximate ? "settings.syncAutoUploadScheduleHintApproximate" : "settings.syncAutoUploadScheduleHint";
|
||||
return t(key, { interval: formatted.interval });
|
||||
});
|
||||
const webdavBusy = ref<"" | "test" | "upload" | "download">("");
|
||||
const webdavMessage = ref("");
|
||||
const webdavError = ref(false);
|
||||
@@ -9858,9 +9878,12 @@ LIMIT 100;</pre
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<Label for="webdav-auto-upload-interval" class="text-xs text-muted-foreground">{{ t("settings.syncAutoUploadInterval") }}</Label>
|
||||
<Input id="webdav-auto-upload-interval" v-model.number="webdavAutoUploadIntervalMinutes" type="number" min="1" max="1440" step="1" class="h-7 w-24 text-xs" :disabled="!webdavAutoUploadEnabled" />
|
||||
<Input id="webdav-auto-upload-interval" v-model.number="webdavAutoUploadIntervalMinutes" type="number" min="1" :max="MAX_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES" step="1" class="h-7 w-24 text-xs" :disabled="!webdavAutoUploadEnabled" />
|
||||
<span class="text-xs text-muted-foreground">{{ t("settings.syncAutoUploadMinutes") }}</span>
|
||||
</div>
|
||||
<p class="text-xs text-muted-foreground" aria-live="polite">
|
||||
{{ webdavAutoUploadIntervalHint }}
|
||||
</p>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ t("settings.syncAutoUploadDescription") }}
|
||||
</p>
|
||||
|
||||
@@ -6,23 +6,46 @@ import { readWebDavAutoUploadConfig, readWebDavBackupSelection, WEB_DAV_AUTO_UPL
|
||||
|
||||
export function useWebDavAutoUpload() {
|
||||
const settingsStore = useSettingsStore();
|
||||
let timer: ReturnType<typeof window.setInterval> | undefined;
|
||||
const maxTimeoutDelayMs = 2_147_000_000;
|
||||
let timer: ReturnType<typeof window.setTimeout> | undefined;
|
||||
let nextUploadAt = 0;
|
||||
let uploading = false;
|
||||
|
||||
function clearTimer() {
|
||||
if (!timer) return;
|
||||
window.clearInterval(timer);
|
||||
if (timer === undefined) return;
|
||||
window.clearTimeout(timer);
|
||||
timer = undefined;
|
||||
}
|
||||
|
||||
function armTimer() {
|
||||
const remainingMs = Math.max(0, nextUploadAt - Date.now());
|
||||
timer = window.setTimeout(
|
||||
() => {
|
||||
if (Date.now() < nextUploadAt) {
|
||||
armTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
void runAutoUpload();
|
||||
const config = readWebDavAutoUploadConfig();
|
||||
if (!config.enabled || !config.webDavConfig) {
|
||||
clearTimer();
|
||||
return;
|
||||
}
|
||||
nextUploadAt = Date.now() + config.intervalMinutes * 60_000;
|
||||
armTimer();
|
||||
},
|
||||
Math.min(remainingMs, maxTimeoutDelayMs),
|
||||
);
|
||||
}
|
||||
|
||||
function schedule() {
|
||||
clearTimer();
|
||||
const config = readWebDavAutoUploadConfig();
|
||||
if (!config.enabled || !config.webDavConfig) return;
|
||||
|
||||
timer = window.setInterval(() => {
|
||||
void runAutoUpload();
|
||||
}, config.intervalMinutes * 60_000);
|
||||
nextUploadAt = Date.now() + config.intervalMinutes * 60_000;
|
||||
armTimer();
|
||||
}
|
||||
|
||||
async function runAutoUpload() {
|
||||
|
||||
@@ -7690,6 +7690,11 @@ export default withEnglishFallback({
|
||||
syncAutoUpload: "Anlıq nüsxələri avtomatik yüklə",
|
||||
syncAutoUploadInterval: "Hər",
|
||||
syncAutoUploadMinutes: "dəqiqə",
|
||||
syncAutoUploadScheduleHint: "Snapshot hər {interval} avtomatik yüklənəcək.",
|
||||
syncAutoUploadScheduleHintApproximate: "Snapshot təxminən hər {interval} avtomatik yüklənəcək.",
|
||||
syncAutoUploadUnitMinute: "dəq.",
|
||||
syncAutoUploadUnitHour: "saat",
|
||||
syncAutoUploadUnitDay: "gün",
|
||||
syncAutoUploadDescription: "DBX açıq olduğu müddətdə işləyir. Yerli yadda saxlanılmış SQL-in gözlənilmədən əvəz olunmaması üçün endirmələr yenə əl ilə təsdiq tələb edir.",
|
||||
syncAutoUploadSuccess: "{path} yoluna {bytes} bayt avtomatik yükləndi.",
|
||||
syncDownloadSuccess: "{path} yolundan {bytes} bayt endirildi və tətbiq edildi.",
|
||||
|
||||
@@ -8752,6 +8752,11 @@ export default {
|
||||
syncAutoUpload: "Automatically upload snapshots",
|
||||
syncAutoUploadInterval: "Every",
|
||||
syncAutoUploadMinutes: "minutes",
|
||||
syncAutoUploadScheduleHint: "A snapshot will be uploaded every {interval}.",
|
||||
syncAutoUploadScheduleHintApproximate: "A snapshot will be uploaded about every {interval}.",
|
||||
syncAutoUploadUnitMinute: "min",
|
||||
syncAutoUploadUnitHour: "h",
|
||||
syncAutoUploadUnitDay: "d",
|
||||
syncAutoUploadDescription: "Runs while DBX is open. Downloads still require manual confirmation to avoid replacing local saved SQL unexpectedly.",
|
||||
syncAutoUploadSuccess: "Auto-uploaded {bytes} bytes to {path}.",
|
||||
syncDownloadSuccess: "Downloaded and applied {bytes} bytes from {path}.",
|
||||
|
||||
@@ -8018,6 +8018,11 @@ export default withEnglishFallback({
|
||||
syncAutoUpload: "Subir instantáneas automáticamente",
|
||||
syncAutoUploadInterval: "Cada",
|
||||
syncAutoUploadMinutes: "minutos",
|
||||
syncAutoUploadScheduleHint: "Se subirá una instantánea automáticamente cada {interval}.",
|
||||
syncAutoUploadScheduleHintApproximate: "Se subirá una instantánea automáticamente aproximadamente cada {interval}.",
|
||||
syncAutoUploadUnitMinute: "min",
|
||||
syncAutoUploadUnitHour: "h",
|
||||
syncAutoUploadUnitDay: "d",
|
||||
syncAutoUploadDescription: "Se ejecuta mientras DBX está abierto. Las descargas aún requieren confirmación manual para evitar reemplazar SQL guardado localmente.",
|
||||
syncAutoUploadSuccess: "Se subieron automáticamente {bytes} bytes a {path}.",
|
||||
syncDownloadSuccess: "Se descargaron y aplicaron {bytes} bytes desde {path}.",
|
||||
|
||||
@@ -8474,6 +8474,11 @@ export default withEnglishFallback({
|
||||
syncAutoUpload: "Unggah snapshot secara otomatis",
|
||||
syncAutoUploadInterval: "Setiap",
|
||||
syncAutoUploadMinutes: "menit",
|
||||
syncAutoUploadScheduleHint: "Snapshot akan diunggah otomatis setiap {interval}.",
|
||||
syncAutoUploadScheduleHintApproximate: "Snapshot akan diunggah otomatis sekitar setiap {interval}.",
|
||||
syncAutoUploadUnitMinute: "mnt",
|
||||
syncAutoUploadUnitHour: "jam",
|
||||
syncAutoUploadUnitDay: "hari",
|
||||
syncAutoUploadDescription: "Berjalan selama DBX terbuka. Unduhan tetap memerlukan konfirmasi manual untuk menghindari penggantian SQL tersimpan lokal secara tidak terduga.",
|
||||
syncAutoUploadSuccess: "Auto-unggah {bytes} byte ke {path}.",
|
||||
syncDownloadSuccess: "Berhasil mengunduh dan menerapkan {bytes} byte dari {path}.",
|
||||
|
||||
@@ -7795,6 +7795,11 @@ export default withEnglishFallback({
|
||||
syncAutoUpload: "Carica automaticamente gli snapshot",
|
||||
syncAutoUploadInterval: "Ogni",
|
||||
syncAutoUploadMinutes: "minuti",
|
||||
syncAutoUploadScheduleHint: "Lo snapshot verrà caricato automaticamente ogni {interval}.",
|
||||
syncAutoUploadScheduleHintApproximate: "Lo snapshot verrà caricato automaticamente circa ogni {interval}.",
|
||||
syncAutoUploadUnitMinute: "min",
|
||||
syncAutoUploadUnitHour: "h",
|
||||
syncAutoUploadUnitDay: "g",
|
||||
syncAutoUploadDescription: "Viene eseguito mentre DBX è aperto. I download richiedono ancora conferma manuale per evitare di sostituire SQL salvati localmente.",
|
||||
syncAutoUploadSuccess: "Caricati automaticamente {bytes} byte su {path}.",
|
||||
syncDownloadSuccess: "Scaricati e applicati {bytes} byte da {path}.",
|
||||
|
||||
@@ -7808,6 +7808,11 @@ export default withEnglishFallback({
|
||||
syncAutoUpload: "スナップショットを自動アップロード",
|
||||
syncAutoUploadInterval: "間隔",
|
||||
syncAutoUploadMinutes: "分",
|
||||
syncAutoUploadScheduleHint: "スナップショットを {interval} ごとに自動アップロードします。",
|
||||
syncAutoUploadScheduleHintApproximate: "スナップショットを約 {interval} ごとに自動アップロードします。",
|
||||
syncAutoUploadUnitMinute: "分",
|
||||
syncAutoUploadUnitHour: "時間",
|
||||
syncAutoUploadUnitDay: "日",
|
||||
syncAutoUploadDescription: "DBXを開いている間だけ実行されます。ローカルの保存済みSQLを誤って置き換えないよう、ダウンロードは手動確認が必要です。",
|
||||
syncAutoUploadSuccess: "{bytes}バイトを{path}に自動アップロードしました。",
|
||||
syncDownloadSuccess: "{path}から{bytes}バイトをダウンロードして適用しました。",
|
||||
|
||||
@@ -7635,6 +7635,11 @@ export default withEnglishFallback({
|
||||
syncAutoUpload: "자동으로 스냅샷 업로드",
|
||||
syncAutoUploadInterval: "매",
|
||||
syncAutoUploadMinutes: "분",
|
||||
syncAutoUploadScheduleHint: "스냅샷을 {interval}마다 자동 업로드합니다.",
|
||||
syncAutoUploadScheduleHintApproximate: "스냅샷을 약 {interval}마다 자동 업로드합니다.",
|
||||
syncAutoUploadUnitMinute: "분",
|
||||
syncAutoUploadUnitHour: "시간",
|
||||
syncAutoUploadUnitDay: "일",
|
||||
syncAutoUploadDescription: "DBX가 열려 있는 동안 실행됩니다. 로컬에 저장된 SQL이 예기치 않게 교체되는 것을 방지하기 위해 다운로드는 여전히 수동 확인이 필요합니다.",
|
||||
syncAutoUploadSuccess: "{path}에 {bytes}바이트를 자동 업로드했습니다.",
|
||||
syncDownloadSuccess: "{path}에서 {bytes}바이트를 다운로드하여 적용했습니다.",
|
||||
|
||||
@@ -7795,6 +7795,11 @@ export default withEnglishFallback({
|
||||
syncAutoUpload: "Enviar snapshots automaticamente",
|
||||
syncAutoUploadInterval: "A cada",
|
||||
syncAutoUploadMinutes: "minutos",
|
||||
syncAutoUploadScheduleHint: "Um snapshot será enviado automaticamente a cada {interval}.",
|
||||
syncAutoUploadScheduleHintApproximate: "Um snapshot será enviado automaticamente aproximadamente a cada {interval}.",
|
||||
syncAutoUploadUnitMinute: "min",
|
||||
syncAutoUploadUnitHour: "h",
|
||||
syncAutoUploadUnitDay: "d",
|
||||
syncAutoUploadDescription: "Executa enquanto o DBX está aberto. Downloads ainda exigem confirmação manual para evitar substituir SQL salvos localmente.",
|
||||
syncAutoUploadSuccess: "Enviados automaticamente {bytes} bytes para {path}.",
|
||||
syncDownloadSuccess: "Baixados e aplicados {bytes} bytes de {path}.",
|
||||
|
||||
@@ -9048,6 +9048,11 @@ export default withEnglishFallback({
|
||||
syncAutoUpload: "Автоматически загружать снимки",
|
||||
syncAutoUploadInterval: "Каждые",
|
||||
syncAutoUploadMinutes: "мин.",
|
||||
syncAutoUploadScheduleHint: "Снимок будет автоматически загружаться каждые {interval}.",
|
||||
syncAutoUploadScheduleHintApproximate: "Снимок будет автоматически загружаться примерно каждые {interval}.",
|
||||
syncAutoUploadUnitMinute: "мин.",
|
||||
syncAutoUploadUnitHour: "ч",
|
||||
syncAutoUploadUnitDay: "дн.",
|
||||
syncAutoUploadDescription: "Выполняется, пока DBX открыт. Скачивание по-прежнему требует ручного подтверждения, чтобы не заменить локальный сохранённый SQL неожиданно.",
|
||||
syncAutoUploadSuccess: "Автоматически загружено {bytes} байт в {path}.",
|
||||
syncDownloadSuccess: "Скачано и применено {bytes} байт из {path}.",
|
||||
|
||||
@@ -7588,6 +7588,11 @@ export default withEnglishFallback({
|
||||
syncAutoUpload: "Anlık görüntüleri otomatik yükle",
|
||||
syncAutoUploadInterval: "Her",
|
||||
syncAutoUploadMinutes: "dakika",
|
||||
syncAutoUploadScheduleHint: "Anlık görüntü her {interval} otomatik yüklenecek.",
|
||||
syncAutoUploadScheduleHintApproximate: "Anlık görüntü yaklaşık her {interval} otomatik yüklenecek.",
|
||||
syncAutoUploadUnitMinute: "dk",
|
||||
syncAutoUploadUnitHour: "sa",
|
||||
syncAutoUploadUnitDay: "gün",
|
||||
syncAutoUploadDescription: "DBX açıkken çalışır. Yerel kayıtlı SQL'in beklenmedik biçimde değiştirilmemesi için indirmeler yine elle onay gerektirir.",
|
||||
syncAutoUploadSuccess: "{path} konumuna otomatik olarak {bytes} bayt yüklendi.",
|
||||
syncDownloadSuccess: "{path} konumundan {bytes} bayt indirildi ve uygulandı.",
|
||||
|
||||
@@ -8736,6 +8736,11 @@ export default withEnglishFallback({
|
||||
syncAutoUpload: "自动上传快照",
|
||||
syncAutoUploadInterval: "每",
|
||||
syncAutoUploadMinutes: "分钟",
|
||||
syncAutoUploadScheduleHint: "每 {interval} 自动上传一次快照。",
|
||||
syncAutoUploadScheduleHintApproximate: "约每 {interval} 自动上传一次快照。",
|
||||
syncAutoUploadUnitMinute: "分钟",
|
||||
syncAutoUploadUnitHour: "小时",
|
||||
syncAutoUploadUnitDay: "天",
|
||||
syncAutoUploadDescription: "仅在 DBX 打开时运行。下载仍需手动确认,避免意外覆盖本地保存的 SQL。",
|
||||
syncAutoUploadSuccess: "已自动上传 {bytes} 字节到 {path}。",
|
||||
syncDownloadSuccess: "已从 {path} 下载并应用 {bytes} 字节。",
|
||||
|
||||
@@ -7142,6 +7142,11 @@ export default withEnglishFallback({
|
||||
syncAutoUpload: "自動上傳快照",
|
||||
syncAutoUploadInterval: "每",
|
||||
syncAutoUploadMinutes: "分鐘",
|
||||
syncAutoUploadScheduleHint: "每 {interval} 自動上傳一次快照。",
|
||||
syncAutoUploadScheduleHintApproximate: "約每 {interval} 自動上傳一次快照。",
|
||||
syncAutoUploadUnitMinute: "分鐘",
|
||||
syncAutoUploadUnitHour: "小時",
|
||||
syncAutoUploadUnitDay: "天",
|
||||
syncAutoUploadDescription: "僅在 DBX 開啟時執行。下載仍需手動確認,避免意外覆蓋本機已儲存的 SQL。",
|
||||
syncAutoUploadSuccess: "已自動上傳 {bytes} 位元組到 {path}。",
|
||||
syncDownloadSuccess: "已從 {path} 下載並套用 {bytes} 位元組。",
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { DEFAULT_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES, MAX_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES, formatWebDavAutoUploadInterval, normalizedWebDavAutoUploadInterval } from "./webdavAutoUploadConfig";
|
||||
|
||||
const zhCnUnits = { minute: "分钟", hour: "小时", day: "天" };
|
||||
|
||||
describe("WebDAV auto-upload interval", () => {
|
||||
it("keeps configured intervals in minutes and accepts up to one year", () => {
|
||||
expect(normalizedWebDavAutoUploadInterval(90)).toBe(90);
|
||||
expect(normalizedWebDavAutoUploadInterval(MAX_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES + 1)).toBe(MAX_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES);
|
||||
expect(normalizedWebDavAutoUploadInterval("invalid")).toBe(DEFAULT_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[30, "30 分钟", false],
|
||||
[60, "1 小时", false],
|
||||
[90, "1 小时 30 分钟", false],
|
||||
[95, "1 小时 35 分钟", false],
|
||||
[1439, "23 小时 59 分钟", false],
|
||||
[1440, "1 天", false],
|
||||
[1441, "1 天 1 分钟", false],
|
||||
[1530, "1 天 1 小时 30 分钟", false],
|
||||
])("formats %i minutes as a readable interval", (minutes, interval, approximate) => {
|
||||
expect(formatWebDavAutoUploadInterval(minutes, "zh-CN", zhCnUnits)).toEqual({ interval, approximate });
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@ export const WEB_DAV_AUTO_UPLOAD_STORAGE_KEYS = ["dbx-webdav-endpoint", "dbx-web
|
||||
|
||||
export const DEFAULT_WEB_DAV_REMOTE_PATH = "DBX/sync/snapshot.json";
|
||||
export const DEFAULT_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES = 30;
|
||||
export const MAX_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES = 365 * 24 * 60;
|
||||
|
||||
export type SyncMethod = "webdav" | "snippet" | "local";
|
||||
|
||||
@@ -28,7 +29,48 @@ export interface WebDavAutoUploadConfig {
|
||||
export function normalizedWebDavAutoUploadInterval(value: unknown): number {
|
||||
const numberValue = Number(value);
|
||||
if (!Number.isFinite(numberValue)) return DEFAULT_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES;
|
||||
return Math.max(1, Math.min(1440, Math.round(numberValue)));
|
||||
return Math.max(1, Math.min(MAX_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES, Math.round(numberValue)));
|
||||
}
|
||||
|
||||
export interface WebDavAutoUploadIntervalUnits {
|
||||
minute: string;
|
||||
hour: string;
|
||||
day: string;
|
||||
}
|
||||
|
||||
export interface FormattedWebDavAutoUploadInterval {
|
||||
interval: string;
|
||||
approximate: boolean;
|
||||
}
|
||||
|
||||
export function formatWebDavAutoUploadInterval(value: unknown, locale: string, units: WebDavAutoUploadIntervalUnits): FormattedWebDavAutoUploadInterval {
|
||||
const minutes = normalizedWebDavAutoUploadInterval(value);
|
||||
const formatNumber = (number: number) => new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }).format(number);
|
||||
const formatHourMinutes = (hourMinutes: number) => {
|
||||
const hours = Math.floor(hourMinutes / 60);
|
||||
const remainderMinutes = hourMinutes % 60;
|
||||
if (hours === 0) return { interval: `${formatNumber(remainderMinutes)} ${units.minute}`, approximate: false };
|
||||
if (remainderMinutes === 0) return { interval: `${formatNumber(hours)} ${units.hour}`, approximate: false };
|
||||
return { interval: `${formatNumber(hours)} ${units.hour} ${formatNumber(remainderMinutes)} ${units.minute}`, approximate: false };
|
||||
};
|
||||
|
||||
if (minutes < 60) {
|
||||
return { interval: `${formatNumber(minutes)} ${units.minute}`, approximate: false };
|
||||
}
|
||||
if (minutes < 24 * 60) {
|
||||
return formatHourMinutes(minutes);
|
||||
}
|
||||
|
||||
const days = Math.floor(minutes / (24 * 60));
|
||||
const remainingMinutes = minutes % (24 * 60);
|
||||
if (remainingMinutes === 0) {
|
||||
return { interval: `${formatNumber(days)} ${units.day}`, approximate: false };
|
||||
}
|
||||
const remaining = formatHourMinutes(remainingMinutes);
|
||||
return {
|
||||
interval: `${formatNumber(days)} ${units.day} ${remaining.interval}`,
|
||||
approximate: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function readWebDavAutoUploadConfig(): WebDavAutoUploadConfig {
|
||||
|
||||
@@ -64,6 +64,7 @@ const backendUrl = process.env.DBX_BACKEND_URL || "http://localhost:4224";
|
||||
|
||||
export default defineConfig(async () => ({
|
||||
root: import.meta.dirname,
|
||||
cacheDir: process.env.DBX_DEV_CACHE_DIR,
|
||||
base: viteBase,
|
||||
plugins: [connectionTypesPlugin(), publicBasePathRedirectPlugin(publicBasePath), vue(), tailwindcss()],
|
||||
resolve: {
|
||||
|
||||
+2
@@ -56,6 +56,8 @@ function installWindow() {
|
||||
Object.defineProperty(globalThis, "window", {
|
||||
configurable: true,
|
||||
value: {
|
||||
setTimeout: globalThis.setTimeout.bind(globalThis),
|
||||
clearTimeout: globalThis.clearTimeout.bind(globalThis),
|
||||
setInterval: globalThis.setInterval.bind(globalThis),
|
||||
clearInterval: globalThis.clearInterval.bind(globalThis),
|
||||
addEventListener: (type: string, listener: (event: Event) => void) => {
|
||||
|
||||
Reference in New Issue
Block a user