feat: discussions edit and delete

This commit is contained in:
Jannat Patel
2024-01-16 17:24:35 +05:30
parent 3a5977a718
commit 3313db844c
5 changed files with 185 additions and 60 deletions

View File

@@ -1,9 +1,12 @@
<template>
<div
class="flex h-full flex-col justify-between transition-all duration-300 ease-in-out bg-gray-50"
:class="isSidebarCollapsed ? 'w-12' : 'w-56'"
:class="isSidebarCollapsed ? 'w-14' : 'w-56'"
>
<div class="flex flex-col overflow-hidden">
<div
class="flex flex-col overflow-hidden"
:class="isSidebarCollapsed ? 'items-center' : ''"
>
<UserDropdown class="p-2" :isCollapsed="isSidebarCollapsed" />
<div class="flex flex-col overflow-y-auto">
<SidebarLink

View File

@@ -1,8 +1,10 @@
<template>
<div class="mt-6">
<div class="flex items-center mb-5">
<Button variant="subtle" @click="showTopics = true">
<ChevronLeft class="w-4 h-4 stroke-1.5" />
<Button variant="outline" @click="showTopics = true">
<template #icon>
<ChevronLeft class="w-5 h-5 stroke-1.5 text-gray-700" />
</template>
</Button>
<span class="text-lg font-semibold ml-2">
{{ topic.title }}
@@ -14,23 +16,65 @@
class="py-3"
:class="{ 'border-b': index + 1 != replies.data.length }"
>
<div class="flex items-center mb-2">
<UserAvatar :user="reply.user" class="mr-2" />
<span>
{{ reply.user.full_name }}
</span>
<span class="text-sm ml-2">
{{ timeAgo(reply.creation) }}
</span>
<div class="flex items-center justify-between mb-2">
<div class="flex items-center">
<UserAvatar :user="reply.user" class="mr-2" />
<span>
{{ reply.user.full_name }}
</span>
<span class="text-sm ml-2">
{{ timeAgo(reply.creation) }}
</span>
</div>
<Dropdown
v-if="user.data.name == reply.owner && !reply.editable"
:options="[
{
label: 'Edit',
onClick() {
reply.editable = true
},
},
{
label: 'Delete',
onClick() {
deleteReply(reply)
},
},
]"
>
<template v-slot="{ open }">
<MoreHorizontal class="w-4 h-4 stroke-1.5 cursor-pointer" />
</template>
</Dropdown>
<div v-if="reply.editable">
<Button variant="ghost" @click="postEdited(reply)">
{{ __('Post') }}
</Button>
<Button variant="ghost" @click="reply.editable = false">
{{ __('Discard') }}
</Button>
</div>
</div>
<div v-html="reply.reply"></div>
<TextEditor
:content="reply.reply"
@change="(val) => (reply.reply = val)"
:editable="reply.editable || false"
:fixedMenu="reply.editable || false"
:editorClass="
reply.editable
? 'prose-sm max-w-none border-b border-x rounded-b-md py-1 px-2 min-h-[4rem]'
: 'prose-sm'
"
/>
</div>
</div>
<TextEditor
:content="newReply"
@change="(val) => (newReply = val)"
placeholder="Type your reply here..."
editorClass="prose-sm py-2 px-2 min-h-[100px] border-gray-300 hover:border-gray-400 rounded-md bg-gray-200 w-full mt-5"
:fixedMenu="true"
editorClass="prose-sm max-w-none min-h-[7rem] border-b border-x rounded-b-md py-1 px-2"
/>
<div class="flex justify-between mt-2">
<span> </span>
@@ -43,15 +87,16 @@
</div>
</template>
<script setup>
import { createResource, TextEditor, Button } from 'frappe-ui'
import { createResource, TextEditor, Button, Dropdown } from 'frappe-ui'
import { timeAgo } from '../utils'
import UserAvatar from '@/components/UserAvatar.vue'
import { ChevronLeft } from 'lucide-vue-next'
import { ChevronLeft, MoreHorizontal } from 'lucide-vue-next'
import { ref, inject, onMounted } from 'vue'
const showTopics = defineModel('showTopics')
const newReply = ref('')
const socket = inject('$socket')
const user = inject('$user')
const props = defineProps({
topic: {
@@ -60,11 +105,16 @@ const props = defineProps({
},
})
console.log(socket)
socket.on('publish_message', (data) => {
console.log('publish')
console.log(data)
replies.reload()
onMounted(() => {
socket.on('publish_message', (data) => {
replies.reload()
})
socket.on('update_message', (data) => {
replies.reload()
})
socket.on('delete_message', (data) => {
replies.reload()
})
})
const replies = createResource({
@@ -107,4 +157,59 @@ const postReply = () => {
}
)
}
const editReplyResource = createResource({
url: 'frappe.client.set_value',
makeParams(values) {
return {
doctype: 'Discussion Reply',
name: values.name,
fieldname: 'reply',
value: values.reply,
}
},
})
const postEdited = (reply) => {
editReplyResource.submit(
{
name: reply.name,
reply: reply.reply,
},
{
validate() {
if (!reply.reply) {
return 'Reply cannot be empty'
}
},
onSuccess() {
reply.editable = false
replies.reload()
},
}
)
}
const deleteReplyResource = createResource({
url: 'frappe.client.delete',
makeParams(values) {
return {
doctype: 'Discussion Reply',
name: values.name,
}
},
})
const deleteReply = (reply) => {
deleteReplyResource.submit(
{
name: reply.name,
},
{
onSuccess() {
replies.reload()
},
}
)
}
</script>

View File

@@ -1,6 +1,9 @@
<template>
<div v-if="topics.data">
<div>
<Button class="float-right" @click="openQuestionModal()">
{{ __('Ask a Question') }}
</Button>
<div class="text-xl font-semibold">
{{ __(title) }}
</div>
@@ -36,7 +39,7 @@
</div>
</template>
<script setup>
import { createResource } from 'frappe-ui'
import { createResource, Button } from 'frappe-ui'
import UserAvatar from '@/components/UserAvatar.vue'
import { timeAgo } from '../utils'
import { ref, onMounted, inject } from 'vue'
@@ -45,6 +48,7 @@ import DiscussionReplies from '@/components/DiscussionReplies.vue'
const showTopics = ref(true)
const currentTopic = ref(null)
const socket = inject('$socket')
const showQuestionModal = ref(false)
const props = defineProps({
title: {
@@ -81,4 +85,8 @@ const showReplies = (topic) => {
showTopics.value = false
currentTopic.value = topic
}
const openQuestionModal = () => {
showQuestionModal.value = true
}
</script>

View File

@@ -1,23 +1,32 @@
<template>
<button
class="flex h-7 cursor-pointer items-center rounded text-gray-800 duration-300 ease-in-out focus:outline-none focus:transition-none focus-visible:rounded focus-visible:ring-2 focus-visible:ring-gray-400"
:class="isActive ? 'bg-white shadow-sm' : 'hover:bg-gray-100'" @click="handleClick">
<div class="flex items-center duration-300 ease-in-out" :class="isCollapsed ? 'p-1' : 'px-2 py-1'">
<Tooltip :text="label" placement="right">
<slot name="icon">
<span class="grid h-5 w-6 flex-shrink-0 place-items-center">
<component :is="icon" class="h-4 w-4 stroke-1.5 text-gray-700" />
</span>
</slot>
</Tooltip>
<span class="flex-shrink-0 text-base duration-300 ease-in-out" :class="isCollapsed
? 'ml-0 w-0 overflow-hidden opacity-0'
: 'ml-2 w-auto opacity-100'
">
{{ label }}
</span>
</div>
</button>
<button
class="flex h-7 cursor-pointer items-center rounded text-gray-800 duration-300 ease-in-out focus:outline-none focus:transition-none focus-visible:rounded focus-visible:ring-2 focus-visible:ring-gray-400"
:class="isActive ? 'bg-white shadow-sm' : 'hover:bg-gray-100'"
@click="handleClick"
>
<div
class="flex items-center duration-300 ease-in-out"
:class="isCollapsed ? 'p-1' : 'px-2 py-1'"
>
<Tooltip :text="label" placement="right">
<slot name="icon">
<span class="grid h-5 w-6 flex-shrink-0 place-items-center">
<component :is="icon" class="h-5 w-5 stroke-1.5 text-gray-800" />
</span>
</slot>
</Tooltip>
<span
class="flex-shrink-0 text-base duration-300 ease-in-out"
:class="
isCollapsed
? 'ml-0 w-0 overflow-hidden opacity-0'
: 'ml-2 w-auto opacity-100'
"
>
{{ label }}
</span>
</div>
</button>
</template>
<script setup>
@@ -28,28 +37,28 @@ import { useRouter } from 'vue-router'
const router = useRouter()
const props = defineProps({
icon: {
type: Function,
},
label: {
type: String,
default: '',
},
to: {
type: String,
default: '',
},
isCollapsed: {
type: Boolean,
default: false,
},
icon: {
type: Function,
},
label: {
type: String,
default: '',
},
to: {
type: String,
default: '',
},
isCollapsed: {
type: Boolean,
default: false,
},
})
function handleClick() {
router.push({ name: props.to })
router.push({ name: props.to })
}
let isActive = computed(() => {
return router.currentRoute.value.name === props.to
return router.currentRoute.value.name === props.to
})
</script>

View File

@@ -5,11 +5,11 @@ import { getCachedResource } from 'frappe-ui/src/resources/resources'
export function initSocket() {
let host = window.location.hostname
let siteName = window.site_name
let siteName = window.site_name || host
let port = window.location.port ? `:${socketio_port}` : ''
let protocol = port ? 'http' : 'https'
let url = `${protocol}://${host}${port}/${siteName}`
console.log(protocol, host, port, siteName)
let socket = io(url, {
withCredentials: true,
reconnectionAttempts: 5,