♻️ (app)

This commit is contained in:
Julien Calixte
2023-08-14 14:08:10 +02:00
parent 111794a40b
commit c0182c7f57
24 changed files with 4281 additions and 2108 deletions

View File

@@ -1,5 +1,8 @@
require('@rushstack/eslint-patch/modern-module-resolution')
const DEV_TOOL_ACTIVATED =
process.env.NODE_ENV === 'production' ? 'warn' : 'off'
module.exports = {
root: true,
env: {
@@ -11,13 +14,12 @@ module.exports = {
'eslint:recommended',
'plugin:vue/recommended',
'@vue/typescript/recommended',
'@vue/prettier',
'@vue/eslint-config-typescript',
'plugin:prettier-vue/recommended'
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-console': DEV_TOOL_ACTIVATED,
'no-debugger': DEV_TOOL_ACTIVATED,
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/camelcase': 'off',
'prettier-vue/prettier': [
@@ -29,7 +31,6 @@ module.exports = {
arrowParens: 'always'
}
],
semi: 0,
'vue/no-v-html': 'off',
'no-restricted-imports': [
'error',

View File

@@ -1,6 +0,0 @@
{
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"arrowParens": "always"
}

View File

@@ -7,7 +7,7 @@
"build": "vite build",
"serve": "vite preview",
"test": "vitest",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
"lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore --fix src",
"pwa:asset": "npx vue-pwa-asset-generator -a public/img/logo.png --no-manifest"
},
"dependencies": {
@@ -52,7 +52,6 @@
"@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",

6157
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,6 @@ import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
import { useVisitRepo } from '@/modules/history/hooks/useVisitRepo.hook'
import { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
import { useUserSettings } from '@/modules/user/hooks/useUserSettings.hook'
import {
computed,
defineAsyncComponent,
@@ -43,11 +42,14 @@ const props = withDefaults(
}
)
const user = computed(() => props.user)
const repo = computed(() => props.repo)
const refProps = toRefs(props)
const store = useUserRepoStore()
useUserSettings()
const { visitRepo } = useVisitRepo({ user: props.user, repo: props.repo })
const { toHTML } = useMarkdown(props.repo)
const { visitRepo } = useVisitRepo({ user: user, repo: repo })
const { toHTML } = useMarkdown(repo)
const { listenToClick } = useLinks('note-display')
const { stackedNotes, resetStackedNotes } = useQueryStackedNotes()
const { scrollToFocusedNote } = useQueryStackedNotes()
@@ -104,7 +106,8 @@ const focus = () => scrollToFocusedNote(undefined, true)
[<router-link
:to="{ name: 'FluxNoteView', params: { user, repo } }"
@click="resetStackedNotes"
>{{ repo }}</router-link
>
{{ repo }} </router-link
>]
<img
v-if="store.isReadmeOffline"
@@ -123,7 +126,7 @@ const focus = () => scrollToFocusedNote(undefined, true)
v-else-if="withContent"
class="note-display"
v-html="renderedContent"
></p>
/>
</div>
<stacked-note
v-for="(stackedNote, index) in stackedNotes"
@@ -160,6 +163,7 @@ $header-height: 40px;
table {
color: var(--font-color);
background-color: var(--background-color);
thead {
th {
color: var(--font-color);

View File

@@ -22,17 +22,22 @@ const props = defineProps<{
sha: string
}>()
const sha = computed(() => props.sha)
const index = computed(() => props.index)
const repo = computed(() => props.repo)
const { scrollToFocusedNote } = useQueryStackedNotes()
const { content } = useFile(props.sha)
const { content } = useFile(sha)
const className = computed(() => `stacked-note-${props.index}`)
const { listenToClick } = useLinks(className.value, props.sha)
const { listenToClick } = useLinks(className.value, sha)
const titleClassName = computed(() => `title-${className.value}`)
useTitleNotes(props.repo)
useTitleNotes(repo)
const store = useUserRepoStore()
const hasBacklinks = computed(() => store.userSettings?.backlink)
const { displayNoteOverlay } = useNoteOverlay(className.value, props.index)
const { displayNoteOverlay } = useNoteOverlay(className.value, index)
const displayedTitle = computed(() => filenameToNoteTitle(props.title))
watch(content, () => {

View File

@@ -2,12 +2,12 @@ import { useMarkdown } from '@/hooks/useMarkdown.hook'
import { prepareNoteCache } from '@/modules/note/cache/prepareNoteCache'
import { getFileContent } from '@/modules/repo/services/repo'
import { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
import { ref } from 'vue'
import { Ref, ref, toValue } from 'vue'
export const useFile = (sha: string, retrieveContent = true) => {
const { render } = useMarkdown(sha)
export const useFile = (sha: Ref<string> | string, retrieveContent = true) => {
const { render } = useMarkdown(toValue(sha))
const store = useUserRepoStore()
const { getCachedNote, saveCacheNote } = prepareNoteCache(sha)
const { getCachedNote, saveCacheNote } = prepareNoteCache(toValue(sha))
const fromCache = ref(false)
const content = ref('')
@@ -42,7 +42,11 @@ export const useFile = (sha: string, retrieveContent = true) => {
return cachedNote.content
}
const contentFile = await getFileContent(store.user, store.repo, sha)
const contentFile = await getFileContent(
store.user,
store.repo,
toValue(sha)
)
if (!contentFile) {
return null

View File

@@ -1,11 +1,11 @@
import { noteEventBus } from '@/bus/noteEventBus'
import { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
import { isExternalLink } from '@/utils/link'
import { ComputedRef, onUnmounted, toValue } from 'vue'
import { ComputedRef, onUnmounted, Ref, toValue } from 'vue'
export const useLinks = (
className: ComputedRef<string> | string,
sha?: string
sha?: Ref<string> | string
) => {
const store = useUserRepoStore()
@@ -31,7 +31,7 @@ export const useLinks = (
noteEventBus.emit({
path: href,
currentNoteSHA: sha,
currentNoteSHA: toValue(sha),
user: store.user,
repo: store.repo
})

View File

@@ -8,6 +8,7 @@ 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 { Ref, toValue } from 'vue'
const md = new MarkdownIt({
typographer: true,
@@ -37,13 +38,13 @@ const md = new MarkdownIt({
height: 400
})
export const useMarkdown = (defaultPrefix?: string) => {
export const useMarkdown = (defaultPrefix?: Ref<string> | string) => {
return {
toHTML: (content: string) => (content ? md.render(content) : ''),
render: (content: string, prefix?: string) =>
content
? md.render(decodeBase64ToUTF8(content), {
docId: defaultPrefix ?? prefix ?? ''
docId: defaultPrefix ? toValue(defaultPrefix) : prefix ?? ''
})
: ''
}

View File

@@ -1,12 +1,11 @@
import { computed, onMounted, onUnmounted, watch } from 'vue'
import { NOTE_WIDTH } from '@/constants/note-width'
import { noteEventBus } from '@/bus/noteEventBus'
import { NOTE_WIDTH } from '@/constants/note-width'
import { useOverlay } from '@/hooks/useOverlay.hook'
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
import { resolvePath } from '@/modules/repo/services/resolvePath'
import { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
import { pathToNotePathTitle } from '@/utils/noteTitle'
import { computed, onMounted, onUnmounted, watch } from 'vue'
export const useNote = (containerClass: string) => {
const store = useUserRepoStore()

View File

@@ -1,20 +1,22 @@
import { computed, onMounted, ref } from 'vue'
import { NOTE_WIDTH } from '@/constants/note-width'
import { useOverlay } from '@/hooks/useOverlay.hook'
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
import { computed, onMounted, Ref, ref, toValue } from 'vue'
const BOOKMARK_WIDTH = 2
export const useNoteOverlay = (className: string, index: number) => {
export const useNoteOverlay = (
className: string,
index: Ref<number> | number
) => {
const { x, y, isMobile } = useOverlay()
const noteHeight = ref(0)
const displayNoteOverlay = computed(() => {
if (isMobile.value) {
return y.value > index * noteHeight.value
return y.value > toValue(index) * noteHeight.value
} else {
return x.value > index * NOTE_WIDTH
return x.value > toValue(index) * NOTE_WIDTH
}
})
@@ -32,7 +34,7 @@ export const useNoteOverlay = (className: string, index: number) => {
if (isMobile.value) {
noteElement.style.top = `0`
} else {
noteElement.style.left = `${(index + 1) * BOOKMARK_WIDTH}rem`
noteElement.style.left = `${(toValue(index) + 1) * BOOKMARK_WIDTH}rem`
const stackedNoteContainers = document.querySelectorAll(
'.stacked-note'

View File

@@ -2,11 +2,11 @@ import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
import { useNotes } from '@/modules/note/hooks/useNotes'
import { pathToNoteTitle } from '@/utils/noteTitle'
import { useTitle } from '@vueuse/core'
import { computed, watch } from 'vue'
import { computed, Ref, toValue, watch } from 'vue'
export const generateTitle = (titles: string[]) => titles.join(' | ')
export const useTitleNotes = (prefix: string) => {
export const useTitleNotes = (prefix: Ref<string> | string) => {
const { stackedNotes } = useQueryStackedNotes()
const { notes } = useNotes()
const titleNotes = computed(() =>
@@ -15,9 +15,9 @@ export const useTitleNotes = (prefix: string) => {
.map((note) => pathToNoteTitle(note.path ?? ''))
)
const title = useTitle(generateTitle([prefix, ...titleNotes.value]))
const title = useTitle(generateTitle([toValue(prefix), ...titleNotes.value]))
watch(titleNotes, () => {
title.value = generateTitle([prefix, ...titleNotes.value])
title.value = generateTitle([toValue(prefix), ...titleNotes.value])
})
}

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
<script lang="ts" setup>
import FlipCard from '@/modules/card/components/FlipCard.vue'
import { Repetition } from '@/modules/card/hooks/useSpacedRepetitionCards'
import { ref } from 'vue'
import { computed, ref } from 'vue'
const props = defineProps<{ cards: Repetition[] }>()
const emits = defineEmits<{
@@ -10,8 +10,10 @@ const emits = defineEmits<{
needsReview: [id: string]
}>()
const cards = ref(
[...props.cards].sort((a, b) =>
const propCards = computed(() => props.cards)
const sortedCards = ref(
[...propCards.value].sort((a, b) =>
a.repetition.level > b.repetition.level ? -1 : 1
)
)
@@ -19,14 +21,14 @@ const cards = ref(
const currentIndex = ref(0)
const goToNextCard = (success: boolean) => {
const id = cards.value[currentIndex.value].repetition._id ?? ''
const id = sortedCards.value[currentIndex.value].repetition._id ?? ''
if (success) {
emits('success', id)
} else {
const failedCard = cards.value.at(currentIndex.value)
const failedCard = sortedCards.value.at(currentIndex.value)
if (failedCard) {
cards.value.push(failedCard)
sortedCards.value.push(failedCard)
}
emits('fail', id)
}
@@ -35,7 +37,7 @@ const goToNextCard = (success: boolean) => {
}
const needsReview = () => {
const id = cards.value[currentIndex.value].repetition._id ?? ''
const id = sortedCards.value[currentIndex.value].repetition._id ?? ''
emits('needsReview', id)
currentIndex.value++
}
@@ -44,13 +46,13 @@ const needsReview = () => {
<template>
<div class="flip-card-list">
<h3 class="subtitle is-3">
Level: {{ cards[currentIndex].repetition.level }}
Level: {{ sortedCards[currentIndex].repetition.level }}
</h3>
<h4>cards left: {{ cards.length - currentIndex }}</h4>
<h4>cards left: {{ sortedCards.length - currentIndex }}</h4>
<div v-if="currentIndex < cards.length">
<div v-if="currentIndex < sortedCards.length">
<flip-card
v-for="(card, index) in cards"
v-for="(card, index) in sortedCards"
:key="card.repetition._id ?? ''"
class="card"
:style="{

View File

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

View File

@@ -1,18 +1,22 @@
import { data } from '@/data/data'
import { DataType } from '@/data/DataType.enum'
import { History } from '@/data/models/History'
import { Ref, toValue } from 'vue'
const HISTORY_ID = data.generateId(DataType.History, 'history')
const MAX_REPO_HISTORY = 10
export const useVisitRepo = (newRepo: { user: string; repo: string }) => {
export const useVisitRepo = (newRepo: {
user: Ref<string> | string
repo: Ref<string> | string
}) => {
const visitRepo = async () => {
const history = await data.get<DataType.History, History>(HISTORY_ID)
if (!history) {
const newHistory: History = {
_id: HISTORY_ID,
$type: DataType.History,
repos: [newRepo]
repos: [{ user: toValue(newRepo.user), repo: toValue(newRepo.repo) }]
}
await data.add<DataType.History>(newHistory)
return
@@ -22,10 +26,10 @@ export const useVisitRepo = (newRepo: { user: string; repo: string }) => {
(repo) => repo.user !== newRepo.user && repo.repo !== newRepo.repo
)
const historyRepos = [newRepo, ...clearedRepos].slice(
0,
MAX_REPO_HISTORY - 1
)
const historyRepos = [
{ user: toValue(newRepo.user), repo: toValue(newRepo.repo) },
...clearedRepos
].slice(0, MAX_REPO_HISTORY - 1)
const newHistory: History = {
...history,

1
src/shims-vue.d.ts vendored
View File

@@ -11,3 +11,4 @@ declare module 'markdown-it-footnote'
declare module 'markdown-it-regexp'
declare module 'markdown-it-latex'
declare module 'markdown-it-iframe'
declare module '@rushstack/eslint-patch/modern-module-resolution'

View File

@@ -1,5 +1,5 @@
@charset "utf-8";
@import url('https://fonts.googleapis.com/css2?family=Courier+Prime&family=Courgette&family=IBM+Plex+Serif&family=Kiwi+Maru&family=Maven+Pro&family=Noto+Sans+KR&family=Tajawal&family=Domine&family=Amiri&display=swap&family=Montagu+Slab&family=Gowun+Batang&family=Cormorant+Garamond&family=Forum');
@import url("https://fonts.googleapis.com/css2?family=Courier+Prime&family=Courgette&family=IBM+Plex+Serif&family=Kiwi+Maru&family=Maven+Pro&family=Noto+Sans+KR&family=Tajawal&family=Domine&family=Amiri&display=swap&family=Montagu+Slab&family=Gowun+Batang&family=Cormorant+Garamond&family=Forum");
/**
font-family: 'Courgette', cursive;
@@ -16,7 +16,7 @@
$primary: #2c3a47;
$link: #44b9a0;
$light-link: lighten($link, 45%);
$family-primary: 'Courier Prime', monospace;
$family-primary: "Courier Prime", monospace;
:root {
--primary-color: #{$primary};
@@ -27,7 +27,7 @@ $family-primary: 'Courier Prime', monospace;
--note-width: 620px;
}
@import '../../node_modules/bulma/bulma.sass';
@import "../../node_modules/bulma/bulma.sass";
html {
overflow-y: auto;
@@ -42,7 +42,6 @@ body {
}
@media screen and (min-width: 769px) {
html,
body {
overflow-y: hidden;
@@ -75,7 +74,7 @@ a {
&::after {
margin-left: 0.2rem;
vertical-align: text-top;
content: url('assets/icons/external-link.svg');
content: url("assets/icons/external-link.svg");
}
}
}
@@ -93,7 +92,6 @@ a {
}
@media print {
html,
body {
overflow-y: auto;

View File

@@ -15,7 +15,8 @@ export const markdownItPlugin = (
const regexp = RegExp('^' + regex.source, flags)
const parse = (state: any, silent: boolean) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const parse = (state: any, silent: boolean): boolean => {
const match = regexp.exec(state.src.slice(state.pos))
if (!match) {

View File

@@ -3,7 +3,7 @@ import { markdownItPlugin } from '@/utils/markdown/markdown-it-regexp'
export const twitterPlugin = markdownItPlugin(
/@\[tweet]\((.*?)\)/g,
(matches: RegExpExecArray[]) => {
const [_, tweetId] = matches
const [, tweetId] = matches
return `<span id="tweet-${tweetId}" data-tweet-id="${tweetId}" class="markdown-tweet"></span>`
}

View File

@@ -1,14 +1,14 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
window.twttr = (function (d, s, id) {
let js,
fjs = d.getElementsByTagName(s)[0],
const fjs = d.getElementsByTagName(s)[0],
t = window.twttr || {}
if (d.getElementById(id)) {
return t
}
js = d.createElement(s)
const js = d.createElement(s)
js.id = id
js.src = 'https://platform.twitter.com/widgets.js'
fjs.parentNode.insertBefore(js, fjs)

View File

@@ -89,9 +89,7 @@ export default defineComponent({
setup() {
const { username } = useGitHubLogin()
const { isReady } = useRepos()
// eslint-disable-next-line prettier-vue/prettier
const { favoriteRepos, otherRepos, favoriteCheckboxes, toggleCheckbox } =
// eslint-disable-next-line prettier-vue/prettier
useRepoList()
return {

View File

@@ -1,3 +1,21 @@
<script lang="ts" setup>
import { useFile } from '@/hooks/useFile.hook'
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
import { computed, defineAsyncComponent, onMounted } from 'vue'
const FluxNote = defineAsyncComponent(() => import('@/components/FluxNote.vue'))
const props = defineProps<{ user: string; repo: string; note: string }>()
const { resetStackedNotes } = useQueryStackedNotes()
const note = computed(() => props.note)
const { content } = useFile(note)
onMounted(() => {
resetStackedNotes()
})
</script>
<template>
<div class="share-notes">
<article class="message is-primary">
@@ -17,38 +35,6 @@
</div>
</template>
<script lang="ts">
import { useFile } from '@/hooks/useFile.hook'
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
import { defineAsyncComponent, defineComponent, onMounted } from 'vue'
const FluxNote = defineAsyncComponent(() => import('@/components/FluxNote.vue'))
export default defineComponent({
name: 'ShareNotes',
components: {
FluxNote
},
props: {
user: { type: String, required: true },
repo: { type: String, required: true },
note: { type: String, required: true }
},
setup(props) {
const { resetStackedNotes } = useQueryStackedNotes()
const { content } = useFile(props.note)
onMounted(() => {
resetStackedNotes()
})
return {
content
}
}
})
</script>
<style scoped lang="scss">
.share-notes {
background-color: var(--background-color);

View File

@@ -1,9 +1,9 @@
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import { defineConfig } from 'vite'
import { VitePWA } from 'vite-plugin-pwa'
import { UserConfigExport } from 'vitest/dist/config'
import { VitePWA } from 'vite-plugin-pwa'
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
import { defineConfig } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
const mainColor = '#ffffff'