3 Commits

Author SHA1 Message Date
979f8fc5e1 update review timing
All checks were successful
Release Build / build-docker (push) Successful in 1m4s
Release Build / build-android-and-release (push) Successful in 4m16s
2026-02-28 00:03:50 +01:00
Rene Kievits
f703cc5727 add more infos to overview, fix redo lessons button, add readings to review for clarification
All checks were successful
Release Build / build-docker (push) Successful in 1m27s
Release Build / build-android-and-release (push) Successful in 2m59s
2026-01-11 21:36:45 +01:00
Rene Kievits
2129cee5c4 update android style to test auto update
All checks were successful
Release Build / build-docker (push) Successful in 25s
Release Build / build-android-and-release (push) Successful in 2m36s
2025-12-26 23:05:47 +01:00
12 changed files with 187 additions and 120 deletions

View File

@@ -1,28 +1,7 @@
<template lang="pug"> <template lang="pug">
v-app.zen-app.overflow-hidden v-app.zen-app.overflow-hidden
.parallax-bg(
:style="parallaxStyle"
:class="{ 'zooming': viewState === 'zooming' }"
)
.bg-gradient
.bg-grid
.kanji-layer
span.bg-kanji(
v-for="(k, i) in backgroundKanjis"
:key="i"
:style="k.style"
) {{ k.char }}
.orb.orb-1(:class="{ 'zooming': viewState === 'zooming' }")
.orb.orb-2(:class="{ 'zooming': viewState === 'zooming' }")
.orb.orb-3(:class="{ 'zooming': viewState === 'zooming' }")
.app-content-wrapper(
v-if="store.token || viewState === 'zooming'"
:class="{ 'app-entering': viewState === 'zooming', 'app-visible': viewState === 'app' }"
)
v-navigation-drawer( v-navigation-drawer(
v-if="store.token || viewState === 'zooming'"
v-model="drawer" v-model="drawer"
temporary temporary
location="right" location="right"
@@ -83,6 +62,28 @@
) )
v-icon(start icon="mdi-logout") v-icon(start icon="mdi-logout")
| {{ $t('nav.logout') }} | {{ $t('nav.logout') }}
.parallax-bg(
:style="parallaxStyle"
:class="{ 'zooming': viewState === 'zooming' }"
)
.bg-gradient
.bg-grid
.kanji-layer
span.bg-kanji(
v-for="(k, i) in backgroundKanjis"
:key="i"
:style="k.style"
) {{ k.char }}
.orb.orb-1(:class="{ 'zooming': viewState === 'zooming' }")
.orb.orb-2(:class="{ 'zooming': viewState === 'zooming' }")
.orb.orb-3(:class="{ 'zooming': viewState === 'zooming' }")
.app-content-wrapper(
v-if="store.token || viewState === 'zooming'"
:class="{ 'app-entering': viewState === 'zooming', 'app-visible': viewState === 'app' }"
)
v-app-bar.px-2.app-bar-blur.safe-area-header( v-app-bar.px-2.app-bar-blur.safe-area-header(
flat flat

View File

@@ -1,3 +1,5 @@
@use 'readings';
@use 'radicals';
@use 'buttons'; @use 'buttons';
@use 'cards'; @use 'cards';
@use 'kanji/index' as kanji; @use 'kanji/index' as kanji;

View File

@@ -0,0 +1,20 @@
@use '../abstracts' as *;
.radical-section {
width: 100%;
background: $color-zinc-900;
border-radius: $radius-md;
padding: $spacing-md;
margin-bottom: $spacing-lg;
}
.radical-chip {
border-radius: $radius-md;
border: $border-width-sm solid $color-border;
background-color: $color-surface-lighter;
img {
filter: invert(1);
width: 100%;
}
}

View File

@@ -0,0 +1,28 @@
@use '../abstracts' as *;
.readings-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: $spacing-lg;
width: 100%;
}
.reading-box {
background: $color-reading-box;
padding: $spacing-lg;
border-radius: $radius-lg;
text-align: center;
.label {
font-size: $font-xs;
color: #666;
letter-spacing: 0.0625rem;
text-transform: uppercase;
margin-bottom: $spacing-xs;
}
.val {
font-size: 1.1rem;
font-weight: $weight-bold;
}
}

View File

@@ -2,7 +2,7 @@
.v-navigation-drawer { .v-navigation-drawer {
background-color: $color-surface !important; background-color: $color-surface !important;
border-right: $border-width-sm solid $color-border; padding-top: env(safe-area-inset-top) !important;
.v-list-item--active { .v-list-item--active {
background-color: $bg-glass-strong; background-color: $bg-glass-strong;

View File

@@ -16,52 +16,6 @@
height: $size-hero-wrapper; height: $size-hero-wrapper;
} }
.radical-section {
width: 100%;
background: $color-zinc-900;
border-radius: $radius-md;
padding: $spacing-md;
margin-bottom: $spacing-lg;
}
.radical-chip {
border-radius: $radius-md;
border: $border-width-sm solid $color-border;
background-color: $color-surface-lighter;
img {
filter: invert(1);
width: 100%;
}
}
.readings-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: $spacing-lg;
width: 100%;
}
.reading-box {
background: $color-reading-box;
padding: $spacing-lg;
border-radius: $radius-lg;
text-align: center;
.label {
font-size: $font-xs;
color: #666;
letter-spacing: 0.0625rem;
text-transform: uppercase;
margin-bottom: $spacing-xs;
}
.val {
font-size: 1.1rem;
font-weight: $weight-bold;
}
}
.lesson-canvas-wrapper { .lesson-canvas-wrapper {
margin: 0 auto; margin: 0 auto;
} }

View File

@@ -45,3 +45,10 @@
.gap-2 { .gap-2 {
gap: $spacing-sm; gap: $spacing-sm;
} }
.reading-box--small {
padding: $spacing-sm;
.val {
font-size: 1rem;
}
}

View File

@@ -92,6 +92,18 @@
:svg-content="preloadedSvg" :svg-content="preloadedSvg"
) )
.radical-section.mb-4(v-if="selectedItem?.radicals?.length")
.text-caption.text-grey-darken-1.text-uppercase.mb-3.font-weight-bold
|{{ $t('lesson.components') }}
.d-flex.flex-wrap.gap-3.justify-center
v-sheet.radical-chip(
v-for="rad in selectedItem.radicals" :key="rad.meaning")
.d-flex.align-center.gap-2.px-3.py-1
v-avatar(size="24" rounded="0")
img(v-if="rad.image" :src="rad.image")
span.text-h6.font-weight-bold.text-white(v-else) {{ rad.char }}
.text-caption.text-grey-lighten-1 {{ rad.meaning }}
.readings-container.mb-4 .readings-container.mb-4
.reading-group(v-if="hasReading(selectedItem?.onyomi)") .reading-group(v-if="hasReading(selectedItem?.onyomi)")
.reading-label {{ $t('collection.onyomi') }} .reading-label {{ $t('collection.onyomi') }}
@@ -105,6 +117,17 @@
.reading-label {{ $t('collection.nanori') }} .reading-label {{ $t('collection.nanori') }}
.reading-value {{ selectedItem?.nanori.join(', ') }} .reading-value {{ selectedItem?.nanori.join(', ') }}
.px-2.mb-4(v-if="selectedItem?.stats?.total > 0")
.d-flex.justify-space-between.align-center.mb-1
.text-caption.text-grey {{ $t('stats.accuracy') }}
.text-caption.font-weight-bold {{ accuracyStats.text }}
v-progress-linear(
:model-value="accuracyStats.percent"
color="light-blue"
height="6"
rounded
)
v-row.px-2.pb-2 v-row.px-2.pb-2
v-col.pr-2(cols="6") v-col.pr-2(cols="6")
v-btn.text-white( v-btn.text-white(
@@ -142,6 +165,16 @@ const searchQuery = ref('');
const preparingId = ref(null); const preparingId = ref(null);
const preloadedSvg = ref(''); const preloadedSvg = ref('');
const accuracyStats = computed(() => {
if (!selectedItem.value?.stats || selectedItem.value.stats.total === 0) {
return { percent: 100, text: 'No history' };
}
const { correct, total } = selectedItem.value.stats;
const percent = Math.round((correct / total) * 100);
const text = `${correct} / ${total}`;
return { percent, text };
});
onMounted(async () => { onMounted(async () => {
await store.fetchCollection(); await store.fetchCollection();
loading.value = false; loading.value = false;

View File

@@ -38,13 +38,13 @@
img(v-if="rad.image" :src="rad.image") img(v-if="rad.image" :src="rad.image")
span.text-h6.font-weight-bold.text-white(v-else) {{ rad.char }} span.text-h6.font-weight-bold.text-white(v-else) {{ rad.char }}
.text-caption.text-grey-lighten-1 {{ rad.meaning }} .text-caption.text-grey-lighten-1 {{ rad.meaning }}
.readings-grid .readings-grid(v-if="(currentItem.onyomi && currentItem.onyomi.length > 0) || (currentItem.kunyomi && currentItem.kunyomi.length > 0)")
.reading-box .reading-box(v-if="currentItem.onyomi && currentItem.onyomi.length > 0")
.label {{ $t('collection.onyomi') }} .label {{ $t('collection.onyomi') }}
.val.text-cyan-lighten-3 {{ currentItem.onyomi[0] || '-' }} .val.text-cyan-lighten-3 {{ currentItem.onyomi.join(', ') }}
.reading-box .reading-box(v-if="currentItem.kunyomi && currentItem.kunyomi.length > 0")
.label {{ $t('collection.kunyomi') }} .label {{ $t('collection.kunyomi') }}
.val.text-pink-lighten-3 {{ currentItem.kunyomi[0] || '-' }} .val.text-pink-lighten-3 {{ currentItem.kunyomi.join(', ') }}
v-window-item(value="demo") v-window-item(value="demo")
.d-flex.flex-column.align-center.justify-center.h-100.pa-6 .d-flex.flex-column.align-center.justify-center.h-100.pa-6
@@ -111,12 +111,14 @@ import {
ref, computed, onMounted, nextTick, watch, ref, computed, onMounted, nextTick, watch,
} from 'vue'; } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { useAppStore } from '@/stores/appStore'; import { useAppStore } from '@/stores/appStore';
import KanjiCanvas from '@/components/kanji/KanjiCanvas.vue'; import KanjiCanvas from '@/components/kanji/KanjiCanvas.vue';
import KanjiSvgViewer from '@/components/kanji/KanjiSvgViewer.vue'; import KanjiSvgViewer from '@/components/kanji/KanjiSvgViewer.vue';
const { t } = useI18n(); const { t } = useI18n();
const store = useAppStore(); const store = useAppStore();
const route = useRoute();
const currentItem = ref(null); const currentItem = ref(null);
const phase = ref('primer'); const phase = ref('primer');
const subPhase = ref('guided'); const subPhase = ref('guided');
@@ -179,7 +181,12 @@ async function handleDrawComplete() {
canvas.value?.reset(); canvas.value?.reset();
canvasOpacity.value = 1; canvasOpacity.value = 1;
} else { } else {
const isRedo = !!route.query.subjectId;
if (!isRedo) {
await store.submitLesson(currentItem.value.wkSubjectId); await store.submitLesson(currentItem.value.wkSubjectId);
}
sessionDone.value += 1; sessionDone.value += 1;
store.lessonQueue.shift(); store.lessonQueue.shift();
loadNext(); loadNext();
@@ -200,7 +207,15 @@ function useHint() {
onMounted(async () => { onMounted(async () => {
loading.value = true; loading.value = true;
const { subjectId } = route.query;
const historyState = window.history.state;
if (subjectId && historyState && historyState.item) {
store.lessonQueue = [historyState.item];
} else {
await store.fetchLessonQueue(); await store.fetchLessonQueue();
}
sessionTotal.value = store.lessonQueue.length; sessionTotal.value = store.lessonQueue.length;
loadNext(); loadNext();
loading.value = false; loading.value = false;

View File

@@ -28,8 +28,15 @@
.text-center.mb-6 .text-center.mb-6
.text-caption.text-grey.text-uppercase.tracking-wide.mb-1 .text-caption.text-grey.text-uppercase.tracking-wide.mb-1
| {{ $t('review.meaning') }} | {{ $t('review.meaning') }}
.text-h3.font-weight-bold.text-white.text-shadow .text-h3.font-weight-bold.text-white.text-shadow.mb-4
| {{ currentItem.meaning }} | {{ currentItem.meaning }}
.readings-grid(v-if="(currentItem.onyomi && currentItem.onyomi.length > 0) || (currentItem.kunyomi && currentItem.kunyomi.length > 0)")
.reading-box.reading-box--small(v-if="currentItem.onyomi && currentItem.onyomi.length > 0")
.label {{ $t('collection.onyomi') }}
.val.text-cyan-lighten-3 {{ currentItem.onyomi.join(', ') }}
.reading-box.reading-box--small(v-if="currentItem.kunyomi && currentItem.kunyomi.length > 0")
.label {{ $t('collection.kunyomi') }}
.val.text-pink-lighten-3 {{ currentItem.kunyomi.join(', ') }}
.review-canvas-area .review-canvas-area
KanjiCanvas( KanjiCanvas(

View File

@@ -57,7 +57,7 @@ export const processReview = async (user, subjectId, success) => {
item.nextReview = getSRSDate(nextLevel); item.nextReview = getSRSDate(nextLevel);
} else { } else {
item.srsLevel = Math.max(1, item.srsLevel - 1); item.srsLevel = Math.max(1, item.srsLevel - 1);
item.nextReview = Date.now(); item.nextReview = getSRSDate(item.srsLevel);
} }
await item.save(); await item.save();

View File

@@ -17,9 +17,9 @@ export const getSRSDate = (level) => {
case 5: hoursToAdd = 7 * 24; break; case 5: hoursToAdd = 7 * 24; break;
case 6: hoursToAdd = 14 * 24; break; case 6: hoursToAdd = 14 * 24; break;
case 7: hoursToAdd = 7 * 24; break; case 7: hoursToAdd = 30 * 24; break;
case 8: hoursToAdd = 30 * 24; break; case 8: hoursToAdd = 90 * 24; break;
case 9: hoursToAdd = 90 * 24; break; case 9: hoursToAdd = 180 * 24; break;
case 10: break; case 10: break;
default: hoursToAdd = 4; default: hoursToAdd = 4;