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
3 changes: 3 additions & 0 deletions src/renderer/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
useSonner,
useTheme,
useVaultDoctor,
VAULT_DOCTOR_NOTICE_ID,
} from '@/composables'
import { i18n, ipc, store } from '@/electron'
import { router, RouterName } from '@/router'
Expand Down Expand Up @@ -114,10 +115,12 @@ function checkVaultHealth() {
}

sonner({
id: VAULT_DOCTOR_NOTICE_ID,
message: i18n.t('messages:warning.vaultDoctorConflicts', {
count: data.summary.conflicts,
}),
type: 'warning',
closeButton: true,
action: {
label: i18n.t('messages:warning.vaultDoctorReview'),
onClick: () => {
Expand Down
50 changes: 43 additions & 7 deletions src/renderer/components/preferences/Storage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
useSnippets,
useSonner,
useVaultDoctor,
VAULT_DOCTOR_NOTICE_ID,
} from '@/composables'
import { i18n, ipc, store } from '@/electron'
import { AlertTriangle, Check, LoaderCircle } from 'lucide-vue-next'
Expand All @@ -36,7 +37,7 @@ interface MoveVaultResponse {
vaultPath: string
}

const { sonner } = useSonner()
const { sonner, dismiss } = useSonner()
const { confirm } = useDialog()
const { getFolders } = useFolders()
const { getHttpFolders } = useHttpFolders()
Expand Down Expand Up @@ -111,6 +112,7 @@ const {
isDecisionSelected: isVaultDoctorDecisionSelected,
scan: scanVault,
apply: applyVault,
reset: resetVaultDoctor,
} = useVaultDoctor()

function showLoadingCounts() {
Expand Down Expand Up @@ -226,6 +228,8 @@ async function openVaultStorage() {
message: i18n.t('messages:success.vaultLoaded'),
type: 'success',
})

await refreshVaultDoctorAfterVaultChange()
}

async function moveVaultStorage() {
Expand Down Expand Up @@ -273,6 +277,10 @@ async function moveVaultStorage() {
vaultPath.value = result.vaultPath
await resetAndReloadVaultData()

// Контент тот же, но путь сменился — прежний отчёт по старым путям неактуален.
dismiss(VAULT_DOCTOR_NOTICE_ID)
resetVaultDoctor()

sonner({
message: i18n.t('messages:success.vaultMoved'),
type: 'success',
Expand Down Expand Up @@ -409,6 +417,39 @@ async function applyVaultDoctorSafeFixes() {
}
}

function scrollToVaultDoctorSection() {
nextTick(() => {
vaultDoctorSection.value?.$el?.scrollIntoView({
behavior: 'smooth',
block: 'start',
})
})
}

// Смена vault делает прежний отчёт неактуальным (он про другой путь). Сбрасываем
// и пере-сканируем новый vault. Сканируем тихо (без sonner): мы уже в Storage,
// секция сама покажет результат, а при конфликтах подсвечиваем её скроллом.
async function refreshVaultDoctorAfterVaultChange() {
// Убираем уведомление о конфликтах прежнего vault — оно больше не актуально.
dismiss(VAULT_DOCTOR_NOTICE_ID)
resetVaultDoctor()
showVaultDoctorScanLoader()

try {
await scanVault()

if (vaultDoctorConflictCount.value > 0) {
scrollToVaultDoctorSection()
}
}
catch {
// Vault уже загружен; провал health-чека не критичен и не требует sonner.
}
finally {
hideVaultDoctorScanLoader()
}
}

// Переход из startup-уведомления о конфликтах: переиспользуем отчёт
// startup-проверки (если он есть), иначе сканируем, и скроллим к секции,
// чтобы пользователь не искал её вручную. Query чистим, чтобы повторный
Expand All @@ -424,12 +465,7 @@ onMounted(() => {
scanVaultDoctor()
}

nextTick(() => {
vaultDoctorSection.value?.$el?.scrollIntoView({
behavior: 'smooth',
block: 'start',
})
})
scrollToVaultDoctorSection()
})
</script>

Expand Down
1 change: 1 addition & 0 deletions src/renderer/components/ui/sonner/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface Props {
id?: string | number
message?: string
component?: Component
type?: 'default' | 'success' | 'error' | 'warning'
Expand Down
8 changes: 7 additions & 1 deletion src/renderer/composables/useSonner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ import { toast } from 'vue-sonner'

export function useSonner() {
const sonner = (config: Props) => {
toast.custom(markRaw(Sonner), {
return toast.custom(markRaw(Sonner), {
id: config.id,
componentProps: config,
duration: config.action ? Infinity : config.duration || 5000,
onDismiss: config.onClose,
})
}

const dismiss = (id?: string | number) => {
toast.dismiss(id)
}

return {
sonner,
dismiss,
}
}
12 changes: 12 additions & 0 deletions src/renderer/composables/useVaultDoctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { api } from '~/renderer/services/api'

type ConflictGroup = VaultDoctorResponse['conflictGroups'][number]

// Стабильный id sonner-уведомления о конфликтах: позволяет убрать висящее
// уведомление о прежнем vault при смене на другой.
export const VAULT_DOCTOR_NOTICE_ID = 'vault-doctor-conflicts'

// Эвристика конфликтных копий cloud-sync: Dropbox "(conflicted copy)",
// Яндекс.Диск / macOS "— копия", generic "copy". Используется только для
// визуальной подсказки и предвыбора canonical, не влияет на backend.
Expand Down Expand Up @@ -133,6 +137,13 @@ export function useVaultDoctor() {
}
}

// Сбрасывает отчёт и решения. Нужно при смене vault: старый отчёт относится
// к прежнему пути и к новому vault неприменим.
function reset() {
report.value = null
resetDecisions()
}

async function apply(): Promise<VaultDoctorResponse> {
isApplying.value = true

Expand Down Expand Up @@ -174,5 +185,6 @@ export function useVaultDoctor() {
isDecisionSelected,
scan,
apply,
reset,
}
}