compute note backlinks
This commit is contained in:
@@ -35,6 +35,7 @@ import { useNoteOverlay } from '@/hooks/useNoteOverlay.hook'
|
|||||||
import { useFocus } from '@/hooks/useFocus.hook'
|
import { useFocus } from '@/hooks/useFocus.hook'
|
||||||
import { useImages } from '@/hooks/useImages.hook'
|
import { useImages } from '@/hooks/useImages.hook'
|
||||||
import LinkedNotes from '@/components/LinkedNotes.vue'
|
import LinkedNotes from '@/components/LinkedNotes.vue'
|
||||||
|
import { filenameToNoteTitle } from '@/utils/noteTitle'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'StackedNote',
|
name: 'StackedNote',
|
||||||
@@ -56,7 +57,7 @@ export default defineComponent({
|
|||||||
const titleClassName = computed(() => `title-${className.value}`)
|
const titleClassName = computed(() => `title-${className.value}`)
|
||||||
|
|
||||||
const { displayNoteOverlay } = useNoteOverlay(className.value, props.index)
|
const { displayNoteOverlay } = useNoteOverlay(className.value, props.index)
|
||||||
const displayedTitle = computed(() => props.title.replaceAll('/', ' / '))
|
const displayedTitle = computed(() => filenameToNoteTitle(props.title))
|
||||||
|
|
||||||
watch(content, () => {
|
watch(content, () => {
|
||||||
if (content.value) {
|
if (content.value) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export enum DataType {
|
export enum DataType {
|
||||||
GithubAccessToken = 'GithubAccessToken',
|
GithubAccessToken = 'GithubAccessToken',
|
||||||
FavoriteRepo = 'FavoriteRepo',
|
FavoriteRepo = 'FavoriteRepo',
|
||||||
Note = 'Note'
|
Note = 'Note',
|
||||||
|
BacklinkNote = 'BacklinkNote'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
|
|
||||||
import { watch } from 'vue'
|
|
||||||
|
|
||||||
export const useBackLinks = () => {
|
|
||||||
const store = useUserRepoStore()
|
|
||||||
|
|
||||||
watch(store, () => {
|
|
||||||
if (!store.userSettings?.backlink) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("let's go backlinks!")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
94
src/hooks/useComputeBacklinks.hook.ts
Normal file
94
src/hooks/useComputeBacklinks.hook.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import { data } from '@/data/data'
|
||||||
|
import { DataType } from '@/data/DataType.enum'
|
||||||
|
import { useFile } from '@/hooks/useFile.hook'
|
||||||
|
import { Backlink } from '@/modules/note/models/Backlink'
|
||||||
|
import { BacklinkNote } from '@/modules/note/models/BacklinkNote'
|
||||||
|
import { resolvePath } from '@/modules/repo/services/resolvePath'
|
||||||
|
import { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
|
||||||
|
import { isExternalLink } from '@/utils/link'
|
||||||
|
import { filenameToNoteTitle } from '@/utils/noteTitle'
|
||||||
|
import { watch } from 'vue'
|
||||||
|
|
||||||
|
const isMarkdown = (filename?: string) => filename?.endsWith('.md') ?? false
|
||||||
|
|
||||||
|
export const useComputeBacklinks = () => {
|
||||||
|
const store = useUserRepoStore()
|
||||||
|
|
||||||
|
watch(store, async () => {
|
||||||
|
if (!store.userSettings?.backlink) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const backlinks: Map<string, Backlink[]> = new Map()
|
||||||
|
|
||||||
|
for (const file of store.files) {
|
||||||
|
if (!isMarkdown(file.path) || !file.sha) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const fileBacklinkId = data.generateId(DataType.BacklinkNote, file.sha)
|
||||||
|
const fileBacklink = await data.get<DataType.BacklinkNote, BacklinkNote>(
|
||||||
|
fileBacklinkId
|
||||||
|
)
|
||||||
|
if (fileBacklink) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
backlinks.set(file.sha, [])
|
||||||
|
const { getContent } = useFile(file.sha, false)
|
||||||
|
const note = await getContent()
|
||||||
|
|
||||||
|
if (!note) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new DOMParser()
|
||||||
|
const htmlDoc = parser.parseFromString(note, 'text/html')
|
||||||
|
|
||||||
|
const links = htmlDoc.querySelectorAll('a')
|
||||||
|
|
||||||
|
for (const link of links) {
|
||||||
|
const href = link.getAttribute('href') ?? ''
|
||||||
|
|
||||||
|
if (isExternalLink(href) || !isMarkdown(href)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = resolvePath(file.path ?? '', href)
|
||||||
|
const backlinkFile = store.files.find((file) => file.path === path)
|
||||||
|
|
||||||
|
if (!backlinkFile?.sha || !backlinkFile?.path) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousBacklinks = backlinks.get(backlinkFile.sha) ?? []
|
||||||
|
backlinks.set(backlinkFile.sha, [
|
||||||
|
...previousBacklinks,
|
||||||
|
{
|
||||||
|
sha: file.sha,
|
||||||
|
title: filenameToNoteTitle(file.path ?? '')
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [sha, fileBacklinks] of backlinks) {
|
||||||
|
const fileBacklinkId = data.generateId(DataType.BacklinkNote, sha)
|
||||||
|
const backlinkNote: BacklinkNote = {
|
||||||
|
_id: fileBacklinkId,
|
||||||
|
$type: DataType.BacklinkNote,
|
||||||
|
sha: sha,
|
||||||
|
links: fileBacklinks
|
||||||
|
}
|
||||||
|
|
||||||
|
await data.add(backlinkNote)
|
||||||
|
}
|
||||||
|
|
||||||
|
const backlinksInDb = await data.getAll<
|
||||||
|
DataType.BacklinkNote,
|
||||||
|
BacklinkNote
|
||||||
|
>({
|
||||||
|
prefix: DataType.BacklinkNote
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(backlinksInDb.filter((b) => b.links.length))
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ export const useFile = (sha: string, retrieveContent = true) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
content.value = render(fileContent)
|
content.value = render(fileContent)
|
||||||
|
return content.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCachedFileContent = async (): Promise<string | null> => {
|
const getCachedFileContent = async (): Promise<string | null> => {
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { noteEventBus } from '@/bus/noteEventBus'
|
import { noteEventBus } from '@/bus/noteEventBus'
|
||||||
import { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
|
import { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
|
||||||
|
import { isExternalLink } from '@/utils/link'
|
||||||
import { onUnmounted } from '@vue/runtime-core'
|
import { onUnmounted } from '@vue/runtime-core'
|
||||||
|
|
||||||
const LINKS = ['http://', 'https://']
|
|
||||||
|
|
||||||
export const useLinks = (className: string, sha?: string) => {
|
export const useLinks = (className: string, sha?: string) => {
|
||||||
const store = useUserRepoStore()
|
const store = useUserRepoStore()
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ export const useLinks = (className: string, sha?: string) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LINKS.some((link) => href.startsWith(link))) {
|
if (isExternalLink(href)) {
|
||||||
window.open(href, '_blank')
|
window.open(href, '_blank')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -50,9 +49,7 @@ export const useLinks = (className: string, sha?: string) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const isExternalLink = LINKS.some((link) => href.startsWith(link))
|
if (isExternalLink(href)) {
|
||||||
|
|
||||||
if (isExternalLink) {
|
|
||||||
element.classList.add('external-link')
|
element.classList.add('external-link')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
4
src/modules/note/models/Backlink.ts
Normal file
4
src/modules/note/models/Backlink.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface Backlink {
|
||||||
|
sha: string
|
||||||
|
title: string
|
||||||
|
}
|
||||||
8
src/modules/note/models/BacklinkNote.ts
Normal file
8
src/modules/note/models/BacklinkNote.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { DataType } from '@/data/DataType.enum'
|
||||||
|
import { Model } from '@/data/models/Model'
|
||||||
|
import { Backlink } from '@/modules/note/models/Backlink'
|
||||||
|
|
||||||
|
export interface BacklinkNote extends Model<DataType.Backlink> {
|
||||||
|
sha: string
|
||||||
|
links: Backlink[]
|
||||||
|
}
|
||||||
4
src/utils/link.ts
Normal file
4
src/utils/link.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const LINKS = ['http://', 'https://']
|
||||||
|
|
||||||
|
export const isExternalLink = (href: string) =>
|
||||||
|
LINKS.some((link) => href.startsWith(link))
|
||||||
2
src/utils/noteTitle.ts
Normal file
2
src/utils/noteTitle.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export const filenameToNoteTitle = (title: string) =>
|
||||||
|
title.replaceAll('/', ' / ')
|
||||||
@@ -12,7 +12,7 @@ import { defineComponent, defineAsyncComponent, computed } from 'vue'
|
|||||||
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
|
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
|
||||||
import NewVersion from '@/components/NewVersion.vue'
|
import NewVersion from '@/components/NewVersion.vue'
|
||||||
import Authorize from '@/components/Authorize.vue'
|
import Authorize from '@/components/Authorize.vue'
|
||||||
import { useBackLinks } from '@/hooks/useBacklinks.hook'
|
import { useComputeBacklinks } from '@/hooks/useComputeBacklinks.hook'
|
||||||
|
|
||||||
const FluxNote = defineAsyncComponent(() => import('@/components/FluxNote.vue'))
|
const FluxNote = defineAsyncComponent(() => import('@/components/FluxNote.vue'))
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const { resetStackedNotes } = useQueryStackedNotes()
|
const { resetStackedNotes } = useQueryStackedNotes()
|
||||||
useBackLinks()
|
useComputeBacklinks()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
resetStackedNotes,
|
resetStackedNotes,
|
||||||
|
|||||||
Reference in New Issue
Block a user