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
6 changes: 3 additions & 3 deletions src/main/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,12 +572,12 @@ export function setupIPC(
return { success: true }
})

// Export progress data (v1.2 includes review history for streak sync)
// Export progress data (v1.3 includes review history + star/block flags)
ipcMain.handle('export-progress', () => {
return buildExportData(db, app.getVersion())
})

// Import progress data (supports v1.0–v1.2; reviewHistory optional)
// Import progress data (supports v1.0–v1.3; reviewHistory / problemFlags optional)
ipcMain.handle('import-progress', (_event, data: ExportData) => {
return importProgressData(db, data)
})
Expand Down Expand Up @@ -693,7 +693,7 @@ export function setupIPC(
return result.filePaths[0] || null
})

// Perform auto-export to specified folder (v1.2 includes review history)
// Perform auto-export to specified folder (v1.3 includes review history + flags)
ipcMain.handle('perform-auto-export', (_event, folderPath: string) => {
return autoExport(folderPath)
})
Expand Down
45 changes: 43 additions & 2 deletions src/main/lib/sync-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,24 @@ export interface ExportReviewHistoryEntry {
ease_factor_after: number | null
}

/** Star / block flags live on `problems`, not progress — synced separately. */
export interface ExportProblemFlagsEntry {
neet_id: number
starred: number
blocked: number
}

export interface ExportData {
version: string
exportDate: string
appVersion: string
progress: ExportProgressEntry[]
reviewHistory?: ExportReviewHistoryEntry[]
/** Present in v1.3+; starred/blocked problem flags */
problemFlags?: ExportProblemFlagsEntry[]
}

export const EXPORT_VERSION = '1.2'
export const EXPORT_VERSION = '1.3'

function reviewHistoryKey(entry: {
neet_id: number
Expand Down Expand Up @@ -99,13 +108,30 @@ export function fetchReviewHistory(db: Database.Database): ExportReviewHistoryEn
.all() as ExportReviewHistoryEntry[]
}

export function fetchProblemFlags(db: Database.Database): ExportProblemFlagsEntry[] {
return db
.prepare(
`
SELECT
neet_id,
COALESCE(starred, 0) as starred,
COALESCE(blocked, 0) as blocked
FROM problems
WHERE COALESCE(starred, 0) = 1 OR COALESCE(blocked, 0) = 1
ORDER BY neet_id
`
)
.all() as ExportProblemFlagsEntry[]
}

export function buildExportData(db: Database.Database, appVersion: string): ExportData {
return {
version: EXPORT_VERSION,
exportDate: new Date().toISOString(),
appVersion,
progress: fetchProgress(db),
reviewHistory: fetchReviewHistory(db)
reviewHistory: fetchReviewHistory(db),
problemFlags: fetchProblemFlags(db)
}
}

Expand Down Expand Up @@ -237,6 +263,21 @@ export function importProgressData(
}

historyImported = mergeReviewHistory(db, data.reviewHistory)

// v1.3+: replace local star/block flags from the backup when the field is present.
// Older backups omit problemFlags — leave local flags untouched.
if (Array.isArray(data.problemFlags)) {
db.prepare('UPDATE problems SET starred = 0, blocked = 0').run()
const updateFlags = db.prepare(
'UPDATE problems SET starred = ?, blocked = ? WHERE neet_id = ?'
)
for (const entry of data.problemFlags) {
if (typeof entry?.neet_id !== 'number') continue
const starred = entry.starred ? 1 : 0
const blocked = entry.blocked ? 1 : 0
updateFlags.run(starred, blocked, entry.neet_id)
}
}
})

transaction()
Expand Down
8 changes: 8 additions & 0 deletions src/preload/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,21 @@ export interface ExportReviewHistoryEntry {
ease_factor_after: number | null
}

export interface ExportProblemFlagsEntry {
neet_id: number
starred: number
blocked: number
}

export interface ExportData {
version: string
exportDate: string
appVersion: string
progress: ExportProgressEntry[]
// Present in v1.2+; used for activity heatmap / streak sync
reviewHistory?: ExportReviewHistoryEntry[]
// Present in v1.3+; starred / blocked flags
problemFlags?: ExportProblemFlagsEntry[]
}

export interface API {
Expand Down
26 changes: 16 additions & 10 deletions src/renderer/src/stores/problems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,24 +252,28 @@ export async function selectProblem(problem: Problem): Promise<void> {
selectedProblem.set(problem)
}


async function maybeAutoExport(reason: string): Promise<void> {
try {
const syncPrefs = await window.api.getAutoSyncPreferences()
if (syncPrefs.enabled && syncPrefs.folderPath) {
await window.api.performAutoExport(syncPrefs.folderPath)
}
} catch (syncError) {
// Don't fail the user action if sync fails
console.error(`Auto-sync after ${reason} failed:`, syncError)
}
}

export async function submitReview(
problemId: number,
quality: number
): Promise<{ success: boolean; nextReviewDate: string; newInterval: number }> {
try {
const result = await window.api.submitReview({ problemId, quality })

// Trigger auto-export if enabled
if (result.success) {
try {
const syncPrefs = await window.api.getAutoSyncPreferences()
if (syncPrefs.enabled && syncPrefs.folderPath) {
await window.api.performAutoExport(syncPrefs.folderPath)
}
} catch (syncError) {
// Don't fail the review if sync fails
console.error('Auto-sync after review failed:', syncError)
}
await maybeAutoExport('review')
}

return result
Expand All @@ -293,6 +297,7 @@ export async function setProblemBlocked(problemId: number, blocked: boolean): Pr
try {
await window.api.setProblemBlocked(problemId, blocked)
await Promise.all([loadProblems(), loadTodayReviews()])
await maybeAutoExport('block')
} catch (error) {
console.error('Failed to set problem blocked:', error)
}
Expand All @@ -302,6 +307,7 @@ export async function setProblemStarred(problemId: number, starred: boolean): Pr
try {
await window.api.setProblemStarred(problemId, starred)
await loadProblems()
await maybeAutoExport('star')
} catch (error) {
console.error('Failed to set problem starred:', error)
}
Expand Down
Loading