✨ (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">
|
<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 HeaderNote from '@/components/HeaderNote.vue'
|
||||||
import { useNote } from '@/hooks/useNote.hook'
|
|
||||||
import { useMarkdown } from '@/hooks/useMarkdown.hook'
|
|
||||||
import { useLinks } from '@/hooks/useLinks.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 { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
|
||||||
import { useUserSettings } from '@/modules/user/hooks/useUserSettings.hook'
|
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(
|
const StackedNote = defineAsyncComponent(
|
||||||
() => import('@/components/StackedNote.vue')
|
() => import('@/components/StackedNote.vue')
|
||||||
@@ -144,6 +98,53 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
</script>
|
</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">
|
<style lang="scss">
|
||||||
$header-height: 40px;
|
$header-height: 40px;
|
||||||
|
|
||||||
|
|||||||
@@ -26,15 +26,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, nextTick, watch } from 'vue'
|
import LinkedNotes from '@/components/LinkedNotes.vue'
|
||||||
import { useFile } from '@/hooks/useFile.hook'
|
import { useFile } from '@/hooks/useFile.hook'
|
||||||
|
import { useImages } from '@/hooks/useImages.hook'
|
||||||
import { useLinks } from '@/hooks/useLinks.hook'
|
import { useLinks } from '@/hooks/useLinks.hook'
|
||||||
import { useNoteOverlay } from '@/hooks/useNoteOverlay.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 { useQueryStackedNotes } from '@/hooks/useQueryStackedNotes.hook'
|
||||||
import { useTitleNotes } from '@/hooks/useTitleNotes.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({
|
export default defineComponent({
|
||||||
name: 'StackedNote',
|
name: 'StackedNote',
|
||||||
@@ -64,6 +65,7 @@ export default defineComponent({
|
|||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
listenToClick()
|
listenToClick()
|
||||||
useImages(props.sha)
|
useImages(props.sha)
|
||||||
|
generateTweets()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { decodeBase64ToUTF8 } from '@/utils/decodeBase64ToUTF8'
|
import { decodeBase64ToUTF8 } from '@/utils/decodeBase64ToUTF8'
|
||||||
import { html5Media } from '@/utils/markdown/markdown-html5-media'
|
import { html5Media } from '@/utils/markdown/markdown-html5-media'
|
||||||
|
import { twitterPlugin } from '@/utils/markdown/markdown-it-twitter'
|
||||||
import markdownItClass from '@toycode/markdown-it-class'
|
import markdownItClass from '@toycode/markdown-it-class'
|
||||||
import MarkdownIt from 'markdown-it'
|
import MarkdownIt from 'markdown-it'
|
||||||
import blockEmbedPlugin from 'markdown-it-block-embed'
|
import blockEmbedPlugin from 'markdown-it-block-embed'
|
||||||
@@ -26,6 +27,7 @@ const md = new MarkdownIt({
|
|||||||
width: '100%'
|
width: '100%'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.use(twitterPlugin)
|
||||||
.use(markdownItCheckbox)
|
.use(markdownItCheckbox)
|
||||||
.use(markdownItSvgCodeCopy, {
|
.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">
|
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-block-embed'
|
||||||
declare module 'markdown-it-checkbox'
|
declare module 'markdown-it-checkbox'
|
||||||
declare module 'markdown-it-footnote'
|
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