✨ Channels member manage
This commit is contained in:
		| @@ -7,6 +7,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="editChannel" /> |       <v-list-item v-if="isOwned" append-icon="mdi-pencil" title="Edit" @click="editChannel" /> | ||||||
|  |       <v-list-item v-if="isOwned" append-icon="mdi-account-supervisor-circle" title="Members" @click="manageChannel" /> | ||||||
|       <v-list-item v-if="isOwned" append-icon="mdi-delete" title="Delete" @click="deleteChannel" /> |       <v-list-item v-if="isOwned" append-icon="mdi-delete" title="Delete" @click="deleteChannel" /> | ||||||
|     </v-list> |     </v-list> | ||||||
|   </v-menu> |   </v-menu> | ||||||
| @@ -29,6 +30,11 @@ function editChannel() { | |||||||
|   channels.show.editor = true |   channels.show.editor = true | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function manageChannel() { | ||||||
|  |   channels.related.manage_to = props.item | ||||||
|  |   channels.show.members = true | ||||||
|  | } | ||||||
|  |  | ||||||
| function deleteChannel() { | function deleteChannel() { | ||||||
|   channels.related.delete_to = props.item |   channels.related.delete_to = props.item | ||||||
|   channels.show.delete = true |   channels.show.delete = true | ||||||
|   | |||||||
							
								
								
									
										54
									
								
								src/components/chat/channels/ChannelInvitation.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/components/chat/channels/ChannelInvitation.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | <template> | ||||||
|  |   <v-card prepend-icon="mdi-account-plus" title="Invite someone"> | ||||||
|  |     <v-form @submit.prevent="inviteMember"> | ||||||
|  |       <v-card-text> | ||||||
|  |         <v-text-field | ||||||
|  |           label="Username" | ||||||
|  |           variant="outlined" | ||||||
|  |           density="comfortable" | ||||||
|  |           hint="Require username not the nickname" | ||||||
|  |           v-model="targetName" | ||||||
|  |         /> | ||||||
|  |       </v-card-text> | ||||||
|  |       <v-card-actions> | ||||||
|  |         <v-spacer></v-spacer> | ||||||
|  |  | ||||||
|  |         <v-btn type="reset" color="grey-darken-3" @click="emits('close')">Cancel</v-btn> | ||||||
|  |         <v-btn type="submit" :disabled="loading">Invite</v-btn> | ||||||
|  |       </v-card-actions> | ||||||
|  |     </v-form> | ||||||
|  |   </v-card> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import { ref } from "vue" | ||||||
|  | import { request } from "@/scripts/request" | ||||||
|  | import { getAtk } from "@/stores/userinfo" | ||||||
|  |  | ||||||
|  | const props = defineProps<{ item: any }>() | ||||||
|  | const emits = defineEmits(["close", "error", "relist"]) | ||||||
|  |  | ||||||
|  | const loading = ref(false) | ||||||
|  |  | ||||||
|  | const targetName = ref("") | ||||||
|  |  | ||||||
|  | async function inviteMember(evt: SubmitEvent) { | ||||||
|  |   const form = evt.target as HTMLFormElement | ||||||
|  |  | ||||||
|  |   loading.value = true | ||||||
|  |   const res = await request("messaging", `/api/channels/${props.item?.id}/invite`, { | ||||||
|  |     method: "POST", | ||||||
|  |     headers: { "Content-Type": "application/json", Authorization: `Bearer ${await getAtk()}` }, | ||||||
|  |     body: JSON.stringify({ | ||||||
|  |       account_name: targetName.value | ||||||
|  |     }) | ||||||
|  |   }) | ||||||
|  |   if (res.status !== 200) { | ||||||
|  |     emits("error", await res.text()) | ||||||
|  |   } else { | ||||||
|  |     form.reset() | ||||||
|  |     emits("relist") | ||||||
|  |   } | ||||||
|  |   loading.value = false | ||||||
|  | } | ||||||
|  | </script> | ||||||
| @@ -36,6 +36,7 @@ const channels = useChannels() | |||||||
|  |  | ||||||
| function createChannel() { | function createChannel() { | ||||||
|   channels.related.edit_to = null |   channels.related.edit_to = null | ||||||
|  |   channels.related.manage_to = null | ||||||
|   channels.related.delete_to = null |   channels.related.delete_to = null | ||||||
|   channels.show.editor = true |   channels.show.editor = true | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										124
									
								
								src/components/chat/channels/ChannelMembers.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/components/chat/channels/ChannelMembers.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | <template> | ||||||
|  |   <v-card title="Channel members" class="min-h-[540px]"> | ||||||
|  |     <v-list density="comfortable" lines="one"> | ||||||
|  |       <v-list-item v-for="item in members" :title="item.account.nick"> | ||||||
|  |         <template #subtitle>@{{ item.account.name }}</template> | ||||||
|  |         <template #prepend> | ||||||
|  |           <v-avatar | ||||||
|  |             color="grey-lighten-2" | ||||||
|  |             icon="mdi-account-circle" | ||||||
|  |             class="rounded-card me-2" | ||||||
|  |             size="small" | ||||||
|  |             :image="item?.account.avatar" | ||||||
|  |           /> | ||||||
|  |         </template> | ||||||
|  |         <template #append> | ||||||
|  |           <v-btn | ||||||
|  |             icon="mdi-account-remove" | ||||||
|  |             size="x-small" | ||||||
|  |             color="error" | ||||||
|  |             variant="text" | ||||||
|  |             :disabled="!checkKickable(item)" | ||||||
|  |             @click="kickMember(item)" | ||||||
|  |           /> | ||||||
|  |         </template> | ||||||
|  |       </v-list-item> | ||||||
|  |     </v-list> | ||||||
|  |  | ||||||
|  |     <div v-if="isOwned"> | ||||||
|  |       <v-divider class="mt-2 mb-3 border-opacity-50 mx-[-1rem]" /> | ||||||
|  |  | ||||||
|  |       <div class="px-3"> | ||||||
|  |         <v-dialog class="max-w-[540px]"> | ||||||
|  |           <template #activator="{ props }"> | ||||||
|  |             <v-btn v-bind="props" block prepend-icon="mdi-account-plus" variant="plain"> Invite someone </v-btn> | ||||||
|  |           </template> | ||||||
|  |  | ||||||
|  |           <template #default="{ isActive }"> | ||||||
|  |             <channel-invitation | ||||||
|  |               :item="props.item" | ||||||
|  |               @relist="listMembers" | ||||||
|  |               @error="(val) => (error = val)" | ||||||
|  |               @close="isActive.value = false" | ||||||
|  |             /> | ||||||
|  |           </template> | ||||||
|  |         </v-dialog> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <!-- @vue-ignore --> | ||||||
|  |     <v-snackbar v-model="error" :timeout="5000">Something went wrong... {{ error }}</v-snackbar> | ||||||
|  |   </v-card> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import { ref, watch } from "vue" | ||||||
|  | import { request } from "@/scripts/request" | ||||||
|  | import { getAtk, useUserinfo } from "@/stores/userinfo" | ||||||
|  | import { computed } from "vue" | ||||||
|  | import ChannelInvitation from "@/components/chat/channels/ChannelInvitation.vue" | ||||||
|  |  | ||||||
|  | const id = useUserinfo() | ||||||
|  |  | ||||||
|  | const props = defineProps<{ item: any }>() | ||||||
|  |  | ||||||
|  | const members = ref<any[]>([]) | ||||||
|  |  | ||||||
|  | const isOwned = computed(() => { | ||||||
|  |   return id.userinfo.idSet?.messaging === props.item?.account_id | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | const loading = ref(false) | ||||||
|  | const error = ref<string | null>(null) | ||||||
|  |  | ||||||
|  | watch( | ||||||
|  |   () => props.item, | ||||||
|  |   (val) => { | ||||||
|  |     if (val?.id) { | ||||||
|  |       listMembers(val.id) | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { deep: true, immediate: true } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | async function listMembers(id: number) { | ||||||
|  |   loading.value = true | ||||||
|  |   const res = await request("messaging", `/api/channels/${id}/members`) | ||||||
|  |   if (res.status !== 200) { | ||||||
|  |     error.value = await res.text() | ||||||
|  |   } else { | ||||||
|  |     error.value = null | ||||||
|  |     members.value = await res.json() | ||||||
|  |   } | ||||||
|  |   loading.value = false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function kickMember(item: any) { | ||||||
|  |   loading.value = true | ||||||
|  |   const res = await request("messaging", `/api/channels/${props.item?.id}/kick`, { | ||||||
|  |     method: "POST", | ||||||
|  |     headers: { "Content-Type": "application/json", Authorization: `Bearer ${await getAtk()}` }, | ||||||
|  |     body: JSON.stringify({ | ||||||
|  |       account_name: item.account.name | ||||||
|  |     }) | ||||||
|  |   }) | ||||||
|  |   if (res.status !== 200) { | ||||||
|  |     error.value = await res.text() | ||||||
|  |   } else { | ||||||
|  |     await listMembers(props.item?.id) | ||||||
|  |   } | ||||||
|  |   loading.value = false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function checkKickable(item: any) { | ||||||
|  |   if (item.account?.id === id.userinfo.idSet?.messaging) return false | ||||||
|  |   if (item.account?.id === props.item?.account_id) return false | ||||||
|  |   return true | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style> | ||||||
|  | .rounded-card { | ||||||
|  |   border-radius: 8px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
| @@ -2,6 +2,9 @@ | |||||||
|   <v-bottom-sheet class="max-w-[480px]" v-model="channels.show.editor"> |   <v-bottom-sheet class="max-w-[480px]" v-model="channels.show.editor"> | ||||||
|     <channel-editor @relist="channels.list" /> |     <channel-editor @relist="channels.list" /> | ||||||
|   </v-bottom-sheet> |   </v-bottom-sheet> | ||||||
|  |   <v-bottom-sheet class="max-w-[480px]" v-model="channels.show.members"> | ||||||
|  |     <channel-members :item="channels.related.manage_to" @relist="channels.list" /> | ||||||
|  |   </v-bottom-sheet> | ||||||
|   <v-bottom-sheet class="max-w-[480px]" v-model="channels.show.delete"> |   <v-bottom-sheet class="max-w-[480px]" v-model="channels.show.delete"> | ||||||
|     <channel-deletion @relist="channels.list" /> |     <channel-deletion @relist="channels.list" /> | ||||||
|   </v-bottom-sheet> |   </v-bottom-sheet> | ||||||
| @@ -10,6 +13,7 @@ | |||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { useChannels } from "@/stores/channels" | import { useChannels } from "@/stores/channels" | ||||||
| import ChannelEditor from "@/components/chat/channels/ChannelEditor.vue" | import ChannelEditor from "@/components/chat/channels/ChannelEditor.vue" | ||||||
|  | import ChannelMembers from "@/components/chat/channels/ChannelMembers.vue" | ||||||
| import ChannelDeletion from "@/components/chat/channels/ChannelDeletion.vue" | import ChannelDeletion from "@/components/chat/channels/ChannelDeletion.vue" | ||||||
|  |  | ||||||
| const channels = useChannels() | const channels = useChannels() | ||||||
|   | |||||||
| @@ -111,7 +111,7 @@ async function kickMember(item: any) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function checkKickable(item: any) { | function checkKickable(item: any) { | ||||||
|   if (item.account?.id === id.userinfo.data?.id) return false |   if (item.account?.id === id.userinfo.idSet?.interactive) return false | ||||||
|   if (item.account?.id === props.item?.account_id) return false |   if (item.account?.id === props.item?.account_id) return false | ||||||
|   return true |   return true | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,12 +9,14 @@ export const useChannels = defineStore("channels", () => { | |||||||
|   const done = ref(false) |   const done = ref(false) | ||||||
|  |  | ||||||
|   const show = reactive({ |   const show = reactive({ | ||||||
|  |     members: false, | ||||||
|     editor: false, |     editor: false, | ||||||
|     delete: false |     delete: false | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   const related = reactive<{ edit_to: any; delete_to: any }>({ |   const related = reactive<{ edit_to: any; manage_to: any; delete_to: any }>({ | ||||||
|     edit_to: null, |     edit_to: null, | ||||||
|  |     manage_to: null, | ||||||
|     delete_to: null |     delete_to: null | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user