Add "Delete post" to post actions menu
This commit is contained in:
parent
6f0f288537
commit
0b82ffff83
8 changed files with 123 additions and 10 deletions
|
@ -79,6 +79,9 @@ export async function getPostContext(
|
||||||
const url = `${BACKEND_URL}/api/v1/statuses/${postId}/context`
|
const url = `${BACKEND_URL}/api/v1/statuses/${postId}/context`
|
||||||
const response = await http(url, { authToken })
|
const response = await http(url, { authToken })
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error(data.message)
|
||||||
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +127,21 @@ export async function getPost(
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deletePost(
|
||||||
|
authToken: string,
|
||||||
|
postId: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const url = `${BACKEND_URL}/api/v1/statuses/${postId}`
|
||||||
|
const response = await http(url, {
|
||||||
|
method: "DELETE",
|
||||||
|
authToken,
|
||||||
|
})
|
||||||
|
if (response.status !== 204) {
|
||||||
|
const data = await response.json()
|
||||||
|
throw new Error(data.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function favourite(
|
export async function favourite(
|
||||||
authToken: string,
|
authToken: string,
|
||||||
postId: string,
|
postId: string,
|
||||||
|
|
1
src/assets/feather/trash.svg
Normal file
1
src/assets/feather/trash.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-trash"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
|
After Width: | Height: | Size: 358 B |
|
@ -108,7 +108,7 @@
|
||||||
</a>
|
</a>
|
||||||
<div
|
<div
|
||||||
class="post-menu-wrapper"
|
class="post-menu-wrapper"
|
||||||
v-if="canSaveToIpfs() || canMintToken()"
|
v-if="canSaveToIpfs() || canMintToken() || canDeletePost()"
|
||||||
v-click-away="hideMenu"
|
v-click-away="hideMenu"
|
||||||
>
|
>
|
||||||
<a class="icon" title="More" @click="toggleMenu()">
|
<a class="icon" title="More" @click="toggleMenu()">
|
||||||
|
@ -135,6 +135,16 @@
|
||||||
<span>Mint NFT</span>
|
<span>Mint NFT</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li v-if="canDeletePost()">
|
||||||
|
<a
|
||||||
|
class="icon"
|
||||||
|
title="Delete post"
|
||||||
|
@click="hideMenu(); deletePost()"
|
||||||
|
>
|
||||||
|
<img :src="require('@/assets/feather/trash.svg')">
|
||||||
|
<span>Delete post</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="crypto-widget">
|
<div class="crypto-widget">
|
||||||
|
@ -167,7 +177,15 @@ import { Options, Vue, setup } from "vue-class-component"
|
||||||
import { Prop } from "vue-property-decorator"
|
import { Prop } from "vue-property-decorator"
|
||||||
|
|
||||||
import { makePermanent, getSignature, mintToken, onTokenMinted } from "@/api/nft"
|
import { makePermanent, getSignature, mintToken, onTokenMinted } from "@/api/nft"
|
||||||
import { Post, getPost, favourite, unfavourite, createRepost, deleteRepost } from "@/api/posts"
|
import {
|
||||||
|
Post,
|
||||||
|
getPost,
|
||||||
|
deletePost,
|
||||||
|
favourite,
|
||||||
|
unfavourite,
|
||||||
|
createRepost,
|
||||||
|
deleteRepost,
|
||||||
|
} from "@/api/posts"
|
||||||
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 PostEditor from "@/components/PostEditor.vue"
|
import PostEditor from "@/components/PostEditor.vue"
|
||||||
|
@ -319,6 +337,16 @@ export default class PostComponent extends Vue {
|
||||||
this.post.ipfs_cid = ipfs_cid
|
this.post.ipfs_cid = ipfs_cid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canDeletePost(): boolean {
|
||||||
|
return this.post.account.id === this.store.currentUser?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePost() {
|
||||||
|
const authToken = this.store.ensureAuthToken()
|
||||||
|
await deletePost(authToken, this.post.id)
|
||||||
|
this.$emit("post-deleted")
|
||||||
|
}
|
||||||
|
|
||||||
getPaymentOptions(): PaymentOption[] {
|
getPaymentOptions(): PaymentOption[] {
|
||||||
const items = []
|
const items = []
|
||||||
for (const [code, name] of CRYPTOCURRENCIES) {
|
for (const [code, name] of CRYPTOCURRENCIES) {
|
||||||
|
|
|
@ -4,9 +4,12 @@
|
||||||
<img :src="require('@/assets/feather/repeat.svg')">
|
<img :src="require('@/assets/feather/repeat.svg')">
|
||||||
<span>{{ post.account.display_name || post.account.username }} reposted</span>
|
<span>{{ post.account.display_name || post.account.username }} reposted</span>
|
||||||
</div>
|
</div>
|
||||||
<post :post="post.reblog"></post>
|
<post
|
||||||
|
:post="post.reblog"
|
||||||
|
@post-deleted="onPostDeleted(post.reblog.id); onPostDeleted(post.id)"
|
||||||
|
></post>
|
||||||
</template>
|
</template>
|
||||||
<post v-else :post="post"></post>
|
<post v-else :post="post" @post-deleted="onPostDeleted(post.id)"></post>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -18,6 +21,12 @@ import Post from "@/components/Post.vue"
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
post: PostObject,
|
post: PostObject,
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{(event: "post-deleted"): void}>()
|
||||||
|
|
||||||
|
function onPostDeleted(postId: string) {
|
||||||
|
emit("post-deleted", postId)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="notification" v-for="notification in notifications" :key="notification.id">
|
<div
|
||||||
|
class="notification"
|
||||||
|
v-for="(notification, index) in notifications"
|
||||||
|
:key="notification.id"
|
||||||
|
>
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<template v-if="notification.type === 'follow'">
|
<template v-if="notification.type === 'follow'">
|
||||||
<img :src="require('@/assets/feather/user-plus.svg')">
|
<img :src="require('@/assets/feather/user-plus.svg')">
|
||||||
|
@ -24,7 +28,11 @@
|
||||||
<span>{{ getSenderName(notification) }} reposted your post</span>
|
<span>{{ getSenderName(notification) }} reposted your post</span>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<post v-if="notification.status" :post="notification.status"></post>
|
<post
|
||||||
|
v-if="notification.status"
|
||||||
|
:post="notification.status"
|
||||||
|
@post-deleted="onPostDeleted(index)"
|
||||||
|
></post>
|
||||||
<router-link
|
<router-link
|
||||||
v-else
|
v-else
|
||||||
class="profile"
|
class="profile"
|
||||||
|
@ -73,6 +81,10 @@ function getSenderName(notification: Notification): string {
|
||||||
const sender = notification.account
|
const sender = notification.account
|
||||||
return sender.display_name || sender.username
|
return sender.display_name || sender.username
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPostDeleted(notificationIndex: number) {
|
||||||
|
notifications.value.splice(notificationIndex, 1)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<div class="content posts">
|
<div v-if="!isLoading && thread.length === 0" class="content not-found">
|
||||||
|
Not found
|
||||||
|
</div>
|
||||||
|
<div v-else class="content posts">
|
||||||
<post
|
<post
|
||||||
v-for="(post, index) in thread"
|
v-for="(post, index) in thread"
|
||||||
:key="post.id"
|
:key="post.id"
|
||||||
|
@ -10,6 +13,7 @@
|
||||||
@highlight="onPostHighlight($event)"
|
@highlight="onPostHighlight($event)"
|
||||||
@navigate-to="onPostNavigate($event)"
|
@navigate-to="onPostNavigate($event)"
|
||||||
@comment-created="onCommentCreated(index, $event)"
|
@comment-created="onCommentCreated(index, $event)"
|
||||||
|
@post-deleted="onPostDeleted(index)"
|
||||||
></post>
|
></post>
|
||||||
</div>
|
</div>
|
||||||
<sidebar></sidebar>
|
<sidebar></sidebar>
|
||||||
|
@ -44,14 +48,26 @@ export default class PostDetail extends Vue {
|
||||||
private highlightedId: string | null = null
|
private highlightedId: string | null = null
|
||||||
|
|
||||||
thread: Post[] = []
|
thread: Post[] = []
|
||||||
|
isLoading = false
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.selectedId = this.$route.params.postId as string
|
this.selectedId = this.$route.params.postId as string
|
||||||
this.loader = getPostContext(this.store.authToken, this.selectedId)
|
this.loader = getPostContext(this.store.authToken, this.selectedId)
|
||||||
|
this.isLoading = true
|
||||||
}
|
}
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.thread = await this.loader
|
try {
|
||||||
|
this.thread = await this.loader
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message === "post not found") {
|
||||||
|
// Show "not found" text
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
} finally {
|
||||||
|
this.isLoading = false
|
||||||
|
}
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
// TODO: scrolls to wrong position if posts above it have images
|
// TODO: scrolls to wrong position if posts above it have images
|
||||||
this.scrollTo(this.selectedId as string)
|
this.scrollTo(this.selectedId as string)
|
||||||
|
@ -98,6 +114,10 @@ export default class PostDetail extends Vue {
|
||||||
this.thread.splice(index + 1, 0, post)
|
this.thread.splice(index + 1, 0, post)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPostDeleted(postIndex: number) {
|
||||||
|
this.thread.splice(postIndex, 1)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -105,6 +125,11 @@ export default class PostDetail extends Vue {
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "../styles/layout";
|
@import "../styles/layout";
|
||||||
|
|
||||||
|
.not-found {
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
margin: 0 0 $block-outer-padding;
|
margin: 0 0 $block-outer-padding;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,12 @@
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<div class="content posts">
|
<div class="content posts">
|
||||||
<post-editor @post-created="insertPost"></post-editor>
|
<post-editor @post-created="insertPost"></post-editor>
|
||||||
<post-or-repost v-for="post in posts" :post="post" :key="post.id"></post-or-repost>
|
<post-or-repost
|
||||||
|
v-for="post in posts"
|
||||||
|
:post="post"
|
||||||
|
:key="post.id"
|
||||||
|
@post-deleted="onPostDeleted($event)"
|
||||||
|
></post-or-repost>
|
||||||
<button
|
<button
|
||||||
v-if="isPageFull()"
|
v-if="isPageFull()"
|
||||||
class="btn"
|
class="btn"
|
||||||
|
@ -53,6 +58,11 @@ export default class PostList extends Vue {
|
||||||
this.posts = [post, ...this.posts]
|
this.posts = [post, ...this.posts]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPostDeleted(postId: string) {
|
||||||
|
const postIndex = this.posts.findIndex((post) => post.id === postId)
|
||||||
|
this.posts.splice(postIndex, 1)
|
||||||
|
}
|
||||||
|
|
||||||
isPageFull(): boolean {
|
isPageFull(): boolean {
|
||||||
return this.posts.length >= PAGE_SIZE
|
return this.posts.length >= PAGE_SIZE
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,12 @@
|
||||||
<dt>{{ profile.followers_count }}</dt><dd>followers</dd>
|
<dt>{{ profile.followers_count }}</dt><dd>followers</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<post-or-repost v-for="post in posts" :post="post" :key="post.id"></post-or-repost>
|
<post-or-repost
|
||||||
|
v-for="post in posts"
|
||||||
|
:post="post"
|
||||||
|
:key="post.id"
|
||||||
|
@post-deleted="onPostDeleted($event)"
|
||||||
|
></post-or-repost>
|
||||||
</div>
|
</div>
|
||||||
<sidebar></sidebar>
|
<sidebar></sidebar>
|
||||||
</div>
|
</div>
|
||||||
|
@ -138,6 +143,11 @@ export default class ProfileView extends Vue {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPostDeleted(postId: string) {
|
||||||
|
const postIndex = this.posts.findIndex((post) => post.id === postId)
|
||||||
|
this.posts.splice(postIndex, 1)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue