✨ (notes) add routing between notes
This commit is contained in:
30
src/hooks/useFile.hook.ts
Normal file
30
src/hooks/useFile.hook.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ref } from 'vue'
|
||||
import { request } from '@octokit/request'
|
||||
import { useMarkdown } from '@/hooks/useMarkdown.hook'
|
||||
|
||||
export const useFile = (owner: string, repo: string, sha: string) => {
|
||||
const content = ref('')
|
||||
|
||||
const getContent = async () => {
|
||||
const { render } = useMarkdown()
|
||||
const file = await request(
|
||||
'GET /repos/{owner}/{repo}/git/blobs/{file_sha}',
|
||||
{
|
||||
owner,
|
||||
repo,
|
||||
file_sha: sha
|
||||
}
|
||||
)
|
||||
|
||||
if (!file) {
|
||||
return
|
||||
}
|
||||
content.value = render(file.data.content)
|
||||
}
|
||||
|
||||
getContent()
|
||||
|
||||
return {
|
||||
content
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,27 @@
|
||||
export const useLinks = (className: string) => {
|
||||
const linkNote: EventListenerOrEventListenerObject = (e) => {
|
||||
e.preventDefault()
|
||||
console.log('use links')
|
||||
import { noteEventBus } from '@/bus/noteBusEvent'
|
||||
import { onUnmounted } from '@vue/runtime-core'
|
||||
|
||||
const LINKS = ['http://', 'https://']
|
||||
|
||||
export const useLinks = (className: string, sha?: string) => {
|
||||
const linkNote: EventListener = (event) => {
|
||||
event.preventDefault()
|
||||
const target = event.target as HTMLElement
|
||||
const href = target.getAttribute('href')
|
||||
|
||||
if (!href) {
|
||||
return
|
||||
}
|
||||
|
||||
if (LINKS.some((link) => href.startsWith(link))) {
|
||||
window.open(href, '_blank')
|
||||
return
|
||||
}
|
||||
|
||||
noteEventBus.emit({
|
||||
path: href,
|
||||
currentNoteSHA: sha
|
||||
})
|
||||
}
|
||||
|
||||
const selector = `.${className} a`
|
||||
@@ -21,8 +41,11 @@ export const useLinks = (className: string) => {
|
||||
})
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
removeListeners()
|
||||
})
|
||||
|
||||
return {
|
||||
listenToClick,
|
||||
removeListeners
|
||||
listenToClick
|
||||
}
|
||||
}
|
||||
|
||||
20
src/hooks/useMarkdown.hook.ts
Normal file
20
src/hooks/useMarkdown.hook.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import markdownItClass from '@toycode/markdown-it-class'
|
||||
// import sanitizeHtml from 'sanitize-html'
|
||||
|
||||
const md = new MarkdownIt().use(markdownItClass, {
|
||||
h1: ['title', 'is-1'],
|
||||
h2: ['subtitle', 'is-2'],
|
||||
h3: ['subtitle', 'is-3'],
|
||||
h4: ['subtitle', 'is-4'],
|
||||
h5: ['subtitle', 'is-5'],
|
||||
h6: ['subtitle', 'is-6'],
|
||||
table: ['table', 'is-striped', 'is-hoverable', 'is-fullwidth']
|
||||
})
|
||||
|
||||
export const useMarkdown = () => {
|
||||
return {
|
||||
render: (content: string) =>
|
||||
md.render(decodeURIComponent(escape(atob(content))))
|
||||
}
|
||||
}
|
||||
98
src/hooks/useNote.hook.ts
Normal file
98
src/hooks/useNote.hook.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { nextTick, onUnmounted, watch } from '@vue/runtime-core'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import { noteEventBus } from '@/bus/noteBusEvent'
|
||||
import { ref } from '@vue/reactivity'
|
||||
import { useLinks } from '@/hooks/useLinks.hook'
|
||||
import { useRepo } from '@/hooks/useRepo.hook'
|
||||
|
||||
const sanitizePath = (path: string) => {
|
||||
if (path.startsWith('./')) {
|
||||
return decodeURIComponent(path.replace('./', ''))
|
||||
}
|
||||
return decodeURIComponent(path)
|
||||
}
|
||||
|
||||
export const useNote = (user: string, repo: string) => {
|
||||
const { push } = useRouter()
|
||||
const { readme, notFound, tree } = useRepo(user, repo)
|
||||
const { listenToClick } = useLinks('note-display')
|
||||
const { query } = useRoute()
|
||||
|
||||
const stackedNotes = ref(
|
||||
query.stackedNotes
|
||||
? Array.isArray(query.stackedNotes)
|
||||
? query.stackedNotes
|
||||
: [query.stackedNotes]
|
||||
: []
|
||||
)
|
||||
|
||||
const unsubscribe = noteEventBus.addEventBusListener(
|
||||
({ path, currentNoteSHA }) => {
|
||||
const currentFile = tree.value.find((file) => file.sha === currentNoteSHA)
|
||||
|
||||
const absolutePathArray = currentFile?.path?.split('/') ?? []
|
||||
absolutePathArray?.pop()
|
||||
const absolutePath = absolutePathArray.join('/')
|
||||
|
||||
const finalPath = absolutePath
|
||||
? `${sanitizePath(absolutePath)}/${sanitizePath(path)}`
|
||||
: sanitizePath(path)
|
||||
|
||||
const file = tree.value.find((file) => file.path === finalPath)
|
||||
|
||||
if (!file?.sha || stackedNotes.value.includes(file.sha)) {
|
||||
return
|
||||
}
|
||||
|
||||
const getStackedNotes = () => {
|
||||
if (!file?.sha) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (!currentNoteSHA) {
|
||||
return [file.sha]
|
||||
}
|
||||
|
||||
const [splittedStackedNotes] = stackedNotes.value
|
||||
.join(';')
|
||||
.split(currentNoteSHA)
|
||||
|
||||
return [
|
||||
...splittedStackedNotes.replaceAll(';;', ';').split(';'),
|
||||
currentNoteSHA,
|
||||
file.sha
|
||||
].filter((sha) => !!sha)
|
||||
}
|
||||
|
||||
const newStackedNotes = getStackedNotes()
|
||||
|
||||
push({
|
||||
name: 'Note',
|
||||
query: {
|
||||
stackedNotes: newStackedNotes
|
||||
}
|
||||
})
|
||||
|
||||
stackedNotes.value = newStackedNotes
|
||||
}
|
||||
)
|
||||
|
||||
watch(readme, () => {
|
||||
if (readme.value) {
|
||||
nextTick(() => {
|
||||
listenToClick()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
unsubscribe()
|
||||
})
|
||||
|
||||
return {
|
||||
readme,
|
||||
notFound,
|
||||
stackedNotes
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,67 @@
|
||||
import { onMounted, ref } from '@vue/runtime-core'
|
||||
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import { request } from '@octokit/request'
|
||||
import { useMarkdown } from '@/hooks/useMarkdown.hook'
|
||||
|
||||
const md = new MarkdownIt()
|
||||
interface Tree {
|
||||
path?: string
|
||||
mode?: string
|
||||
type?: string
|
||||
sha?: string
|
||||
size?: number
|
||||
url?: string
|
||||
}
|
||||
|
||||
export const useRepo = (owner: string, repo: string) => {
|
||||
const { render } = useMarkdown()
|
||||
const readme = ref<string | null>(null)
|
||||
const notFound = ref(false)
|
||||
const tree = ref<Tree[]>([])
|
||||
|
||||
onMounted(async () => {
|
||||
const README = await request('GET /repos/{owner}/{repo}/readme', {
|
||||
repo,
|
||||
owner
|
||||
})
|
||||
|
||||
if (README) {
|
||||
readme.value = md.render(
|
||||
decodeURIComponent(escape(atob(README.data.content)))
|
||||
)
|
||||
}
|
||||
|
||||
const commits = await request('GET /repos/{owner}/{repo}/commits', {
|
||||
owner,
|
||||
repo
|
||||
})
|
||||
|
||||
const lastCommit = commits.data.shift()
|
||||
|
||||
if (!lastCommit) {
|
||||
return
|
||||
}
|
||||
|
||||
const tree = await request(
|
||||
'GET /repos/{owner}/{repo}/git/trees/{tree_sha}',
|
||||
{
|
||||
owner,
|
||||
try {
|
||||
const README = await request('GET /repos/{owner}/{repo}/readme', {
|
||||
repo,
|
||||
tree_sha: lastCommit.commit.tree.sha,
|
||||
recursive: 'true'
|
||||
}
|
||||
)
|
||||
owner
|
||||
})
|
||||
|
||||
console.log(tree.data.tree.filter((t) => t.type === 'blob'))
|
||||
if (README) {
|
||||
readme.value = render(README.data.content)
|
||||
}
|
||||
|
||||
const commits = await request('GET /repos/{owner}/{repo}/commits', {
|
||||
owner,
|
||||
repo
|
||||
})
|
||||
|
||||
const lastCommit = commits.data.shift()
|
||||
|
||||
if (!lastCommit) {
|
||||
return
|
||||
}
|
||||
|
||||
const treeResponse = await request(
|
||||
'GET /repos/{owner}/{repo}/git/trees/{tree_sha}',
|
||||
{
|
||||
owner,
|
||||
repo,
|
||||
tree_sha: lastCommit.commit.tree.sha,
|
||||
recursive: 'true'
|
||||
}
|
||||
)
|
||||
|
||||
if (treeResponse) {
|
||||
tree.value = treeResponse.data.tree.filter((t) => t.type === 'blob')
|
||||
console.log(tree.value)
|
||||
}
|
||||
} catch (error) {
|
||||
notFound.value = true
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
readme
|
||||
readme,
|
||||
notFound,
|
||||
tree
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user