Convert Post component into script-setup format
This commit is contained in:
parent
2ac54a2ff9
commit
ac4b8e275a
11 changed files with 321 additions and 393 deletions
|
@ -7,19 +7,14 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue } from "vue-class-component"
|
||||
import { Prop } from "vue-property-decorator"
|
||||
|
||||
export default class CryptoAddress extends Vue {
|
||||
|
||||
@Prop()
|
||||
address!: string
|
||||
|
||||
copyAddress() {
|
||||
navigator.clipboard.writeText(this.address)
|
||||
}
|
||||
<script setup lang="ts">
|
||||
/* eslint-disable-next-line no-undef */
|
||||
const props = defineProps<{
|
||||
address: string,
|
||||
}>()
|
||||
|
||||
function copyAddress() {
|
||||
navigator.clipboard.writeText(props.address)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -5,14 +5,6 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue } from "vue-class-component"
|
||||
|
||||
export default class Loader extends Vue {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../styles/theme";
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
<router-link
|
||||
class="display-name"
|
||||
:to="{ name: 'profile', params: { profileId: post.account.id }}"
|
||||
:title="authorName"
|
||||
:title="getAuthorName()"
|
||||
>
|
||||
{{ authorName }}
|
||||
{{ getAuthorName() }}
|
||||
</router-link>
|
||||
<div class="actor-address" :title="'@' + getActorAddress(post.account)">
|
||||
@{{ getActorAddress(post.account) }}
|
||||
|
@ -26,7 +26,7 @@
|
|||
</a>
|
||||
<span
|
||||
class="icon icon-small"
|
||||
:title="visibilityDisplay"
|
||||
:title="getVisibilityDisplay()"
|
||||
>
|
||||
<visibility-icon :visibility="post.visibility"></visibility-icon>
|
||||
</span>
|
||||
|
@ -39,10 +39,10 @@
|
|||
{{ formatDate(post.created_at) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="post-subheader" v-if="replyingTo.length > 0">
|
||||
<div class="post-subheader" v-if="getReplyMentions().length > 0">
|
||||
<span>replying to</span>
|
||||
<router-link
|
||||
v-for="mention in replyingTo"
|
||||
v-for="mention in getReplyMentions()"
|
||||
:to="{ name: 'profile', params: { profileId: mention.id }}"
|
||||
:title="getActorAddress(mention)"
|
||||
:key="mention.id"
|
||||
|
@ -50,7 +50,7 @@
|
|||
@{{ mention.username }}
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="post-content" ref="postContent" v-html="content"></div>
|
||||
<div class="post-content" ref="postContentRef" v-html="getContent()"></div>
|
||||
<div class="post-attachment" v-if="post.media_attachments.length > 0">
|
||||
<template v-for="attachment in post.media_attachments" :key="attachment.id">
|
||||
<img v-if="attachment.type === 'image'" :src="attachment.url">
|
||||
|
@ -110,17 +110,17 @@
|
|||
<span>{{ post.favourites_count }}</span>
|
||||
</span>
|
||||
<a
|
||||
v-if="ipfsUrl"
|
||||
v-if="getIpfsUrl()"
|
||||
class="icon"
|
||||
title="Saved to IPFS"
|
||||
:href="ipfsUrl"
|
||||
:href="getIpfsUrl() || ''"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<img :src="require('@/assets/ipfs.svg')">
|
||||
</a>
|
||||
<router-link
|
||||
v-if="isTokenized"
|
||||
v-if="isTokenized()"
|
||||
class="icon tokenized"
|
||||
title="View token"
|
||||
:to="{ name: 'post-overlay', params: { postId: post.id }}"
|
||||
|
@ -157,7 +157,7 @@
|
|||
<button
|
||||
class="icon"
|
||||
title="Mint NFT"
|
||||
@click="hideMenu(); mintToken()"
|
||||
@click="hideMenu(); onMintToken()"
|
||||
>
|
||||
<img :src="require('@/assets/forkawesome/diamond.svg')">
|
||||
<span>Mint NFT</span>
|
||||
|
@ -167,7 +167,7 @@
|
|||
<button
|
||||
class="icon"
|
||||
title="Delete post"
|
||||
@click="hideMenu(); deletePost()"
|
||||
@click="hideMenu(); onDeletePost()"
|
||||
>
|
||||
<img :src="require('@/assets/feather/trash.svg')">
|
||||
<span>Delete post</span>
|
||||
|
@ -200,9 +200,11 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue, setup } from "vue-class-component"
|
||||
import { Prop } from "vue-property-decorator"
|
||||
<script setup lang="ts">
|
||||
/* eslint-disable vue/no-mutating-props */
|
||||
import { onMounted } from "vue"
|
||||
import { $, $ref } from "vue/macros"
|
||||
import { useRouter } from "vue-router"
|
||||
|
||||
import {
|
||||
makePermanent,
|
||||
|
@ -221,7 +223,6 @@ import {
|
|||
createRepost,
|
||||
deleteRepost,
|
||||
} from "@/api/posts"
|
||||
import { Profile } from "@/api/users"
|
||||
import Avatar from "@/components/Avatar.vue"
|
||||
import CryptoAddress from "@/components/CryptoAddress.vue"
|
||||
import PostEditor from "@/components/PostEditor.vue"
|
||||
|
@ -239,300 +240,283 @@ interface PaymentOption {
|
|||
address: string;
|
||||
}
|
||||
|
||||
@Options({
|
||||
components: {
|
||||
Avatar,
|
||||
CryptoAddress,
|
||||
PostEditor,
|
||||
VisibilityIcon,
|
||||
},
|
||||
const router = useRouter()
|
||||
const { currentUser, ensureAuthToken } = $(useCurrentUser())
|
||||
const { instance, getActorAddress } = $(useInstanceInfo())
|
||||
|
||||
/* eslint-disable-next-line no-undef */
|
||||
const props = defineProps<{
|
||||
post: Post,
|
||||
highlighted: boolean,
|
||||
inThread: boolean,
|
||||
}>()
|
||||
|
||||
/* eslint-disable-next-line no-undef, func-call-spacing */
|
||||
const emit = defineEmits<{
|
||||
(event: "highlight", postId: string | null): void,
|
||||
(event: "navigate-to", postId: string): void,
|
||||
(event: "comment-created", post: Post): void,
|
||||
(event: "post-deleted"): void,
|
||||
}>()
|
||||
|
||||
const postContentRef = $ref<HTMLElement | null>(null)
|
||||
|
||||
let commentFormVisible = $ref(false)
|
||||
let menuVisible = $ref(false)
|
||||
let selectedPaymentAddress = $ref<string | null>(null)
|
||||
let isWaitingForToken = $ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
if (postContentRef === null) {
|
||||
return
|
||||
}
|
||||
const mentions = postContentRef.getElementsByClassName("mention")
|
||||
for (const mentionElement of Array.from(mentions)) {
|
||||
mentionElement.addEventListener("click", (event: Event) => {
|
||||
event.preventDefault()
|
||||
const mention = props.post.mentions
|
||||
.find((mention) => mention.url === mentionElement.getAttribute("href"))
|
||||
if (mention) {
|
||||
router.push({ name: "profile", params: { profileId: mention.id } })
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
export default class PostComponent extends Vue {
|
||||
|
||||
@Prop()
|
||||
post!: Post
|
||||
function getAuthorName(): string {
|
||||
return props.post.account.display_name || props.post.account.username
|
||||
}
|
||||
|
||||
@Prop()
|
||||
highlighted = false
|
||||
function highlight(postId: string | null) {
|
||||
emit("highlight", postId)
|
||||
}
|
||||
|
||||
@Prop()
|
||||
inThread = false
|
||||
function navigateTo(postId: string) {
|
||||
if (props.inThread) {
|
||||
emit("navigate-to", postId)
|
||||
} else {
|
||||
router.push({ name: "post", params: { postId: postId } })
|
||||
}
|
||||
}
|
||||
|
||||
function getVisibilityDisplay(): string {
|
||||
return VISIBILITY_MAP[props.post.visibility]
|
||||
}
|
||||
|
||||
function getContent(): string {
|
||||
const content = addGreentext(props.post.content)
|
||||
return content
|
||||
}
|
||||
|
||||
function getReplyMentions(): Mention[] {
|
||||
if (props.post.in_reply_to_id === null) {
|
||||
return []
|
||||
}
|
||||
return props.post.mentions
|
||||
}
|
||||
|
||||
function canReply(): boolean {
|
||||
return currentUser !== null
|
||||
}
|
||||
|
||||
function onCommentCreated(post: Post) {
|
||||
commentFormVisible = false
|
||||
menuVisible = false
|
||||
emit("comment-created", post)
|
||||
}
|
||||
|
||||
private store = setup(() => {
|
||||
const { currentUser, ensureAuthToken } = useCurrentUser()
|
||||
const { instance, getActorAddress } = useInstanceInfo()
|
||||
return { currentUser, ensureAuthToken, instance, getActorAddress }
|
||||
})
|
||||
function canRepost(): boolean {
|
||||
return currentUser !== null && props.post.visibility === "public"
|
||||
}
|
||||
|
||||
$refs!: { postContent: HTMLElement }
|
||||
|
||||
mounted() {
|
||||
const mentions = this.$refs.postContent.getElementsByClassName("mention")
|
||||
for (const mentionElement of Array.from(mentions)) {
|
||||
mentionElement.addEventListener("click", (event: Event) => {
|
||||
event.preventDefault()
|
||||
const mention = this.post.mentions
|
||||
.find((mention) => mention.url === mentionElement.getAttribute("href"))
|
||||
if (mention) {
|
||||
this.$router.push({ name: "profile", params: { profileId: mention.id } })
|
||||
}
|
||||
})
|
||||
}
|
||||
async function toggleRepost() {
|
||||
if (!currentUser) {
|
||||
return
|
||||
}
|
||||
|
||||
get authorName(): string {
|
||||
return this.post.account.display_name || this.post.account.username
|
||||
}
|
||||
|
||||
getActorAddress(profile: Profile | Mention): string {
|
||||
return this.store.getActorAddress(profile)
|
||||
}
|
||||
|
||||
highlight(postId: string | null) {
|
||||
this.$emit("highlight", postId)
|
||||
}
|
||||
|
||||
navigateTo(postId: string) {
|
||||
if (this.inThread) {
|
||||
this.$emit("navigate-to", postId)
|
||||
const authToken = ensureAuthToken()
|
||||
let updatedPost
|
||||
try {
|
||||
if (props.post.reblogged) {
|
||||
updatedPost = await deleteRepost(authToken, props.post.id)
|
||||
} else {
|
||||
this.$router.push({ name: "post", params: { postId: postId } })
|
||||
updatedPost = await createRepost(authToken, props.post.id)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return
|
||||
}
|
||||
props.post.reblogs_count = updatedPost.reblogs_count
|
||||
props.post.reblogged = updatedPost.reblogged
|
||||
}
|
||||
|
||||
get visibilityDisplay(): string {
|
||||
return VISIBILITY_MAP[this.post.visibility]
|
||||
function canLike(): boolean {
|
||||
return currentUser !== null && props.post.visibility === "public"
|
||||
}
|
||||
|
||||
async function toggleLike() {
|
||||
if (!currentUser) {
|
||||
return
|
||||
}
|
||||
|
||||
formatDate(isoDate: string): string {
|
||||
return formatDate(isoDate)
|
||||
}
|
||||
|
||||
get content(): string {
|
||||
const content = addGreentext(this.post.content)
|
||||
return content
|
||||
}
|
||||
|
||||
get replyingTo(): Mention[] {
|
||||
if (this.post.in_reply_to_id === null) {
|
||||
return []
|
||||
const authToken = ensureAuthToken()
|
||||
let updatedPost
|
||||
try {
|
||||
if (props.post.favourited) {
|
||||
updatedPost = await unfavourite(authToken, props.post.id)
|
||||
} else {
|
||||
updatedPost = await favourite(authToken, props.post.id)
|
||||
}
|
||||
return this.post.mentions
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return
|
||||
}
|
||||
props.post.favourites_count = updatedPost.favourites_count
|
||||
props.post.favourited = updatedPost.favourited
|
||||
}
|
||||
|
||||
canReply(): boolean {
|
||||
return this.store.currentUser !== null
|
||||
function toggleMenu() {
|
||||
menuVisible = !menuVisible
|
||||
}
|
||||
|
||||
function hideMenu() {
|
||||
menuVisible = false
|
||||
}
|
||||
|
||||
function getIpfsUrl(): string | null {
|
||||
const gatewayUrl = instance?.ipfs_gateway_url
|
||||
if (
|
||||
!gatewayUrl ||
|
||||
props.post.ipfs_cid === null ||
|
||||
isTokenized() ||
|
||||
isWaitingForToken
|
||||
) {
|
||||
return null
|
||||
}
|
||||
return `${gatewayUrl}/ipfs/${props.post.ipfs_cid}`
|
||||
}
|
||||
|
||||
onCommentCreated(post: Post) {
|
||||
this.commentFormVisible = false
|
||||
this.$emit("comment-created", post)
|
||||
function canSaveToIpfs(): boolean {
|
||||
return (
|
||||
Boolean(instance?.ipfs_gateway_url) &&
|
||||
props.post.account.id === currentUser?.id &&
|
||||
props.post.visibility === "public" &&
|
||||
props.post.ipfs_cid === null &&
|
||||
!isWaitingForToken
|
||||
)
|
||||
}
|
||||
|
||||
async function saveToIpfs() {
|
||||
if (!currentUser || !instance || !instance.ipfs_gateway_url) {
|
||||
return
|
||||
}
|
||||
const authToken = ensureAuthToken()
|
||||
const { ipfs_cid } = await makePermanent(authToken, props.post.id)
|
||||
props.post.ipfs_cid = ipfs_cid
|
||||
}
|
||||
|
||||
canRepost(): boolean {
|
||||
return this.store.currentUser !== null && this.post.visibility === "public"
|
||||
function canDeletePost(): boolean {
|
||||
return props.post.account.id === currentUser?.id
|
||||
}
|
||||
|
||||
async function onDeletePost() {
|
||||
if (confirm("Are you sure you want to delete this post?")) {
|
||||
const authToken = ensureAuthToken()
|
||||
await deletePost(authToken, props.post.id)
|
||||
emit("post-deleted")
|
||||
}
|
||||
}
|
||||
|
||||
async toggleRepost() {
|
||||
if (!this.store.currentUser) {
|
||||
return
|
||||
function getPaymentOptions(): PaymentOption[] {
|
||||
const items = []
|
||||
for (const [code, name] of CRYPTOCURRENCIES) {
|
||||
const symbol = `$${code}`
|
||||
const field = props.post.account.fields.find(item => item.name === symbol)
|
||||
if (!field) {
|
||||
continue
|
||||
}
|
||||
const authToken = this.store.ensureAuthToken()
|
||||
let post
|
||||
try {
|
||||
if (this.post.reblogged) {
|
||||
post = await deleteRepost(authToken, this.post.id)
|
||||
} else {
|
||||
post = await createRepost(authToken, this.post.id)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return
|
||||
}
|
||||
this.post.reblogs_count = post.reblogs_count
|
||||
this.post.reblogged = post.reblogged
|
||||
const address = field.value.trim()
|
||||
items.push({ code, name, address })
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
canLike(): boolean {
|
||||
return this.store.currentUser !== null && this.post.visibility === "public"
|
||||
function togglePaymentAddress(payment: PaymentOption) {
|
||||
selectedPaymentAddress = selectedPaymentAddress === payment.address ? null : payment.address
|
||||
}
|
||||
|
||||
function isTokenized(): boolean {
|
||||
return props.post.token_id !== null
|
||||
}
|
||||
|
||||
function canMintToken(): boolean {
|
||||
return (
|
||||
Boolean(instance?.ipfs_gateway_url) &&
|
||||
Boolean(instance?.blockchain_contract_address) &&
|
||||
Boolean(instance?.blockchain_features?.minter) &&
|
||||
props.post.account.id === currentUser?.id &&
|
||||
props.post.visibility === "public" &&
|
||||
Boolean(currentUser?.wallet_address) &&
|
||||
!isTokenized() &&
|
||||
!isWaitingForToken
|
||||
)
|
||||
}
|
||||
|
||||
async function onMintToken() {
|
||||
if (
|
||||
!currentUser ||
|
||||
!currentUser.wallet_address ||
|
||||
!instance ||
|
||||
!instance.blockchain_contract_address
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
async toggleLike() {
|
||||
if (!this.store.currentUser) {
|
||||
return
|
||||
}
|
||||
const authToken = this.store.ensureAuthToken()
|
||||
let post
|
||||
try {
|
||||
if (this.post.favourited) {
|
||||
post = await unfavourite(authToken, this.post.id)
|
||||
} else {
|
||||
post = await favourite(authToken, this.post.id)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return
|
||||
}
|
||||
this.post.favourites_count = post.favourites_count
|
||||
this.post.favourited = post.favourited
|
||||
if (isTokenized() || isWaitingForToken) {
|
||||
return
|
||||
}
|
||||
|
||||
toggleMenu() {
|
||||
this.menuVisible = !this.menuVisible
|
||||
const authToken = ensureAuthToken()
|
||||
isWaitingForToken = true
|
||||
if (props.post.ipfs_cid === null) {
|
||||
const { ipfs_cid } = await makePermanent(authToken, props.post.id)
|
||||
props.post.ipfs_cid = ipfs_cid
|
||||
}
|
||||
|
||||
hideMenu() {
|
||||
this.menuVisible = false
|
||||
const tokenUri = `ipfs://${props.post.ipfs_cid}`
|
||||
console.info("token URI:", tokenUri)
|
||||
let signature
|
||||
try {
|
||||
signature = await getMintingAuthorization(authToken, props.post.id)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
isWaitingForToken = false
|
||||
return
|
||||
}
|
||||
|
||||
get ipfsUrl(): string | null {
|
||||
const gatewayUrl = this.store.instance?.ipfs_gateway_url
|
||||
if (
|
||||
!gatewayUrl ||
|
||||
this.post.ipfs_cid === null ||
|
||||
this.isTokenized ||
|
||||
this.isWaitingForToken
|
||||
) {
|
||||
return null
|
||||
}
|
||||
return `${gatewayUrl}/ipfs/${this.post.ipfs_cid}`
|
||||
const signer = await getWallet()
|
||||
if (!signer) {
|
||||
isWaitingForToken = false
|
||||
return
|
||||
}
|
||||
|
||||
canSaveToIpfs(): boolean {
|
||||
return (
|
||||
Boolean(this.store.instance?.ipfs_gateway_url) &&
|
||||
this.post.account.id === this.store.currentUser?.id &&
|
||||
this.post.visibility === "public" &&
|
||||
this.post.ipfs_cid === null &&
|
||||
!this.isWaitingForToken
|
||||
try {
|
||||
const transaction = await mintToken(
|
||||
instance.blockchain_contract_address,
|
||||
signer,
|
||||
currentUser.wallet_address,
|
||||
tokenUri,
|
||||
signature,
|
||||
)
|
||||
await onTokenMinted(authToken, props.post.id, transaction.hash)
|
||||
} catch (error) {
|
||||
// User has rejected tx
|
||||
isWaitingForToken = false
|
||||
return
|
||||
}
|
||||
|
||||
async saveToIpfs() {
|
||||
const { currentUser, instance } = this.store
|
||||
if (!currentUser || !instance || !instance.ipfs_gateway_url) {
|
||||
return
|
||||
// Wait until the server sees the tx
|
||||
const intervalId = setInterval(async () => {
|
||||
const updatedPost = await getPost(authToken, props.post.id)
|
||||
if (updatedPost.token_id) {
|
||||
clearInterval(intervalId)
|
||||
isWaitingForToken = false
|
||||
// Update post
|
||||
props.post.token_id = updatedPost.token_id
|
||||
props.post.token_tx_id = updatedPost.token_tx_id
|
||||
}
|
||||
const authToken = this.store.ensureAuthToken()
|
||||
const { ipfs_cid } = await makePermanent(authToken, this.post.id)
|
||||
this.post.ipfs_cid = ipfs_cid
|
||||
}
|
||||
|
||||
canDeletePost(): boolean {
|
||||
return this.post.account.id === this.store.currentUser?.id
|
||||
}
|
||||
|
||||
async deletePost() {
|
||||
if (confirm("Are you sure you want to delete this post?")) {
|
||||
const authToken = this.store.ensureAuthToken()
|
||||
await deletePost(authToken, this.post.id)
|
||||
this.$emit("post-deleted")
|
||||
}
|
||||
}
|
||||
|
||||
getPaymentOptions(): PaymentOption[] {
|
||||
const items = []
|
||||
for (const [code, name] of CRYPTOCURRENCIES) {
|
||||
const symbol = `$${code}`
|
||||
const field = this.post.account.fields.find(item => item.name === symbol)
|
||||
if (!field) {
|
||||
continue
|
||||
}
|
||||
const address = field.value.trim()
|
||||
items.push({ code, name, address })
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
selectedPaymentAddress: string | null = null
|
||||
|
||||
togglePaymentAddress(payment: PaymentOption) {
|
||||
this.selectedPaymentAddress = this.selectedPaymentAddress === payment.address ? null : payment.address
|
||||
}
|
||||
|
||||
get isTokenized(): boolean {
|
||||
return this.post.token_id !== null
|
||||
}
|
||||
|
||||
isWaitingForToken = false
|
||||
|
||||
canMintToken(): boolean {
|
||||
return (
|
||||
Boolean(this.store.instance?.ipfs_gateway_url) &&
|
||||
Boolean(this.store.instance?.blockchain_contract_address) &&
|
||||
Boolean(this.store.instance?.blockchain_features?.minter) &&
|
||||
this.post.account.id === this.store.currentUser?.id &&
|
||||
this.post.visibility === "public" &&
|
||||
Boolean(this.store.currentUser?.wallet_address) &&
|
||||
!this.isTokenized &&
|
||||
!this.isWaitingForToken
|
||||
)
|
||||
}
|
||||
|
||||
async mintToken() {
|
||||
const { currentUser, instance } = this.store
|
||||
if (
|
||||
!currentUser ||
|
||||
!currentUser.wallet_address ||
|
||||
!instance ||
|
||||
!instance.blockchain_contract_address
|
||||
) {
|
||||
return
|
||||
}
|
||||
if (this.isTokenized || this.isWaitingForToken) {
|
||||
return
|
||||
}
|
||||
const authToken = this.store.ensureAuthToken()
|
||||
this.isWaitingForToken = true
|
||||
if (this.post.ipfs_cid === null) {
|
||||
const { ipfs_cid } = await makePermanent(authToken, this.post.id)
|
||||
this.post.ipfs_cid = ipfs_cid
|
||||
}
|
||||
const tokenUri = `ipfs://${this.post.ipfs_cid}`
|
||||
console.info("token URI:", tokenUri)
|
||||
let signature
|
||||
try {
|
||||
signature = await getMintingAuthorization(authToken, this.post.id)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
this.isWaitingForToken = false
|
||||
return
|
||||
}
|
||||
const signer = await getWallet()
|
||||
if (!signer) {
|
||||
this.isWaitingForToken = false
|
||||
return
|
||||
}
|
||||
try {
|
||||
const transaction = await mintToken(
|
||||
instance.blockchain_contract_address,
|
||||
signer,
|
||||
currentUser.wallet_address,
|
||||
tokenUri,
|
||||
signature,
|
||||
)
|
||||
await onTokenMinted(authToken, this.post.id, transaction.hash)
|
||||
} catch (error) {
|
||||
// User has rejected tx
|
||||
this.isWaitingForToken = false
|
||||
return
|
||||
}
|
||||
// Wait until the server sees the tx
|
||||
const intervalId = setInterval(async () => {
|
||||
const post = await getPost(authToken, this.post.id)
|
||||
if (post.token_id) {
|
||||
clearInterval(intervalId)
|
||||
this.isWaitingForToken = false
|
||||
// Update post
|
||||
this.post.token_id = post.token_id
|
||||
this.post.token_tx_id = post.token_tx_id
|
||||
}
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
}, 5000)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
</menu>
|
||||
</div>
|
||||
<div class="character-counter" title="Characters left">
|
||||
{{ characterCounter }}
|
||||
{{ getCharacterCount() }}
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
|
@ -78,7 +78,7 @@
|
|||
<button
|
||||
class="btn"
|
||||
type="submit"
|
||||
:disabled="characterCounter < 0"
|
||||
:disabled="getCharacterCount() < 0"
|
||||
@click.prevent="publish()"
|
||||
>Publish</button>
|
||||
</div>
|
||||
|
@ -188,7 +188,7 @@ export default class PostEditor extends Vue {
|
|||
this.visibilityMenuVisible = false
|
||||
}
|
||||
|
||||
get characterCounter(): number {
|
||||
getCharacterCount(): number {
|
||||
if (!this.store.instance) {
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -25,8 +25,10 @@ import PostOrRepost from "@/components/PostOrRepost.vue"
|
|||
const props = defineProps<{
|
||||
posts: PostObject[],
|
||||
}>()
|
||||
/* eslint-disable-next-line no-undef */
|
||||
const emit = defineEmits<{(event: "load-next-page", maxId: string): void}>()
|
||||
/* eslint-disable-next-line no-undef, func-call-spacing */
|
||||
const emit = defineEmits<{
|
||||
(event: "load-next-page", maxId: string): void,
|
||||
}>()
|
||||
|
||||
let initialPostCount: number | null = null
|
||||
|
||||
|
|
|
@ -9,10 +9,18 @@
|
|||
</div>
|
||||
<post
|
||||
:post="post.reblog"
|
||||
:highlighted="false"
|
||||
:in-thread="false"
|
||||
@post-deleted="onPostDeleted(post.reblog.id); onPostDeleted(post.id)"
|
||||
></post>
|
||||
</template>
|
||||
<post v-else :post="post" @post-deleted="onPostDeleted(post.id)"></post>
|
||||
<post
|
||||
v-else
|
||||
:post="post"
|
||||
:highlighted="false"
|
||||
:in-thread="false"
|
||||
@post-deleted="onPostDeleted(post.id)"
|
||||
></post>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<avatar :profile="profile"></avatar>
|
||||
<div class="name-group">
|
||||
<div class="display-name">{{ profile.display_name || profile.username }}</div>
|
||||
<div class="actor-address">@{{ actorAddress }}</div>
|
||||
<div class="actor-address">@{{ getActorAddress(profile) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bio" v-html="profile.note"></div>
|
||||
|
@ -22,37 +22,18 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue, setup } from "vue-class-component"
|
||||
import { Prop } from "vue-property-decorator"
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Profile } from "@/api/users"
|
||||
import Avatar from "@/components/Avatar.vue"
|
||||
import { useInstanceInfo } from "@/store/instance"
|
||||
|
||||
@Options({
|
||||
components: {
|
||||
Avatar,
|
||||
},
|
||||
})
|
||||
export default class ProfileCard extends Vue {
|
||||
/* eslint-disable-next-line no-undef */
|
||||
defineProps<{
|
||||
profile: Profile,
|
||||
compact: boolean,
|
||||
}>()
|
||||
|
||||
@Prop()
|
||||
profile!: Profile
|
||||
|
||||
@Prop()
|
||||
compact = false
|
||||
|
||||
private store = setup(() => {
|
||||
const { instance, getActorAddress } = useInstanceInfo()
|
||||
return { instance, getActorAddress }
|
||||
})
|
||||
|
||||
get actorAddress(): string {
|
||||
return this.store.getActorAddress(this.profile)
|
||||
}
|
||||
|
||||
}
|
||||
const { getActorAddress } = useInstanceInfo()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
|
|
@ -10,21 +10,19 @@
|
|||
</form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue } from "vue-class-component"
|
||||
<script setup lang="ts">
|
||||
import { $ref } from "vue/macros"
|
||||
import { useRouter } from "vue-router"
|
||||
|
||||
export default class Search extends Vue {
|
||||
const router = useRouter()
|
||||
let q = $ref("")
|
||||
|
||||
function clear() {
|
||||
q = ""
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.q = ""
|
||||
}
|
||||
|
||||
search() {
|
||||
this.$router.push({ name: "search", query: { q: this.q } })
|
||||
}
|
||||
|
||||
function search() {
|
||||
router.push({ name: "search", query: { q: q } })
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -8,35 +8,14 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue, setup } from "vue-class-component"
|
||||
<script setup lang="ts">
|
||||
import { $ } from "vue/macros"
|
||||
|
||||
import { InstanceInfo } from "@/api/instance"
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import { useInstanceInfo } from "@/store/instance"
|
||||
import { renderMarkdown } from "@/utils/markdown"
|
||||
|
||||
@Options({
|
||||
components: {
|
||||
Sidebar,
|
||||
},
|
||||
})
|
||||
export default class AboutPage extends Vue {
|
||||
|
||||
private store = setup(() => {
|
||||
const { instance } = useInstanceInfo()
|
||||
return { instance }
|
||||
})
|
||||
|
||||
get instance(): InstanceInfo | null {
|
||||
return this.store.instance
|
||||
}
|
||||
|
||||
renderMarkdown(description: string): string {
|
||||
return renderMarkdown(description)
|
||||
}
|
||||
|
||||
}
|
||||
const { instance } = $(useInstanceInfo())
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue, setup } from "vue-class-component"
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from "vue"
|
||||
import { $ref } from "vue/macros"
|
||||
|
||||
import { Post, getHomeTimeline } from "@/api/posts"
|
||||
import PostEditor from "@/components/PostEditor.vue"
|
||||
|
@ -17,37 +18,23 @@ import PostList from "@/components/PostList.vue"
|
|||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
|
||||
@Options({
|
||||
components: {
|
||||
PostEditor,
|
||||
PostList,
|
||||
Sidebar,
|
||||
},
|
||||
const { ensureAuthToken } = useCurrentUser()
|
||||
|
||||
let posts = $ref<Post[]>([])
|
||||
|
||||
onMounted(async () => {
|
||||
const authToken = ensureAuthToken()
|
||||
posts = await getHomeTimeline(authToken)
|
||||
})
|
||||
export default class HomeTimeline extends Vue {
|
||||
|
||||
private store = setup(() => {
|
||||
const { ensureAuthToken } = useCurrentUser()
|
||||
return { ensureAuthToken }
|
||||
})
|
||||
|
||||
posts: Post[] = []
|
||||
|
||||
async created() {
|
||||
const authToken = this.store.ensureAuthToken()
|
||||
this.posts = await getHomeTimeline(authToken)
|
||||
}
|
||||
|
||||
insertPost(post: Post) {
|
||||
this.posts = [post, ...this.posts]
|
||||
}
|
||||
|
||||
async loadNextPage(maxId: string) {
|
||||
const authToken = this.store.ensureAuthToken()
|
||||
const posts = await getHomeTimeline(authToken, maxId)
|
||||
this.posts.push(...posts)
|
||||
}
|
||||
function insertPost(post: Post) {
|
||||
posts = [post, ...posts]
|
||||
}
|
||||
|
||||
async function loadNextPage(maxId: string) {
|
||||
const authToken = ensureAuthToken()
|
||||
const nextPage = await getHomeTimeline(authToken, maxId)
|
||||
posts.push(...nextPage)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -60,6 +60,8 @@
|
|||
<post
|
||||
v-if="notification.status"
|
||||
:post="notification.status"
|
||||
:highlighted="false"
|
||||
:in-thread="false"
|
||||
@post-deleted="onPostDeleted(index)"
|
||||
></post>
|
||||
<router-link
|
||||
|
|
Loading…
Reference in a new issue