Create post content component
This commit is contained in:
parent
816c133479
commit
b4c2ab260e
2 changed files with 120 additions and 98 deletions
|
@ -57,7 +57,7 @@
|
||||||
@{{ mention.username }}
|
@{{ mention.username }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="post-content" ref="postContentRef" v-html="getContent()"></div>
|
<post-content :post="post"></post-content>
|
||||||
<div class="post-attachment" v-if="post.media_attachments.length > 0">
|
<div class="post-attachment" v-if="post.media_attachments.length > 0">
|
||||||
<template v-for="attachment in post.media_attachments" :key="attachment.id">
|
<template v-for="attachment in post.media_attachments" :key="attachment.id">
|
||||||
<img v-if="attachment.type === 'image'" :src="attachment.url">
|
<img v-if="attachment.type === 'image'" :src="attachment.url">
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
@{{ getActorAddress(post.quote.account) }}
|
@{{ getActorAddress(post.quote.account) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="quote-content" v-html="post.quote.content"></div>
|
<post-content :post="post.quote"></post-content>
|
||||||
</a>
|
</a>
|
||||||
<div class="post-footer">
|
<div class="post-footer">
|
||||||
<router-link
|
<router-link
|
||||||
|
@ -223,7 +223,6 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
/* eslint-disable vue/no-mutating-props */
|
/* eslint-disable vue/no-mutating-props */
|
||||||
import { onMounted } from "vue"
|
|
||||||
import { $, $computed, $ref } from "vue/macros"
|
import { $, $computed, $ref } from "vue/macros"
|
||||||
import { useRouter } from "vue-router"
|
import { useRouter } from "vue-router"
|
||||||
|
|
||||||
|
@ -247,6 +246,7 @@ import {
|
||||||
import { ProfileWrapper } from "@/api/users"
|
import { ProfileWrapper } from "@/api/users"
|
||||||
import Avatar from "@/components/Avatar.vue"
|
import Avatar from "@/components/Avatar.vue"
|
||||||
import CryptoAddress from "@/components/CryptoAddress.vue"
|
import CryptoAddress from "@/components/CryptoAddress.vue"
|
||||||
|
import PostContent from "@/components/PostContent.vue"
|
||||||
import PostEditor from "@/components/PostEditor.vue"
|
import PostEditor from "@/components/PostEditor.vue"
|
||||||
import VisibilityIcon from "@/components/VisibilityIcon.vue"
|
import VisibilityIcon from "@/components/VisibilityIcon.vue"
|
||||||
import { useInstanceInfo } from "@/store/instance"
|
import { useInstanceInfo } from "@/store/instance"
|
||||||
|
@ -254,7 +254,6 @@ import { useCurrentUser } from "@/store/user"
|
||||||
import { CRYPTOCURRENCIES } from "@/utils/cryptocurrencies"
|
import { CRYPTOCURRENCIES } from "@/utils/cryptocurrencies"
|
||||||
import { getWallet } from "@/utils/ethereum"
|
import { getWallet } from "@/utils/ethereum"
|
||||||
import { humanizeDate } from "@/utils/dates"
|
import { humanizeDate } from "@/utils/dates"
|
||||||
import { addGreentext } from "@/utils/greentext"
|
|
||||||
|
|
||||||
interface PaymentOption {
|
interface PaymentOption {
|
||||||
code: string;
|
code: string;
|
||||||
|
@ -281,8 +280,6 @@ const emit = defineEmits<{
|
||||||
(event: "post-deleted"): void,
|
(event: "post-deleted"): void,
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const postContentRef = $ref<HTMLElement | null>(null)
|
|
||||||
|
|
||||||
let commentFormVisible = $ref(false)
|
let commentFormVisible = $ref(false)
|
||||||
let menuVisible = $ref(false)
|
let menuVisible = $ref(false)
|
||||||
let selectedPaymentAddress = $ref<string | null>(null)
|
let selectedPaymentAddress = $ref<string | null>(null)
|
||||||
|
@ -291,55 +288,6 @@ let isWaitingForToken = $ref(false)
|
||||||
const blockchain = $computed(() => instance?.blockchains[0])
|
const blockchain = $computed(() => instance?.blockchains[0])
|
||||||
const author = $computed(() => new ProfileWrapper(props.post.account))
|
const author = $computed(() => new ProfileWrapper(props.post.account))
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (currentUser !== null) {
|
|
||||||
configureInlineLinks()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function configureInlineLinks() {
|
|
||||||
if (postContentRef === null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const mentions = postContentRef.getElementsByClassName("mention")
|
|
||||||
for (const mentionElement of Array.from(mentions)) {
|
|
||||||
const mention = props.post.mentions
|
|
||||||
.find((mention) => mentionElement.getAttribute("href") === mention.url)
|
|
||||||
if (mention) {
|
|
||||||
mentionElement.addEventListener("click", (event: Event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
router.push({ name: "profile", params: { profileId: mention.id } })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const hashtags = postContentRef.getElementsByClassName("hashtag")
|
|
||||||
for (const hashtagElement of Array.from(hashtags)) {
|
|
||||||
const hashtag = props.post.tags
|
|
||||||
.find((tag) => {
|
|
||||||
const innerText = (hashtagElement as HTMLElement).innerText
|
|
||||||
return innerText.toLowerCase() === `#${tag.name}`
|
|
||||||
})
|
|
||||||
if (hashtag) {
|
|
||||||
hashtagElement.addEventListener("click", (event: Event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
router.push({ name: "tag", params: { tagName: hashtag.name } })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const quote = props.post.quote
|
|
||||||
if (quote) {
|
|
||||||
const links = postContentRef.querySelectorAll("a")
|
|
||||||
for (const linkElement of Array.from(links)) {
|
|
||||||
if (quote.uri === linkElement.getAttribute("href")) {
|
|
||||||
linkElement.addEventListener("click", (event: Event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
router.push({ name: "post", params: { postId: quote.id } })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function openProfile(event: Event, profileId: string) {
|
function openProfile(event: Event, profileId: string) {
|
||||||
if (currentUser === null) {
|
if (currentUser === null) {
|
||||||
// Viewing as guest; do not override click handler
|
// Viewing as guest; do not override click handler
|
||||||
|
@ -370,11 +318,6 @@ function getVisibilityDisplay(): string {
|
||||||
return VISIBILITY_MAP[props.post.visibility]
|
return VISIBILITY_MAP[props.post.visibility]
|
||||||
}
|
}
|
||||||
|
|
||||||
function getContent(): string {
|
|
||||||
const content = addGreentext(props.post.content)
|
|
||||||
return content
|
|
||||||
}
|
|
||||||
|
|
||||||
function getReplyMentions(): Mention[] {
|
function getReplyMentions(): Mention[] {
|
||||||
if (props.post.in_reply_to_id === null) {
|
if (props.post.in_reply_to_id === null) {
|
||||||
return []
|
return []
|
||||||
|
@ -664,40 +607,6 @@ async function onMintToken() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content {
|
|
||||||
color: $text-color;
|
|
||||||
line-height: 1.5;
|
|
||||||
padding: $block-inner-padding;
|
|
||||||
word-wrap: break-word;
|
|
||||||
|
|
||||||
:deep(a) {
|
|
||||||
@include block-link;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(pre),
|
|
||||||
:deep(code) {
|
|
||||||
overflow-x: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(ul),
|
|
||||||
:deep(ol) {
|
|
||||||
list-style-position: inside;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.greentext) {
|
|
||||||
color: $greentext-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(blockquote) {
|
|
||||||
color: $greentext-color;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: ">";
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-quote {
|
.post-quote {
|
||||||
border: 1px solid $separator-color;
|
border: 1px solid $separator-color;
|
||||||
border-radius: $block-border-radius;
|
border-radius: $block-border-radius;
|
||||||
|
@ -730,10 +639,6 @@ async function onMintToken() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote-content {
|
|
||||||
padding: $block-inner-padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-attachment {
|
.post-attachment {
|
||||||
padding: 0 $block-inner-padding $block-inner-padding;
|
padding: 0 $block-inner-padding $block-inner-padding;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
117
src/components/PostContent.vue
Normal file
117
src/components/PostContent.vue
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
<template>
|
||||||
|
<div class="post-content" ref="postContentRef" v-html="getContent()"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted } from "vue"
|
||||||
|
import { $, $ref } from "vue/macros"
|
||||||
|
import { useRouter } from "vue-router"
|
||||||
|
|
||||||
|
import { Post } from "@/api/posts"
|
||||||
|
import { useCurrentUser } from "@/store/user"
|
||||||
|
import { addGreentext } from "@/utils/greentext"
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const { currentUser } = $(useCurrentUser())
|
||||||
|
|
||||||
|
/* eslint-disable-next-line no-undef */
|
||||||
|
const props = defineProps<{
|
||||||
|
post: Post,
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const postContentRef = $ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (currentUser !== null) {
|
||||||
|
configureInlineLinks()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function configureInlineLinks() {
|
||||||
|
if (postContentRef === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const mentions = postContentRef.getElementsByClassName("mention")
|
||||||
|
for (const mentionElement of Array.from(mentions)) {
|
||||||
|
const mention = props.post.mentions
|
||||||
|
.find((mention) => mentionElement.getAttribute("href") === mention.url)
|
||||||
|
if (mention) {
|
||||||
|
mentionElement.addEventListener("click", (event: Event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
router.push({ name: "profile", params: { profileId: mention.id } })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const hashtags = postContentRef.getElementsByClassName("hashtag")
|
||||||
|
for (const hashtagElement of Array.from(hashtags)) {
|
||||||
|
const hashtag = props.post.tags
|
||||||
|
.find((tag) => {
|
||||||
|
const innerText = (hashtagElement as HTMLElement).innerText
|
||||||
|
return innerText.toLowerCase() === `#${tag.name}`
|
||||||
|
})
|
||||||
|
if (hashtag) {
|
||||||
|
hashtagElement.addEventListener("click", (event: Event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
router.push({ name: "tag", params: { tagName: hashtag.name } })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const quote = props.post.quote
|
||||||
|
if (quote) {
|
||||||
|
const links = postContentRef.querySelectorAll("a")
|
||||||
|
for (const linkElement of Array.from(links)) {
|
||||||
|
if (quote.uri === linkElement.getAttribute("href")) {
|
||||||
|
linkElement.addEventListener("click", (event: Event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
router.push({ name: "post", params: { postId: quote.id } })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContent(): string {
|
||||||
|
const content = addGreentext(props.post.content)
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "../styles/layout";
|
||||||
|
@import "../styles/theme";
|
||||||
|
@import "../styles/mixins";
|
||||||
|
|
||||||
|
.post-content {
|
||||||
|
color: $text-color;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: $block-inner-padding;
|
||||||
|
word-wrap: break-word;
|
||||||
|
|
||||||
|
:deep(a) {
|
||||||
|
@include block-link;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(pre),
|
||||||
|
:deep(code) {
|
||||||
|
overflow-x: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(ul),
|
||||||
|
:deep(ol) {
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.greentext) {
|
||||||
|
color: $greentext-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(blockquote) {
|
||||||
|
color: $greentext-color;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: ">";
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in a new issue