integrate need review cards

This commit is contained in:
Julien Calixte
2023-08-06 21:27:01 +02:00
parent af421042ad
commit 32a76ca3b7
11 changed files with 120 additions and 18 deletions

View File

@@ -32,7 +32,7 @@ const reload = () => {
</span> </span>
</div> </div>
<div class="buttons"> <div class="buttons">
<button class="button is-primary" v-if="needRefresh" @click="reload"> <button v-if="needRefresh" class="button is-primary" @click="reload">
<LiteLoading v-if="isLoading" /> <LiteLoading v-if="isLoading" />
<span v-else>Reload</span> <span v-else>Reload</span>
</button> </button>

View File

@@ -3,7 +3,7 @@ import { ref } from 'vue'
import { Card } from '../models/Card' import { Card } from '../models/Card'
defineProps<{ card: Card }>() defineProps<{ card: Card }>()
const emit = defineEmits<{ success: []; fail: [] }>() const emit = defineEmits<{ success: []; fail: []; needsReview: [] }>()
const flipped = ref(false) const flipped = ref(false)
const flip = () => { const flip = () => {
@@ -11,8 +11,8 @@ const flip = () => {
} }
const success = () => emit('success') const success = () => emit('success')
const fail = () => emit('fail') const fail = () => emit('fail')
const needsReview = () => emit('needsReview')
</script> </script>
<template> <template>
@@ -27,8 +27,13 @@ const fail = () => emit('fail')
<div class="actions"> <div class="actions">
<p>Did you remember this?</p> <p>Did you remember this?</p>
<div class="buttons is-centered"> <div class="buttons is-centered">
<div class="button is-warning" @click.stop="fail">failed</div> <button class="button is-warning" @click.stop="fail">failed</button>
<div class="button is-success" @click.stop="success">got it</div> <button class="button is-success" @click.stop="success">
got it
</button>
<button class="button is-danger" @click.stop="needsReview">
<em>needs review</em>
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -7,6 +7,7 @@ const props = defineProps<{ cards: Repetition[] }>()
const emits = defineEmits<{ const emits = defineEmits<{
success: [id: string] success: [id: string]
fail: [id: string] fail: [id: string]
needsReview: [id: string]
}>() }>()
const cards = ref( const cards = ref(
@@ -32,6 +33,12 @@ const goToNextCard = (success: boolean) => {
currentIndex.value++ currentIndex.value++
} }
const needsReview = () => {
const id = cards.value[currentIndex.value].repetition._id ?? ''
emits('needsReview', id)
currentIndex.value++
}
</script> </script>
<template> <template>
@@ -52,6 +59,7 @@ const goToNextCard = (success: boolean) => {
:card="card.card" :card="card.card"
@success="goToNextCard(true)" @success="goToNextCard(true)"
@fail="goToNextCard(false)" @fail="goToNextCard(false)"
@needs-review="needsReview"
/> />
</div> </div>
<div v-else>No more cards to check!</div> <div v-else>No more cards to check!</div>

View File

@@ -0,0 +1,22 @@
import { data } from '@/data/data'
import { DataType } from '@/data/DataType.enum'
import { RepetitionCard } from '@/modules/card/models/RepetitionCard'
import { useAsyncState } from '@vueuse/core'
export const useNeedReviewCards = () => {
const { state: cardsToReview, isReady } = useAsyncState(async () => {
const repetitions = await data.getAll<
DataType.RepetitionCard,
RepetitionCard
>({
prefix: DataType.RepetitionCard
})
return repetitions.filter((repetition) => repetition.needsReview)
}, [])
return {
cardsToReview,
isReady
}
}

View File

@@ -1,7 +1,7 @@
// https://npm.io/package/supermemo // https://npm.io/package/supermemo
import { DataType } from '@/data/DataType.enum'
import { data } from '@/data/data' import { data } from '@/data/data'
import { DataType } from '@/data/DataType.enum'
import { useFile } from '@/hooks/useFile.hook' import { useFile } from '@/hooks/useFile.hook'
import { useLinks } from '@/hooks/useLinks.hook' import { useLinks } from '@/hooks/useLinks.hook'
import { useMarkdown } from '@/hooks/useMarkdown.hook' import { useMarkdown } from '@/hooks/useMarkdown.hook'
@@ -53,12 +53,14 @@ export const useSpacedRepetitionCards = () => {
>(data.generateId(DataType.RepetitionCard, cardFile.path), { >(data.generateId(DataType.RepetitionCard, cardFile.path), {
$type: DataType.RepetitionCard, $type: DataType.RepetitionCard,
level: 1, level: 1,
repeatDate: new Date() repeatDate: new Date(),
needsReview: false
}) })
if ( if (
isAfter(new Date(repetition.repeatDate), new Date()) || isAfter(new Date(repetition.repeatDate), new Date()) ||
repetition.level === MAX_LEVEL repetition.level === MAX_LEVEL ||
repetition.needsReview
) { ) {
continue continue
} }
@@ -95,6 +97,7 @@ export const useSpacedRepetitionCards = () => {
await data.update<DataType.RepetitionCard, RepetitionCard>({ await data.update<DataType.RepetitionCard, RepetitionCard>({
...repetition, ...repetition,
needsReview: false,
level: Math.min(repetition.level + 1, MAX_LEVEL), level: Math.min(repetition.level + 1, MAX_LEVEL),
repeatDate: addDays(new Date(), 2 ** repetition.level) repeatDate: addDays(new Date(), 2 ** repetition.level)
}) })
@@ -113,10 +116,25 @@ export const useSpacedRepetitionCards = () => {
await data.update<DataType.RepetitionCard, RepetitionCard>({ await data.update<DataType.RepetitionCard, RepetitionCard>({
...repetition, ...repetition,
level, level,
needsReview: false,
repeatDate: addDays(new Date(), level) repeatDate: addDays(new Date(), level)
}) })
} }
const needsReview = async (cardId: string) => {
const repetition = await data.get<DataType.RepetitionCard, RepetitionCard>(
cardId
)
if (!repetition) {
return
}
await data.update<DataType.RepetitionCard, RepetitionCard>({
...repetition,
needsReview: true
})
}
watch( watch(
cards, cards,
() => () =>
@@ -128,5 +146,11 @@ export const useSpacedRepetitionCards = () => {
watch(cardFiles, () => execute()) watch(cardFiles, () => execute())
return { cards, successRepetition, failRepetition, isLoading: !isReady } return {
cards,
successRepetition,
failRepetition,
needsReview,
isLoading: !isReady
}
} }

View File

@@ -4,4 +4,5 @@ import { Model } from '@/data/models/Model'
export interface RepetitionCard extends Model<DataType.RepetitionCard> { export interface RepetitionCard extends Model<DataType.RepetitionCard> {
level: number level: number
repeatDate: Date repeatDate: Date
needsReview: boolean
} }

View File

@@ -1,5 +1,5 @@
import { resolvePath } from '@/modules/repo/services/resolvePath'
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { resolvePath } from './resolvePath'
describe('resolve path service', () => { describe('resolve path service', () => {
it('set the absolute path if path to resolve is empty', () => { it('set the absolute path if path to resolve is empty', () => {

View File

@@ -43,6 +43,12 @@ const routes: Array<RouteRecordRaw> = [
props: true, props: true,
component: () => import('@/views/SpacedRepetitionCard.vue') component: () => import('@/views/SpacedRepetitionCard.vue')
}, },
{
path: '/:user/:repo/need-review-cards',
name: 'NeedReviewCards',
props: true,
component: () => import('@/views/NeedReviewCards.vue')
},
{ {
path: '/about', path: '/about',
name: 'About', name: 'About',

View File

@@ -0,0 +1,38 @@
<script setup lang="ts">
import FluxNote from '@/components/FluxNote.vue'
import { DataType } from '@/data/DataType.enum'
import { useNeedReviewCards } from '@/modules/card/hooks/useNeedReviewCards'
import { computed } from 'vue'
defineProps<{
user: string
repo: string
}>()
const { cardsToReview } = useNeedReviewCards()
const cardNames = computed(() =>
cardsToReview.value.map((card) => ({
id: card._id,
name: card._id?.split(DataType.RepetitionCard).pop()
}))
)
</script>
<template>
<div class="needs-review-cards">
<flux-note
key="needs-review-cards"
class="card-container"
:user="user"
:repo="repo"
:with-content="false"
>
needs review cards
<ul>
<li v-for="card in cardNames" :key="card.id">
{{ card.name }}
</li>
</ul>
</flux-note>
</div>
</template>

View File

@@ -8,7 +8,7 @@ defineProps<{
repo: string repo: string
}>() }>()
const { cards, isLoading, successRepetition, failRepetition } = const { cards, isLoading, successRepetition, failRepetition, needsReview } =
useSpacedRepetitionCards() useSpacedRepetitionCards()
</script> </script>
@@ -27,9 +27,13 @@ const { cards, isLoading, successRepetition, failRepetition } =
:cards="cards" :cards="cards"
@success="successRepetition" @success="successRepetition"
@fail="failRepetition" @fail="failRepetition"
@needs-review="needsReview"
/> />
</section> </section>
<section v-else>No cards to review!</section> <section v-else>No cards to review!</section>
<router-link :to="{ name: 'NeedReviewCards', params: { user, repo } }">
needs review cards
</router-link>
</flux-note> </flux-note>
</div> </div>
</template> </template>

View File

@@ -18,12 +18,6 @@
}, },
"lib": ["esnext", "dom", "dom.iterable", "scripthost", "ES2015"] "lib": ["esnext", "dom", "dom.iterable", "scripthost", "ES2015"]
}, },
"include": [ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }