Merge branch 'main' of github.com:jcalixte/lite-note

This commit is contained in:
Julien Calixte
2023-08-13 21:07:24 +02:00
27 changed files with 1377 additions and 970 deletions

View File

@@ -1,3 +1,5 @@
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
env: {
@@ -9,7 +11,8 @@ module.exports = {
'eslint:recommended',
'plugin:vue/recommended',
'@vue/typescript/recommended',
'@vue/prettier/@typescript-eslint',
'@vue/prettier',
'@vue/eslint-config-typescript',
'plugin:prettier-vue/recommended'
],
rules: {
@@ -26,6 +29,7 @@ module.exports = {
arrowParens: 'always'
}
],
semi: 0,
'vue/no-v-html': 'off',
'no-restricted-imports': [
'error',

View File

@@ -15,7 +15,7 @@
"@octokit/core": "^5.0.0",
"@octokit/rest": "^20.0.1",
"@toycode/markdown-it-class": "^1.2.4",
"@vueuse/core": "^10.2.1",
"@vueuse/core": "^10.3.0",
"bulma": "^0.9.4",
"date-fns": "^2.30.0",
"isomorphic-fetch": "^3.0.0",
@@ -25,10 +25,9 @@
"markdown-it-footnote": "^3.0.3",
"markdown-it-iframe": "^1.0.0",
"markdown-it-latex": "^0.2.0",
"markdown-it-svg-code-copy": "^1.0.0",
"nanoid": "^4.0.2",
"notyf": "^3.10.0",
"pinia": "^2.1.4",
"pinia": "^2.1.6",
"pouchdb-adapter-indexeddb": "^8.0.1",
"pouchdb-browser": "^8.0.1",
"register-service-worker": "^1.7.2",
@@ -39,29 +38,29 @@
"vue-router": "^4.2.4"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/core": "^7.22.9",
"@rushstack/eslint-patch": "^1.3.2",
"@types/markdown-it": "^12.2.3",
"@types/node": "^20.3.3",
"@types/node": "^20.4.8",
"@types/pouchdb-browser": "^6.1.3",
"@types/sanitize-html": "^2.6.0",
"@typescript-eslint/eslint-plugin": "^5.6.0",
"@typescript-eslint/parser": "^5.6.0",
"@types/sanitize-html": "^2.9.0",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.1",
"@vitejs/plugin-vue": "^4.2.3",
"@vue/compiler-sfc": "^3.2.24",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^9.1.0",
"@vue/test-utils": "^2.0.0-beta.8",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-prettier-vue": "^3.1.0",
"eslint-plugin-vue": "^8.2.0",
"prettier": "^2.5.1",
"sass": "^1.63.6",
"@vue/compiler-sfc": "^3.3.4",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^11.0.3",
"eslint": "^8.46.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-prettier-vue": "^4.2.0",
"eslint-plugin-vue": "^9.16.1",
"prettier": "^3.0.1",
"sass": "^1.64.2",
"typescript": "~4.5.3",
"vite": "^4.4.4",
"vite": "^4.4.8",
"vite-plugin-pwa": "^0.16.4",
"vitest": "^0.33.0",
"webpack": "^5.88.1"
"vitest": "^0.34.1",
"webpack": "^5.88.2"
}
}

1987
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,10 +6,18 @@ const { isReady } = useGitHubLogin()
</script>
<template>
<div id="main-app">
<router-view v-if="isReady" />
<new-version />
</div>
</template>
<style lang="scss">
@import 'styles/app';
#main-app {
height: 100vh;
display: flex;
flex: 1;
}
</style>

View File

@@ -102,7 +102,7 @@ const focus = () => scrollToFocusedNote(undefined, true)
<div class="repo-title">
<h1 class="title is-1">
[<router-link
:to="{ name: 'Home', params: { user, repo } }"
:to="{ name: 'FluxNoteView', params: { user, repo } }"
@click="resetStackedNotes"
>{{ repo }}</router-link
>]

View File

@@ -1,3 +1,23 @@
<script lang="ts" setup>
import { useBacklinks } from '@/hooks/useBacklinks.hook'
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
import { computed } from 'vue'
const props = defineProps<{
sha: string
}>()
const shaProp = computed((props) => props.sha)
const { backlink } = useBacklinks(shaProp)
const { addStackedNote } = useQueryStackedNotes()
const hasBacklinks = computed(() => (backlink.value?.links.length ?? 0) > 0)
const emitNote = (sha: string) => {
addStackedNote(props.sha, sha)
}
</script>
<template>
<div v-if="hasBacklinks" class="linked-notes">
<h5 class="subtitle is-5">🔗</h5>
@@ -11,36 +31,6 @@
</div>
</template>
<script lang="ts">
import { useBacklinks } from '@/hooks/useBacklinks.hook'
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
import { computed, defineComponent } from 'vue'
export default defineComponent({
name: 'LinkedNotes',
props: {
sha: { type: String, required: true }
},
setup(props) {
const { backlink } = useBacklinks(props.sha)
const { addStackedNote } = useQueryStackedNotes()
const hasBacklinks = computed(
() => (backlink.state.value?.links.length ?? 0) > 0
)
const emitNote = (sha: string) => {
addStackedNote(props.sha, sha)
}
return {
backlink: backlink.state,
hasBacklinks,
emitNote
}
}
})
</script>
<style scoped lang="scss">
.linked-notes {
padding: 1rem;

View File

@@ -1,7 +1,7 @@
<template>
<div class="login-github">
<br />
<a ;href="url" target="_blank" rel="noopener noreferrer">
<a :href="url.toString()" target="_blank" rel="noopener noreferrer">
login to
<img src="@/assets/icons/github.svg" alt="GitHub icon" />
</a>
@@ -25,8 +25,3 @@ export default defineComponent({
}
})
</script>
<style lang="scss" scoped>
.login-github {
}
</style>

View File

