(card) make the buttons work

This commit is contained in:
Julien Calixte
2023-07-02 23:25:14 +02:00
parent c827aad4b9
commit 6c251f797d
5 changed files with 148 additions and 109 deletions

View File

@@ -1,8 +1,8 @@
import { nanoid } from 'nanoid'
import indexedDb from 'pouchdb-adapter-indexeddb'
import PouchDb from 'pouchdb-browser'
import { DataType } from './DataType.enum' import { DataType } from './DataType.enum'
import { Model } from './models/Model' import { Model } from './models/Model'
import PouchDb from 'pouchdb-browser'
import indexedDb from 'pouchdb-adapter-indexeddb'
import { nanoid } from 'nanoid'
PouchDb.plugin(indexedDb) PouchDb.plugin(indexedDb)
@@ -38,7 +38,9 @@ class Data {
} }
} }
public async update<DT extends DataType>(model: Model<DT>): Promise<boolean> { public async update<DT extends DataType, T extends Model<DT>>(
model: T
): Promise<boolean> {
try { try {
if (model._id) { if (model._id) {
const oldModel = await this.get(model._id) const oldModel = await this.get(model._id)
@@ -82,6 +84,21 @@ class Data {
} }
} }
public async getOrCreate<DT extends DataType, T extends Model<DT>>(
id: string,
initialValue: T
): Promise<T> {
const element = await this.get<DT, T>(id)
if (element) {
return element
}
await data.add<DT>({ ...initialValue, _id: id })
return this.getOrCreate(id, initialValue)
}
public async getAll<DT extends DataType, T extends Model<DT>>({ public async getAll<DT extends DataType, T extends Model<DT>>({
prefix, prefix,
includeDocs = true, includeDocs = true,

View File

@@ -1,11 +1,11 @@
import MarkdownIt from 'markdown-it' import { decodeBase64ToUTF8 } from '@/utils/decodeBase64ToUTF8'
import { html5Media } from '@/utils/markdown/markdown-html5-media'
import markdownItClass from '@toycode/markdown-it-class' import markdownItClass from '@toycode/markdown-it-class'
import MarkdownIt from 'markdown-it'
import blockEmbedPlugin from 'markdown-it-block-embed' import blockEmbedPlugin from 'markdown-it-block-embed'
import markdownItCheckbox from 'markdown-it-checkbox' import markdownItCheckbox from 'markdown-it-checkbox'
import markdownItSvgCodeCopy from 'markdown-it-svg-code-copy'
import markdownItFootnote from 'markdown-it-footnote' import markdownItFootnote from 'markdown-it-footnote'
import { html5Media } from '@/utils/markdown/markdown-html5-media' import markdownItSvgCodeCopy from 'markdown-it-svg-code-copy'
import { decodeBase64ToUTF8 } from '@/utils/decodeBase64ToUTF8'
const md = new MarkdownIt({ const md = new MarkdownIt({
typographer: true, typographer: true,

View File

@@ -1,3 +1,18 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { Card } from '../models/Card'
defineProps<{ card: Card }>()
const emit = defineEmits(['success', 'fail'])
const flipped = ref(false)
const flip = () => (flipped.value = !flipped.value)
const success = () => emit('success')
const fail = () => emit('fail')
</script>
<template> <template>
<div class="flip-card" :class="{ flipped }" @click="flip"> <div class="flip-card" :class="{ flipped }" @click="flip">
<div class="flip-card-inner"> <div class="flip-card-inner">
@@ -8,10 +23,10 @@
<div class="references" v-html="card.references"></div> <div class="references" v-html="card.references"></div>
<hr /> <hr />
<div class="buttons is-centered"> <div class="buttons is-centered">
<div class="button is-warning is-light" @click.stop="success"> <div class="button is-warning is-light" @click.stop="fail">
failed failed
</div> </div>
<div class="button is-success is-light" @click.stop="fail"> <div class="button is-success is-light" @click.stop="success">
got it got it
</div> </div>
</div> </div>
@@ -20,29 +35,6 @@
</div> </div>
</template> </template>
<script lang="ts">
import { defineComponent, PropType, ref } from 'vue'
import { Card } from '../models/Card'
export default defineComponent({
name: 'FlipCard',
props: {
card: { type: Object as PropType<Card>, required: true }
},
emits: ['success', 'fail'],
setup(_, context) {
const flipped = ref(false)
const flip = () => (flipped.value = !flipped.value)
const success = () => context.emit('success')
const fail = () => context.emit('fail')
return { flip, flipped, success, fail }
}
})
</script>
<style scoped lang="scss"> <style scoped lang="scss">
$border-radius: 0.5rem; $border-radius: 0.5rem;

View File

@@ -7,8 +7,8 @@ import { Card } from '@/modules/card/models/Card'
import { RepetitionCard } from '@/modules/card/models/RepetitionCard' import { RepetitionCard } from '@/modules/card/models/RepetitionCard'
import { useUserRepoStore } from '@/modules/repo/store/userRepo.store' import { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
import { decodeBase64ToUTF8 } from '@/utils/decodeBase64ToUTF8' import { decodeBase64ToUTF8 } from '@/utils/decodeBase64ToUTF8'
import { asyncComputed } from '@vueuse/core' import { useAsyncState } from '@vueuse/core'
import { isAfter } from 'date-fns' import { addDays, isAfter } from 'date-fns'
import { computed, nextTick, watch } from 'vue' import { computed, nextTick, watch } from 'vue'
interface Repetition { interface Repetition {
@@ -23,62 +23,95 @@ export const useSpacedRepetitionCards = () => {
const cardFiles = computed(() => const cardFiles = computed(() =>
store.files.filter( store.files.filter(
(file) => file.path?.startsWith('_cards') && file.path?.endsWith('.md') (file) =>
file.path !== undefined &&
file.path.startsWith('_cards') &&
file.path.endsWith('.md')
) )
) )
const cards = asyncComputed(async () => { const {
const cards: Repetition[] = [] state: cards,
isReady,
execute
} = useAsyncState(
async () => {
const cards: Repetition[] = []
for (const cardFile of cardFiles.value) { for (const cardFile of cardFiles.value) {
if (!cardFile.sha) { if (!cardFile.sha) {
continue continue
} }
const repetitionId = data.generateId( const repetition = await data.getOrCreate<
DataType.RepetitionCard, DataType.RepetitionCard,
cardFile.sha RepetitionCard
) >(data.generateId(DataType.RepetitionCard, cardFile.path), {
let repetition = await data.get<DataType.RepetitionCard, RepetitionCard>(
repetitionId
)
if (!repetition) {
const newRepetition: RepetitionCard = {
_id: repetitionId,
$type: DataType.RepetitionCard, $type: DataType.RepetitionCard,
level: 1, level: 1,
repeatDate: new Date() repeatDate: new Date()
})
if (isAfter(new Date(repetition.repeatDate), new Date())) {
continue
} }
await data.add<DataType.RepetitionCard>(newRepetition)
repetition = (await data.get<DataType.RepetitionCard, RepetitionCard>( const { getRawContent } = useFile(cardFile.sha, false)
repetitionId const content = (await getRawContent()) ?? ''
)) as RepetitionCard
const [front, back, references] =
decodeBase64ToUTF8(content).split('___') ?? []
cards.push({
repetition,
card: {
front: toHTML(front),
back: toHTML(back),
references: toHTML(references)
}
})
} }
if (isAfter(new Date(repetition.repeatDate), new Date())) { console.log(cards)
continue
}
const { getRawContent } = useFile(cardFile.sha, false) return cards
const content = await getRawContent() },
[],
{ immediate: false }
)
const [front, back, references] = const failRepetition = async (cardId: string) => {
decodeBase64ToUTF8(content ?? '').split('___') ?? [] const repetition = await data.get<DataType.RepetitionCard, RepetitionCard>(
cardId
cards.push({ )
repetition, if (!repetition) {
card: { return
front: toHTML(front),
back: toHTML(back),
references: toHTML(references)
}
})
} }
return cards await data.update<DataType.RepetitionCard, RepetitionCard>({
}, []) ...repetition,
repeatDate: addDays(new Date(), 1)
})
await execute()
}
const successRepetition = async (cardId: string) => {
const repetition = await data.get<DataType.RepetitionCard, RepetitionCard>(
cardId
)
if (!repetition) {
return
}
await data.update<DataType.RepetitionCard, RepetitionCard>({
...repetition,
level: repetition.level,
repeatDate: addDays(new Date(), repetition.level)
})
await execute()
}
watch( watch(
cards, cards,
@@ -89,5 +122,7 @@ export const useSpacedRepetitionCards = () => {
{ immediate: true } { immediate: true }
) )
return { cards } watch(cardFiles, () => execute(), { immediate: true })
return { cards, failRepetition, successRepetition, isLoading: !isReady }
} }

View File

@@ -1,3 +1,23 @@
<script lang="ts" setup>
import FluxNote from '@/components/FluxNote.vue'
import FlipCard from '@/modules/card/components/FlipCard.vue'
import { useSpacedRepetitionCards } from '@/modules/card/hooks/useSpacedRepetitionCards'
import { computed } from 'vue'
defineProps<{
user: string
repo: string
}>()
const { cards, isLoading, successRepetition, failRepetition } =
useSpacedRepetitionCards()
const firstCard = computed(() => {
const [repetitionCard] = cards.value
return repetitionCard
})
</script>
<template> <template>
<div class="spaced-repetition-card repo-note"> <div class="spaced-repetition-card repo-note">
<flux-note <flux-note
@@ -6,45 +26,20 @@
:repo="repo" :repo="repo"
:with-content="false" :with-content="false"
> >
<section v-if="firstCard"> <section v-if="isLoading">Loading...</section>
<section v-else-if="firstCard">
<h3 class="subtitle is-3">Level: {{ firstCard.repetition.level }}</h3> <h3 class="subtitle is-3">Level: {{ firstCard.repetition.level }}</h3>
<flip-card :card="firstCard.card" /> <flip-card
:card="firstCard.card"
@success="() => successRepetition(firstCard.repetition._id ?? '')"
@fail="() => failRepetition(firstCard.repetition._id ?? '')"
/>
</section> </section>
<section v-else>No cards to review!</section> <section v-else>No cards to review!</section>
</flux-note> </flux-note>
</div> </div>
</template> </template>
<script lang="ts">
import { useSpacedRepetitionCards } from '@/modules/card/hooks/useSpacedRepetitionCards'
import { computed, defineComponent } from 'vue'
import FluxNote from '@/components/FluxNote.vue'
import FlipCard from '@/modules/card/components/FlipCard.vue'
export default defineComponent({
name: 'SpacedRepetitionCard',
components: {
FluxNote,
FlipCard
},
props: {
user: { type: String, required: true },
repo: { type: String, required: true }
},
setup() {
const { cards } = useSpacedRepetitionCards()
const firstCard = computed(() => {
const [repetitionCard] = cards.value
return repetitionCard
})
return {
firstCard
}
}
})
</script>
<style scoped lang="scss"> <style scoped lang="scss">
.spaced-repetition-card { .spaced-repetition-card {
display: flex; display: flex;