♻️ Interactive v2 #1
| @@ -3,7 +3,7 @@ | |||||||
|     <v-progress-circular indeterminate /> |     <v-progress-circular indeterminate /> | ||||||
|   </div> |   </div> | ||||||
|  |  | ||||||
|   <div v-else class="flex flex-col gap-2 mt-3"> |   <div v-else class="flex flex-col gap-5 mt-3"> | ||||||
|     <div v-for="(item, idx) in props.comments" class="text-sm"> |     <div v-for="(item, idx) in props.comments" class="text-sm"> | ||||||
|       <post-item :item="item" @update:item="(val) => updateItem(idx, val)" /> |       <post-item :item="item" @update:item="(val) => updateItem(idx, val)" /> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ | |||||||
|         <v-list density="compact" lines="one"> |         <v-list density="compact" lines="one"> | ||||||
|           <v-list-item disabled append-icon="mdi-flag" title="Report" /> |           <v-list-item disabled append-icon="mdi-flag" title="Report" /> | ||||||
|           <v-list-item v-if="isOwned" append-icon="mdi-pencil" title="Edit" @click="editPost" /> |           <v-list-item v-if="isOwned" append-icon="mdi-pencil" title="Edit" @click="editPost" /> | ||||||
|           <v-list-item v-if="isOwned" append-icon="mdi-delete" title="Delete" /> |           <v-list-item v-if="isOwned" append-icon="mdi-delete" title="Delete" @click="deletePost" /> | ||||||
|         </v-list> |         </v-list> | ||||||
|       </v-menu> |       </v-menu> | ||||||
|     </div> |     </div> | ||||||
| @@ -81,10 +81,19 @@ const isOwned = computed(() => props.item?.author_id === id.userinfo.data.id) | |||||||
|  |  | ||||||
| function editPost() { | function editPost() { | ||||||
|   editor.related.edit_to = props.item |   editor.related.edit_to = props.item | ||||||
|   if(editor.show.hasOwnProperty(props.item.model_type)) { |   if (editor.show.hasOwnProperty(props.item.model_type)) { | ||||||
|     // @ts-ignore |     // @ts-ignore | ||||||
|     editor.show[props.item.model_type] = true |     editor.show[props.item.model_type] = true | ||||||
|   } |   } | ||||||
|  |   if (props.item.model_type === "comment") { | ||||||
|  |     editor.related.comment_to = props.item | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function deletePost() { | ||||||
|  |   editor.related.delete_to = JSON.parse(JSON.stringify(props.item)) | ||||||
|  |   editor.related.delete_to.model_type = props.item.model_type + "s" | ||||||
|  |   editor.show.delete = true | ||||||
| } | } | ||||||
|  |  | ||||||
| function updateReactions(symbol: string, num: number) { | function updateReactions(symbol: string, num: number) { | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ | |||||||
|  |  | ||||||
|       <v-card-text> |       <v-card-text> | ||||||
|         <v-container class="article-container"> |         <v-container class="article-container"> | ||||||
|           <v-alert v-if="editor.related.edit_to" class="mb-3" type="info" variant="tonal"> |           <v-alert v-if="editor.related.edit_to" class="mb-5" type="info" variant="tonal"> | ||||||
|             You are editing a post with alias <b class="font-mono">{{ editor.related.edit_to?.alias }}</b> |             You are editing a post with alias <b class="font-mono">{{ editor.related.edit_to?.alias }}</b> | ||||||
|           </v-alert> |           </v-alert> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,7 +2,11 @@ | |||||||
|   <v-card title="Leave your comment" :loading="loading"> |   <v-card title="Leave your comment" :loading="loading"> | ||||||
|     <v-form @submit.prevent="postComment"> |     <v-form @submit.prevent="postComment"> | ||||||
|       <v-card-text> |       <v-card-text> | ||||||
|         <v-textarea required hide-details name="content" variant="outlined" label="What do you want to say?" /> |         <v-alert v-if="editor.related.edit_to" class="mb-5" type="info" variant="tonal"> | ||||||
|  |           You are editing a comment with alias <b class="font-mono">{{ editor.related.edit_to?.alias }}</b> | ||||||
|  |         </v-alert> | ||||||
|  |  | ||||||
|  |         <v-textarea required hide-details variant="outlined" label="What do you want to say?" v-model="data.content" /> | ||||||
|  |  | ||||||
|         <p class="px-2 mt-1 text-body-2 opacity-80">Your comment will leave below {{ postIdentifier }}</p> |         <p class="px-2 mt-1 text-body-2 opacity-80">Your comment will leave below {{ postIdentifier }}</p> | ||||||
|       </v-card-text> |       </v-card-text> | ||||||
| @@ -26,7 +30,7 @@ | |||||||
| import { request } from "@/scripts/request" | import { request } from "@/scripts/request" | ||||||
| import { useEditor } from "@/stores/editor" | import { useEditor } from "@/stores/editor" | ||||||
| import { getAtk } from "@/stores/userinfo" | import { getAtk } from "@/stores/userinfo" | ||||||
| import { computed, ref } from "vue" | import { computed, ref, watch } from "vue" | ||||||
|  |  | ||||||
| const editor = useEditor() | const editor = useEditor() | ||||||
|  |  | ||||||
| @@ -43,16 +47,26 @@ const error = ref<string | null>(null) | |||||||
| const success = ref(false) | const success = ref(false) | ||||||
| const loading = ref(false) | const loading = ref(false) | ||||||
|  |  | ||||||
|  | const data = ref<any>({ | ||||||
|  |   content: "" | ||||||
|  | }) | ||||||
|  |  | ||||||
| async function postComment(evt: SubmitEvent) { | async function postComment(evt: SubmitEvent) { | ||||||
|   const form = evt.target as HTMLFormElement |   const form = evt.target as HTMLFormElement | ||||||
|   const data = new FormData(form) |   const payload = data.value | ||||||
|   if (!data.has("content")) return |  | ||||||
|  |   if (!payload.content) return | ||||||
|  |  | ||||||
|  |   const url = editor.related.edit_to | ||||||
|  |     ? `/api/p/comments/${editor.related.edit_to?.id}` | ||||||
|  |     : `/api/p/${target.value?.model_type}/${target.value?.alias}/comments` | ||||||
|  |   const method = editor.related.edit_to ? "PUT" : "POST" | ||||||
|  |  | ||||||
|   loading.value = true |   loading.value = true | ||||||
|   const res = await request(`/api/p/${target.value?.model_type}/${target.value?.alias}/comments`, { |   const res = await request(url, { | ||||||
|     method: "POST", |     method: method, | ||||||
|     headers: { Authorization: `Bearer ${getAtk()}` }, |     headers: { "Content-Type": "application/json", Authorization: `Bearer ${getAtk()}` }, | ||||||
|     body: data |     body: JSON.stringify(payload) | ||||||
|   }) |   }) | ||||||
|   if (res.status === 200) { |   if (res.status === 200) { | ||||||
|     form.reset() |     form.reset() | ||||||
| @@ -64,4 +78,10 @@ async function postComment(evt: SubmitEvent) { | |||||||
|   loading.value = false |   loading.value = false | ||||||
|   editor.done = true |   editor.done = true | ||||||
| } | } | ||||||
|  |  | ||||||
|  | watch(editor.related, (val) => { | ||||||
|  |   if (val.edit_to && val.edit_to.model_type === "comment") { | ||||||
|  |     data.value = val.edit_to | ||||||
|  |   } | ||||||
|  | }) | ||||||
| </script> | </script> | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|   <v-card title="Record a moment" :loading="loading"> |   <v-card title="Record a moment" :loading="loading"> | ||||||
|     <v-form @submit.prevent="postMoment"> |     <v-form @submit.prevent="postMoment"> | ||||||
|       <v-card-text> |       <v-card-text> | ||||||
|         <v-alert v-if="editor.related.edit_to" class="mb-3" type="info" variant="tonal"> |         <v-alert v-if="editor.related.edit_to" class="mb-5" type="info" variant="tonal"> | ||||||
|           You are editing a post with alias <b class="font-mono">{{ editor.related.edit_to?.alias }}</b> |           You are editing a post with alias <b class="font-mono">{{ editor.related.edit_to?.alias }}</b> | ||||||
|         </v-alert> |         </v-alert> | ||||||
|  |  | ||||||
| @@ -117,7 +117,7 @@ async function postMoment(evt: SubmitEvent) { | |||||||
| } | } | ||||||
|  |  | ||||||
| watch(editor.related, (val) => { | watch(editor.related, (val) => { | ||||||
|   if (val.edit_to) { |   if (val.edit_to && val.edit_to.model_type === "moment") { | ||||||
|     data.value = val.edit_to |     data.value = val.edit_to | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
|   | |||||||
| @@ -8,13 +8,18 @@ | |||||||
|   <v-dialog v-model="editor.show.article" transition="dialog-bottom-transition" fullscreen eager> |   <v-dialog v-model="editor.show.article" transition="dialog-bottom-transition" fullscreen eager> | ||||||
|     <article-editor /> |     <article-editor /> | ||||||
|   </v-dialog> |   </v-dialog> | ||||||
|  |  | ||||||
|  |   <v-dialog v-model="editor.show.delete" class="max-w-[540px]" eager> | ||||||
|  |     <post-deletion /> | ||||||
|  |   </v-dialog> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { useEditor } from "@/stores/editor" | import { useEditor } from "@/stores/editor" | ||||||
| import MomentEditor from "@/components/publish/MomentEditor.vue" | import MomentEditor from "@/components/publish/MomentEditor.vue" | ||||||
| import CommentEditor from "@/components/publish/CommentEditor.vue"; | import CommentEditor from "@/components/publish/CommentEditor.vue" | ||||||
| import ArticleEditor from "@/components/publish/ArticleEditor.vue"; | import ArticleEditor from "@/components/publish/ArticleEditor.vue" | ||||||
|  | import PostDeletion from "@/components/publish/PostDeletion.vue" | ||||||
|  |  | ||||||
| const editor = useEditor() | const editor = useEditor() | ||||||
| </script> | </script> | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								pkg/views/src/components/publish/PostDeletion.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								pkg/views/src/components/publish/PostDeletion.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | <template> | ||||||
|  |   <v-card title="Delete a post" :loading="loading"> | ||||||
|  |     <template #text> | ||||||
|  |       You are deleting a post with alias | ||||||
|  |       <b class="font-mono">{{ editor.related.delete_to?.alias }}</b> | ||||||
|  |       Are you confirm? | ||||||
|  |     </template> | ||||||
|  |     <template #actions> | ||||||
|  |       <div class="w-full flex justify-end"> | ||||||
|  |         <v-btn color="grey-darken-3" @click="editor.show.delete = false">Not really</v-btn> | ||||||
|  |         <v-btn color="error" :disabled="loading" @click="deletePost">Yes</v-btn> | ||||||
|  |       </div> | ||||||
|  |     </template> | ||||||
|  |   </v-card> | ||||||
|  |  | ||||||
|  |   <v-snackbar v-model="success" :timeout="3000">The post has been deleted.</v-snackbar> | ||||||
|  |  | ||||||
|  |   <!-- @vue-ignore --> | ||||||
|  |   <v-snackbar v-model="error" :timeout="5000">Something went wrong... {{ error }}</v-snackbar> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import { useEditor } from "@/stores/editor" | ||||||
|  | import { getAtk } from "@/stores/userinfo" | ||||||
|  | import { ref } from "vue" | ||||||
|  |  | ||||||
|  | const editor = useEditor() | ||||||
|  |  | ||||||
|  | const error = ref<string | null>(null) | ||||||
|  | const success = ref(false) | ||||||
|  | const loading = ref(false) | ||||||
|  |  | ||||||
|  | async function deletePost() { | ||||||
|  |   const target = editor.related.delete_to | ||||||
|  |   const url = `/api/p/${target.model_type}/${target.id}` | ||||||
|  |  | ||||||
|  |   loading.value = true | ||||||
|  |   const res = await fetch(url, { | ||||||
|  |     method: "DELETE", | ||||||
|  |     headers: { Authorization: `Bearer ${getAtk()}` } | ||||||
|  |   }) | ||||||
|  |   if (res.status !== 200) { | ||||||
|  |     error.value = await res.text() | ||||||
|  |   } else { | ||||||
|  |     success.value = true | ||||||
|  |     editor.show.delete = false | ||||||
|  |     editor.related.delete_to = null | ||||||
|  |   } | ||||||
|  |   loading.value = false | ||||||
|  | } | ||||||
|  | </script> | ||||||
| @@ -1,5 +1,6 @@ | |||||||
| import { defineStore } from "pinia" | import { defineStore } from "pinia" | ||||||
| import { reactive, ref } from "vue" | import { reactive, ref } from "vue" | ||||||
|  | import { getAtk } from "@/stores/userinfo" | ||||||
|  |  | ||||||
| export const useEditor = defineStore("editor", () => { | export const useEditor = defineStore("editor", () => { | ||||||
|   const done = ref(false) |   const done = ref(false) | ||||||
| @@ -7,14 +8,22 @@ export const useEditor = defineStore("editor", () => { | |||||||
|   const show = reactive({ |   const show = reactive({ | ||||||
|     moment: false, |     moment: false, | ||||||
|     article: false, |     article: false, | ||||||
|     comment: false |     comment: false, | ||||||
|  |     delete: false | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   const related = reactive<{ edit_to: any; comment_to: any; reply_to: any; repost_to: any }>({ |   const related = reactive<{ | ||||||
|  |     edit_to: any | ||||||
|  |     comment_to: any | ||||||
|  |     reply_to: any | ||||||
|  |     repost_to: any | ||||||
|  |     delete_to: any | ||||||
|  |   }>({ | ||||||
|     edit_to: null, |     edit_to: null, | ||||||
|     comment_to: null, |     comment_to: null, | ||||||
|     reply_to: null, |     reply_to: null, | ||||||
|     repost_to: null |     repost_to: null, | ||||||
|  |     delete_to: null | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   return { show, related, done } |   return { show, related, done } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| <template> | <template> | ||||||
|   <v-container class="flex max-md:flex-col gap-3 overflow-auto max-h-[calc(100vh-64px)] no-scrollbar"> |   <v-container class="flex max-md:flex-col gap-3 overflow-auto max-h-[calc(100vh-64px)] no-scrollbar"> | ||||||
|     <div class="timeline flex-grow-1"> |     <div class="content flex-grow-1"> | ||||||
|       <v-card :loading="loading"> |       <v-card :loading="loading"> | ||||||
|         <article> |         <article> | ||||||
|           <v-card-text> |           <v-card-text> | ||||||
| @@ -26,7 +26,7 @@ | |||||||
|       </v-card> |       </v-card> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <div class="aside sticky top-0 w-full h-fit md:max-w-[360px] md:min-w-[280px]"> |     <div class="aside sticky top-0 w-full h-fit w-full md:max-w-[380px] md:min-w-[360px]"> | ||||||
|       <v-card title="Comments"> |       <v-card title="Comments"> | ||||||
|         <div class="px-[1rem] pb-[0.825rem] mt-[-12px]"> |         <div class="px-[1rem] pb-[0.825rem] mt-[-12px]"> | ||||||
|           <comment-list |           <comment-list | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user