@@ -32,7 +32,7 @@ const reload = () => {
</span>
</div>
<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" />
<span v-else>Reload</span>
</button>

View File

@@ -6,7 +6,7 @@
<div class="buttons is-centered">
<router-link
:to="{
name: 'Home',
name: 'FluxNoteView',
params: { user: 'lite-note', repo: 'getting-started' }
}"
class="button is-primary"
@@ -32,7 +32,7 @@
>
<router-link
:to="{
name: 'Home',
name: 'FluxNoteView',
params: {
user: username,
repo: favoriteRepo.name
@@ -102,15 +102,15 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import SignInGithub from '@/components/SignInGithub.vue'
import { useForm } from '@/hooks/useForm.hook'
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
import { useFavoriteRepos } from '@/modules/repo/hooks/useFavoriteRepos.hook'
import SignInGithub from '@/components/SignInGithub.vue'
import LastVisited from '@/modules/history/components/LastVisited.vue'
import { useFavoriteRepos } from '@/modules/repo/hooks/useFavoriteRepos.hook'
import { defineComponent } from 'vue'
export default defineComponent({
name: 'WelcomeWord',
name: 'WelcomeWorld',
components: { SignInGithub, LastVisited },
setup() {
const { isLogged, username } = useGitHubLogin()

View File

@@ -3,10 +3,12 @@ import { data } from '@/data/data'
import { DataType } from '@/data/DataType.enum'
import { BacklinkNote } from '@/modules/note/models/BacklinkNote'
import { useAsyncState } from '@vueuse/core'
import { onUnmounted } from 'vue'
import { ComputedRef, onUnmounted, toValue } from 'vue'
export const useBacklinks = (sha: string) => {
const backlink = useAsyncState(
export const useBacklinks = (sha: string | ComputedRef<string>) => {
sha = toValue(sha)
const { state: backlink, execute } = useAsyncState(
data.get<DataType.BacklinkNote, BacklinkNote>(
data.generateId(DataType.BacklinkNote, sha)
),
@@ -21,7 +23,7 @@ export const useBacklinks = (sha: string) => {
if (fileSha !== sha) {
return
}
backlink.execute()
execute()
},
{
retro: true

View File

@@ -12,7 +12,7 @@ export const useForm = () => {
}
push({
name: 'Home',
name: 'FluxNoteView',
params: {
user: userInput.value,
repo: repoInput.value

View File

@@ -8,7 +8,6 @@ import markdownItCheckbox from 'markdown-it-checkbox'
import markdownItFootnote from 'markdown-it-footnote'
import markdownItIframe from 'markdown-it-iframe'
import markdownItLatex from 'markdown-it-latex'
import markdownItSvgCodeCopy from 'markdown-it-svg-code-copy'
const md = new MarkdownIt({
typographer: true,
@@ -31,14 +30,6 @@ const md = new MarkdownIt({
})
.use(twitterPlugin)
.use(markdownItCheckbox)
.use(markdownItSvgCodeCopy, {
svg: `<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-copy" width="36" height="36" viewBox="0 0 24 24" stroke-width="1.5" stroke="#2c3a47" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<rect x="8" y="8" width="10" height="10" rx="2" />
<path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2" />
</svg>`,
buttonClass: 'button is-light'
})
.use(markdownItFootnote)
.use(markdownItLatex)
.use(markdownItIframe, {

View File

@@ -1,10 +1,8 @@
import { NOTE_WIDTH } from '@/constants/note-width'
import { useOverlay } from '@/hooks/useOverlay.hook'
import { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
import { readonly, ref } from 'vue'
import { useWindowSize } from '@vueuse/core'
import { nextTick } from 'vue'
import { nextTick, readonly, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const stackedNotes = ref<string[]>([])
@@ -90,7 +88,7 @@ export const useQueryStackedNotes = () => {
const newStackedNotes = getStackedNotes()
push({
name: currentRoute.value.name ?? 'Home',
name: currentRoute.value.name ?? 'FluxNoteView',
params: {
user: store.user,
repo: store.repo

View File

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

View File

@@ -7,6 +7,7 @@ const props = defineProps<{ cards: Repetition[] }>()
const emits = defineEmits<{
success: [id: string]
fail: [id: string]
needsReview: [id: string]
}>()
const cards = ref(
@@ -32,6 +33,12 @@ const goToNextCard = (success: boolean) => {
currentIndex.value++
}
const needsReview = () => {
const id = cards.value[currentIndex.value].repetition._id ?? ''
emits('needsReview', id)
currentIndex.value++
}
</script>
<template>
@@ -52,6 +59,7 @@ const goToNextCard = (success: boolean) => {
:card="card.card"
@success="goToNextCard(true)"
@fail="goToNextCard(false)"
@needs-review="needsReview"
/>
</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
import { DataType } from '@/data/DataType.enum'
import { data } from '@/data/data'
import { DataType } from '@/data/DataType.enum'
import { useFile } from '@/hooks/useFile.hook'
import { useLinks } from '@/hooks/useLinks.hook'
import { useMarkdown } from '@/hooks/useMarkdown.hook'
@@ -53,12 +53,14 @@ export const useSpacedRepetitionCards = () => {
>(data.generateId(DataType.RepetitionCard, cardFile.path), {
$type: DataType.RepetitionCard,
level: 1,
repeatDate: new Date()
repeatDate: new Date(),
needsReview: false
})
if (
isAfter(new Date(repetition.repeatDate), new Date()) ||
repetition.level === MAX_LEVEL
repetition.level === MAX_LEVEL ||
repetition.needsReview
) {
continue
}
@@ -95,6 +97,7 @@ export const useSpacedRepetitionCards = () => {
await data.update<DataType.RepetitionCard, RepetitionCard>({
...repetition,
needsReview: false,
level: Math.min(repetition.level + 1, MAX_LEVEL),
repeatDate: addDays(new Date(), 2 ** repetition.level)
})
@@ -113,10 +116,25 @@ export const useSpacedRepetitionCards = () => {
await data.update<DataType.RepetitionCard, RepetitionCard>({
...repetition,
level,
needsReview: false,
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(
cards,
() =>
@@ -128,5 +146,11 @@ export const useSpacedRepetitionCards = () => {
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> {
level: number
repeatDate: Date
needsReview: boolean
}

View File

@@ -9,7 +9,7 @@
<div>
<router-link
:to="{
name: `Home`,
name: `FluxNoteView`,
params: { user: lastVisitedRepo.user, repo: lastVisitedRepo.repo }
}"
>{{ lastVisitedRepo.user }}/{{ lastVisitedRepo.repo }}</router-link

View File

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

View File

@@ -1,6 +1,5 @@
import { RouteRecordRaw, createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/HomeApp.vue'
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: Array<RouteRecordRaw> = [
{
@@ -9,10 +8,10 @@ const routes: Array<RouteRecordRaw> = [
component: () => import('@/views/RepoList.vue')
},
{
path: '/:user?/:repo?',
name: 'Home',
path: '/:user/:repo',
name: 'FluxNoteView',
props: true,
component: Home
component: () => import('@/views/FluxNoteView.vue')
},
{
path: '/:user/:repo/share/:note',
@@ -44,11 +43,22 @@ const routes: Array<RouteRecordRaw> = [
props: true,
component: () => import('@/views/SpacedRepetitionCard.vue')
},
{
path: '/:user/:repo/need-review-cards',
name: 'NeedReviewCards',
props: true,
component: () => import('@/views/NeedReviewCards.vue')
},
{
path: '/about',
name: 'About',
component: () => import('@/views/AboutApp.vue')
},
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/:catchAll(.*)',
name: 'SpaceCowboy',

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import FluxNote from '@/components/FluxNote.vue'
import { useComputeBacklinks } from '@/hooks/useComputeBacklinks.hook'
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
import { computed } from 'vue'
useQueryStackedNotes()
useComputeBacklinks()
const props = defineProps<{ user: string; repo: string }>()
const routeKey = computed(() => `${props.user}-${props.repo}`)
</script>
<template>
<flux-note :key="routeKey" :user="user" :repo="repo" />
</template>

View File

@@ -1,28 +1,13 @@
<script setup lang="ts">
import AuthorizeUser from '@/components/AuthorizeUser.vue'
import { useComputeBacklinks } from '@/hooks/useComputeBacklinks.hook'
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
import { computed, defineAsyncComponent } from 'vue'
const FluxNote = defineAsyncComponent(() => import('@/components/FluxNote.vue'))
const WelcomeWorld = defineAsyncComponent(
() => import('@/components/WelcomeWorld.vue')
)
const props = defineProps<{ user?: string; repo?: string }>()
useQueryStackedNotes()
useComputeBacklinks()
const routeKey = computed(() => `${props.user}-${props.repo}`)
import WelcomeWorld from '@/components/WelcomeWorld.vue'
</script>
<template>
<div v-if="!user || !repo" class="home content">
<div class="home content">
<authorize-user class="authorize" />
<welcome-world />
</div>
<flux-note v-else :key="routeKey" :user="user" :repo="repo" />
</template>
<style lang="scss" scoped>

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

@@ -31,7 +31,7 @@
<router-link
:to="{
name: 'Home',
name: 'FluxNoteView',
params: { user: username, repo: repo.name }
}"
>{{ repo.name }}</router-link
@@ -62,7 +62,7 @@
<td>
<router-link
:to="{
name: 'Home',
name: 'FluxNoteView',
params: { user: username, repo: repo.name }
}"
>{{ repo.name }}</router-link
@@ -77,11 +77,11 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
import { useRepoList } from '@/modules/repo/hooks/useRepoList.hook'
import { useRepos } from '@/hooks/useRepos.hook'
import GoBack from '@/components/GoBack.vue'
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
import { useRepos } from '@/hooks/useRepos.hook'
import { useRepoList } from '@/modules/repo/hooks/useRepoList.hook'
import { defineComponent } from 'vue'
export default defineComponent({
name: 'RepoList',

View File

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

View File

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