aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/shared/card-generator.ts
blob: f824e3cda0f7ce894ba027a797ce290252f2ef4e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
 * Shared card generation logic used by both server (note repository) and client
 * (offline-first IndexedDB writes). Pure functions: produces card payloads from
 * a note type and field values without touching any storage layer.
 */

const NEW_CARD_STATE = 0;

export interface NoteTypeForGeneration {
	frontTemplate: string;
	backTemplate: string;
	isReversible: boolean;
}

export interface FieldTypeForGeneration {
	id: string;
	name: string;
}

export interface FieldValueForGeneration {
	noteFieldTypeId: string;
	value: string;
}

export interface GeneratedCard {
	isReversed: boolean;
	front: string;
	back: string;
	state: number;
	due: Date;
	stability: number;
	difficulty: number;
	elapsedDays: number;
	scheduledDays: number;
	reps: number;
	lapses: number;
}

export function renderCardTemplate(
	template: string,
	fields: Map<string, string>,
): string {
	let result = template;
	for (const [name, value] of fields) {
		result = result.replace(new RegExp(`\\{\\{${name}\\}\\}`, "g"), value);
	}
	return result;
}

export function generateCardsForNote(input: {
	noteType: NoteTypeForGeneration;
	fieldTypes: FieldTypeForGeneration[];
	fieldValues: FieldValueForGeneration[];
	now?: Date;
}): GeneratedCard[] {
	const { noteType, fieldTypes, fieldValues, now = new Date() } = input;

	const fieldMap = new Map<string, string>();
	for (const fv of fieldValues) {
		const ft = fieldTypes.find((f) => f.id === fv.noteFieldTypeId);
		if (ft) {
			fieldMap.set(ft.name, fv.value);
		}
	}

	const generated: GeneratedCard[] = [
		buildCard(noteType, fieldMap, false, now),
	];
	if (noteType.isReversible) {
		generated.push(buildCard(noteType, fieldMap, true, now));
	}
	return generated;
}

function buildCard(
	noteType: NoteTypeForGeneration,
	fields: Map<string, string>,
	isReversed: boolean,
	now: Date,
): GeneratedCard {
	const frontTemplate = isReversed
		? noteType.backTemplate
		: noteType.frontTemplate;
	const backTemplate = isReversed
		? noteType.frontTemplate
		: noteType.backTemplate;

	return {
		isReversed,
		front: renderCardTemplate(frontTemplate, fields),
		back: renderCardTemplate(backTemplate, fields),
		state: NEW_CARD_STATE,
		due: new Date(now),
		stability: 0,
		difficulty: 0,
		elapsedDays: 0,
		scheduledDays: 0,
		reps: 0,
		lapses: 0,
	};
}