✨ (twitter) implement tweets in notes
This commit is contained in:
@@ -1,69 +1,23 @@
|
||||
<template>
|
||||
<main class="flux-note repo-note content note-container">
|
||||
<div class="note readme">
|
||||
<header-note v-if="withHeader" class="header" :user="user" :repo="repo" />
|
||||
<div class="repo-title-breadcrumb">
|
||||
<a @click.prevent="focus">{{ repo }}</a>
|
||||
</div>
|
||||
<div class="repo-title">
|
||||
<h1 class="title is-1">
|
||||
[<router-link
|
||||
:to="{ name: 'Home', params: { user, repo } }"
|
||||
@click="resetStackedNotes"
|
||||
>{{ repo }}</router-link
|
||||
>]
|
||||
</h1>
|
||||
<h4 class="subtitle is-4">
|
||||
<em>{{ user }}</em>
|
||||
</h4>
|
||||
</div>
|
||||
<slot />
|
||||
<div v-if="isLoading" class="loading">
|
||||
<img
|
||||
class="is-loading"
|
||||
src="@/assets/icons/loading.svg"
|
||||
alt="loading..."
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="!hasContent">No content here 📝</div>
|
||||
<p
|
||||
v-else-if="withContent"
|
||||
class="note-display"
|
||||
v-html="renderedContent"
|
||||
></p>
|
||||
</div>
|
||||
<stacked-note
|
||||
v-for="(stackedNote, index) in stackedNotes"
|
||||
:key="stackedNote"
|
||||
class="note"
|
||||
:index="index"
|
||||
:sha="stackedNote"
|
||||
:user="user"
|
||||
:repo="repo"
|
||||
:title="titles[stackedNote ?? '']"
|
||||
/>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
|
||||
import {
|
||||
defineComponent,
|
||||
defineAsyncComponent,
|
||||
computed,
|
||||
watch,
|
||||
nextTick,
|
||||
toRefs,
|
||||
onUnmounted,
|
||||
onMounted
|
||||
} from 'vue'
|
||||
import HeaderNote from '@/components/HeaderNote.vue'
|
||||
import { useNote } from '@/hooks/useNote.hook'
|
||||
import { useMarkdown } from '@/hooks/useMarkdown.hook'
|
||||
import { useLinks } from '@/hooks/useLinks.hook'
|
||||
import { useMarkdown } from '@/hooks/useMarkdown.hook'
|
||||
import { useNote } from '@/hooks/useNote.hook'
|
||||
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 { useVisitRepo } from '@/modules/history/hooks/useVisitRepo.hook'
|
||||
|
||||
import {
|
||||
computed,
|
||||
defineAsyncComponent,
|
||||
defineComponent,
|
||||
nextTick,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
toRefs,
|
||||
watch
|
||||
} from 'vue'
|
||||
|
||||
const StackedNote = defineAsyncComponent(
|
||||
() => import('@/components/StackedNote.vue')
|
||||
@@ -144,6 +98,53 @@ export default defineComponent({
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="flux-note repo-note content note-container">
|
||||
<div class="note readme">
|
||||
<header-note v-if="withHeader" class="header" :user="user" :repo="repo" />
|
||||
<div class="repo-title-breadcrumb">
|
||||
<a @click.prevent="focus">{{ repo }}</a>
|
||||
</div>
|
||||
<div class="repo-title">
|
||||
<h1 class="title is-1">
|
||||
[<router-link
|
||||
:to="{ name: 'Home', params: { user, repo } }"
|
||||
@click="resetStackedNotes"
|
||||
>{{ repo }}</router-link
|
||||
>]
|
||||
</h1>
|
||||
<h4 class="subtitle is-4">
|
||||
<em>{{ user }}</em>
|
||||
</h4>
|
||||
</div>
|
||||
<slot />
|
||||
<div v-if="isLoading" class="loading">
|
||||
<img
|
||||
class="is-loading"
|
||||
src="@/assets/icons/loading.svg"
|
||||
alt="loading..."
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="!hasContent">No content here 📝</div>
|
||||
<p
|
||||
v-else-if="withContent"
|
||||
class="note-display"
|
||||
v-html="renderedContent"
|
||||
></p>
|
||||
</div>
|
||||
<stacked-note
|
||||
v-for="(stackedNote, index) in stackedNotes"
|
||||
:key="stackedNote"
|
||||
class="note"
|
||||
:index="index"
|
||||
:sha="stackedNote"
|
||||
:user="user"
|
||||
:repo="repo"
|
||||
:title="titles[stackedNote ?? '']"
|
||||
/>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
$header-height: 40px;
|
||||
|
||||
|
||||
@@ -26,15 +26,16 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, nextTick, watch } from 'vue'
|
||||
import LinkedNotes from '@/components/LinkedNotes.vue'
|
||||
import { useFile } from '@/hooks/useFile.hook'
|
||||
import { useImages } from '@/hooks/useImages.hook'
|
||||
import { useLinks } from '@/hooks/useLinks.hook'
|
||||
import { useNoteOverlay } from '@/hooks/useNoteOverlay.hook'
|
||||
import { useImages } from '@/hooks/useImages.hook'
|
||||
import LinkedNotes from '@/components/LinkedNotes.vue'
|
||||
import { filenameToNoteTitle } from '@/utils/noteTitle'
|
||||
import { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
|
||||
import { useTitleNotes } from '@/hooks/useTitleNotes.hook'
|
||||
import { filenameToNoteTitle } from '@/utils/noteTitle'
|
||||
import { generateTweets } from '@/utils/twitter'
|
||||
import { computed, defineComponent, nextTick, watch } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'StackedNote',
|
||||
@@ -64,6 +65,7 @@ export default defineComponent({
|
||||
nextTick(() => {
|
||||
listenToClick()
|
||||
useImages(props.sha)
|
||||
generateTweets()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { decodeBase64ToUTF8 } from '@/utils/decodeBase64ToUTF8'
|
||||
import { html5Media } from '@/utils/markdown/markdown-html5-media'
|
||||
import { twitterPlugin } from '@/utils/markdown/markdown-it-twitter'
|
||||
import markdownItClass from '@toycode/markdown-it-class'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import blockEmbedPlugin from 'markdown-it-block-embed'
|
||||
@@ -26,6 +27,7 @@ const md = new MarkdownIt({
|
||||
width: '100%'
|
||||
}
|
||||
})
|
||||
.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">
|
||||
|
||||
1
src/shims-vue.d.ts
vendored
1
src/shims-vue.d.ts
vendored
@@ -8,3 +8,4 @@ declare module '@toycode/markdown-it-class'
|
||||
declare module 'markdown-it-block-embed'
|
||||
declare module 'markdown-it-checkbox'
|
||||
declare module 'markdown-it-footnote'
|
||||
declare module 'markdown-it-regexp'
|
||||
|
||||
51
src/utils/markdown/markdown-it-regexp.ts
Normal file
51
src/utils/markdown/markdown-it-regexp.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import MarkdownIt, { PluginSimple } from 'markdown-it'
|
||||
|
||||
let counter = 0
|
||||
|
||||
export const markdownItPlugin = (
|
||||
regex: RegExp,
|
||||
replacer: (matches: RegExpExecArray[]) => string
|
||||
): PluginSimple => {
|
||||
const id = 'regexp-' + counter
|
||||
counter++
|
||||
const flags =
|
||||
(regex.global ? 'g' : '') +
|
||||
(regex.multiline ? 'm' : '') +
|
||||
(regex.ignoreCase ? 'i' : '')
|
||||
|
||||
const regexp = RegExp('^' + regex.source, flags)
|
||||
|
||||
const parse = (state: any, silent: boolean) => {
|
||||
const match = regexp.exec(state.src.slice(state.pos))
|
||||
|
||||
if (!match) {
|
||||
return false
|
||||
}
|
||||
|
||||
// valid match found, now we need to advance cursor
|
||||
state.pos += match[0].length
|
||||
|
||||
// don't insert any tokens in silent mode
|
||||
if (silent) {
|
||||
return true
|
||||
}
|
||||
|
||||
const token = state.push(id, '', 0)
|
||||
token.meta = { match }
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const render = (
|
||||
tokens: { meta: { match: RegExpExecArray[] } }[],
|
||||
id: number
|
||||
) => {
|
||||
return replacer(tokens[id].meta.match)
|
||||
}
|
||||
|
||||
return (md: MarkdownIt) => {
|
||||
md.inline.ruler.push(id, parse)
|
||||
|
||||
md.renderer.rules[id] = render
|
||||
}
|
||||
}
|
||||
10
src/utils/markdown/markdown-it-twitter.ts
Normal file
10
src/utils/markdown/markdown-it-twitter.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { markdownItPlugin } from '@/utils/markdown/markdown-it-regexp'
|
||||
|
||||
export const twitterPlugin = markdownItPlugin(
|
||||
/@\[tweet]\((.*?)\)/g,
|
||||
(matches: RegExpExecArray[]) => {
|
||||
const [_, tweetId] = matches
|
||||
|
||||
return `<span id="tweet-${tweetId}" data-tweet-id="${tweetId}" class="markdown-tweet"></span>`
|
||||
}
|
||||
)
|
||||
@@ -41,3 +41,11 @@ export const createTweet = (
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const generateTweets = () => {
|
||||
const elements = document.querySelectorAll('.markdown-tweet')
|
||||
|
||||
elements.forEach((element) => {
|
||||
createTweet(element.dataset.tweetId)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user