new changes
This commit is contained in:
@@ -29,17 +29,17 @@ const {
|
||||
isPending: isChatPending,
|
||||
isFetchingNextPage: isNextChatPagePending,
|
||||
hasNextPage: hasMoreChat,
|
||||
fetchNextPage: loadMoreChat
|
||||
fetchNextPage: loadMoreChat,
|
||||
} = useGetChat(id, isOpen);
|
||||
const isCreateMessagePending = useIsMutating({
|
||||
mutationKey: [MUTATION_KEYS.create_chat]
|
||||
mutationKey: [MUTATION_KEYS.create_chat],
|
||||
});
|
||||
|
||||
const canLoadMoreChat = ref(false);
|
||||
|
||||
const isChatScrollLocked = useScrollLock(chatContainerEl);
|
||||
const { y: chatContainerScrollY } = useScroll(chatContainerEl, {
|
||||
behavior: "smooth"
|
||||
behavior: "smooth",
|
||||
});
|
||||
|
||||
useInfiniteScroll(
|
||||
@@ -56,11 +56,11 @@ useInfiniteScroll(
|
||||
distance: 10,
|
||||
direction: "top",
|
||||
throttle: 1000,
|
||||
canLoadMore: () => canLoadMoreChat.value
|
||||
canLoadMore: () => canLoadMoreChat.value,
|
||||
}
|
||||
);
|
||||
|
||||
// method
|
||||
// methods
|
||||
|
||||
const scrollToBottom = () => {
|
||||
chatContainerScrollY.value = chatContainerEl.value?.scrollHeight ?? 0;
|
||||
@@ -116,7 +116,7 @@ whenever(
|
||||
}, 2000);
|
||||
},
|
||||
{
|
||||
once: true
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
@@ -131,16 +131,15 @@ whenever(
|
||||
|
||||
<template v-if="isLoggedIn">
|
||||
<Transition name="zoom" mode="out-in">
|
||||
|
||||
<div
|
||||
v-if="!isChatPending"
|
||||
class="p-4.5 h-full flex flex-col justify-between gap-4"
|
||||
>
|
||||
<div
|
||||
:style="{
|
||||
maskImage:
|
||||
'linear-gradient(to top, transparent, black 5%, black, black)',
|
||||
}"
|
||||
maskImage:
|
||||
'linear-gradient(to top, transparent, black 5%, black, black)',
|
||||
}"
|
||||
class="hide-scrollbar flex flex-col py-7 gap-6 h-full overflow-y-auto"
|
||||
ref="chatContainerEl"
|
||||
>
|
||||
@@ -148,7 +147,10 @@ whenever(
|
||||
v-if="hasMoreChat"
|
||||
class="py-2 flex items-center justify-center"
|
||||
>
|
||||
<Icon name="svg-spinners:3-dots-fade" size="24" />
|
||||
<Icon
|
||||
name="svg-spinners:3-dots-fade"
|
||||
size="24"
|
||||
/>
|
||||
</div>
|
||||
<ChatMessage
|
||||
v-for="(message, index) in chatMessages"
|
||||
@@ -173,7 +175,6 @@ whenever(
|
||||
<ChatInput />
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="w-full h-full flex items-center justify-center absolute inset-0"
|
||||
@@ -182,7 +183,10 @@ whenever(
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
<div class="text-black p-4.5 size-full flex justify-center items-center" v-else>
|
||||
<div
|
||||
class="text-black p-4.5 size-full flex justify-center items-center"
|
||||
v-else
|
||||
>
|
||||
Please sign in first
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
// state
|
||||
|
||||
const isOpen = ref(false);
|
||||
|
||||
// method
|
||||
// methods
|
||||
|
||||
const closeChat = () => isOpen.value = false;
|
||||
const closeChat = () => (isOpen.value = false);
|
||||
|
||||
// provide-inject
|
||||
|
||||
provide("isOpen", {
|
||||
isOpen,
|
||||
closeChat
|
||||
closeChat,
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<button
|
||||
v-if="!isOpen"
|
||||
@click="isOpen = !isOpen"
|
||||
@@ -32,5 +29,4 @@ provide("isOpen", {
|
||||
</button>
|
||||
|
||||
<ChatBoxContainer :isOpen="isOpen" />
|
||||
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
// types
|
||||
|
||||
import AiLoading from "~/components/product/ChatBox/AiLoading.vue";
|
||||
@@ -12,12 +11,12 @@ const { $queryClient: queryClient } = useNuxtApp();
|
||||
|
||||
const { addToast } = useToast();
|
||||
|
||||
const { mutateAsync: createMessage, isPending: isCreatingMessage } = useCreateChatMessage(queryClient);
|
||||
const { mutateAsync: createMessage, isPending: isCreatingMessage } =
|
||||
useCreateChatMessage(queryClient);
|
||||
|
||||
const chatInputEl = ref<HTMLInputElement | null>(null);
|
||||
|
||||
|
||||
// method
|
||||
// methods
|
||||
|
||||
const sendMessage = async () => {
|
||||
const value = chatInputEl.value!.value;
|
||||
@@ -28,20 +27,18 @@ const sendMessage = async () => {
|
||||
|
||||
await createMessage({
|
||||
new_message: value,
|
||||
productId: 1
|
||||
productId: 1,
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
addToast({
|
||||
message: "مشکلی پیش آمده",
|
||||
options: {
|
||||
status: "error"
|
||||
}
|
||||
status: "error",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -71,12 +68,20 @@ const sendMessage = async () => {
|
||||
|
||||
<form
|
||||
class="transition-all duration-200 relative flex items-center gap-4 w-full shadow-sm rounded-full h-[56px] border pe-3 ps-4"
|
||||
:class="isCreatingMessage ? 'border-transparent shadow-black/10 bg-white/85 backdrop-blur-xl' : 'border-slate-200 shadow-transparent bg-white'"
|
||||
:class="
|
||||
isCreatingMessage
|
||||
? 'border-transparent shadow-black/10 bg-white/85 backdrop-blur-xl'
|
||||
: 'border-slate-200 shadow-transparent bg-white'
|
||||
"
|
||||
>
|
||||
<input
|
||||
ref="chatInputEl"
|
||||
:disabled="isCreatingMessage"
|
||||
:placeholder="isCreatingMessage ? 'دارم فکر میکنم...' : 'سوال خود را بپرسید'"
|
||||
:placeholder="
|
||||
isCreatingMessage
|
||||
? 'دارم فکر میکنم...'
|
||||
: 'سوال خود را بپرسید'
|
||||
"
|
||||
type="text"
|
||||
name="text"
|
||||
class="focus:outline-none h-full typo-p-sm w-full border-none"
|
||||
@@ -89,13 +94,21 @@ const sendMessage = async () => {
|
||||
:class="isCreatingMessage ? 'bg-transparent' : 'bg-black'"
|
||||
>
|
||||
<TransitionGroup name="fade-down">
|
||||
<AiLoading v-if="isCreatingMessage" circle :size="75" class="mb-1" />
|
||||
<Icon v-else name="iconamoon:send-light" class="absolute rotate-180 **:stroke-white" />
|
||||
<AiLoading
|
||||
v-if="isCreatingMessage"
|
||||
circle
|
||||
:size="75"
|
||||
class="mb-1"
|
||||
/>
|
||||
<Icon
|
||||
v-else
|
||||
name="iconamoon:send-light"
|
||||
class="absolute rotate-180 **:stroke-white"
|
||||
/>
|
||||
</TransitionGroup>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -303,4 +316,4 @@ const sendMessage = async () => {
|
||||
transform: translate(-50%, -50%) rotate(443deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
id: number,
|
||||
reverse?: boolean,
|
||||
content: string,
|
||||
isLast?: boolean,
|
||||
loadingContent?: boolean
|
||||
}
|
||||
id: number;
|
||||
reverse?: boolean;
|
||||
content: string;
|
||||
isLast?: boolean;
|
||||
loadingContent?: boolean;
|
||||
};
|
||||
|
||||
// props
|
||||
|
||||
@@ -23,20 +22,24 @@ const emit = defineEmits(["textUpdate"]);
|
||||
|
||||
const { $gsap: gsap } = useNuxtApp();
|
||||
|
||||
// method
|
||||
// methods
|
||||
|
||||
const showMessage = () => {
|
||||
gsap.fromTo(`#message-container-${id.value}`, {
|
||||
rotateX: -50,
|
||||
translateY: -40,
|
||||
opacity: 0
|
||||
}, {
|
||||
rotateX: 0,
|
||||
translateY: 0,
|
||||
opacity: 1,
|
||||
duration: 0.5,
|
||||
ease: "expo.out"
|
||||
});
|
||||
gsap.fromTo(
|
||||
`#message-container-${id.value}`,
|
||||
{
|
||||
rotateX: -50,
|
||||
translateY: -40,
|
||||
opacity: 0,
|
||||
},
|
||||
{
|
||||
rotateX: 0,
|
||||
translateY: 0,
|
||||
opacity: 1,
|
||||
duration: 0.5,
|
||||
ease: "expo.out",
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// lifecycle
|
||||
@@ -47,19 +50,22 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
if (reverse.value && isLast.value) {
|
||||
gsap.fromTo(`#chat-message-content-${id.value}`, {
|
||||
text: "",
|
||||
duration: 2.5,
|
||||
ease: "none"
|
||||
}, {
|
||||
text: { value: content.value, rtl: false },
|
||||
duration: 2.5,
|
||||
ease: "none",
|
||||
onUpdate: () => emit("textUpdate")
|
||||
});
|
||||
gsap.fromTo(
|
||||
`#chat-message-content-${id.value}`,
|
||||
{
|
||||
text: "",
|
||||
duration: 2.5,
|
||||
ease: "none",
|
||||
},
|
||||
{
|
||||
text: { value: content.value, rtl: false },
|
||||
duration: 2.5,
|
||||
ease: "none",
|
||||
onUpdate: () => emit("textUpdate"),
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -81,7 +87,11 @@ onMounted(() => {
|
||||
</div>
|
||||
<div
|
||||
class="rounded-150 px-4 py-3"
|
||||
:class="reverse ? 'bg-slate-100 text-slate-600' : 'bg-black text-white'"
|
||||
:class="
|
||||
reverse
|
||||
? 'bg-slate-100 text-slate-600'
|
||||
: 'bg-black text-white'
|
||||
"
|
||||
>
|
||||
<p
|
||||
v-if="!loadingContent"
|
||||
@@ -91,12 +101,8 @@ onMounted(() => {
|
||||
{{ content }}
|
||||
</p>
|
||||
|
||||
<Icon
|
||||
v-else
|
||||
name="svg-spinners:3-dots-bounce"
|
||||
size="20"
|
||||
/>
|
||||
<Icon v-else name="svg-spinners:3-dots-bounce" size="20" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user