✨ (card) make the buttons work
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user