View.vue 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. <script setup>
  2. import AppLayout from "@/Layouts/AppLayout.vue";
  3. import {router, useForm} from "@inertiajs/vue3";
  4. import ChatInput from "@/Components/ChatInput.vue";
  5. import {useEcho} from "@laravel/echo-vue";
  6. import Message from "@/Components/Message.vue";
  7. import {nextTick, onMounted} from "vue";
  8. const {chat} = defineProps({chat: {type: Object}})
  9. const form = useForm({message: null})
  10. const submit = () => {
  11. form.post(route('chats.messages.store', chat.id), {
  12. preserveScroll: true,
  13. onSuccess: () => {
  14. scroll()
  15. form.reset()
  16. }
  17. })
  18. }
  19. useEcho(`App.Models.Chat.${chat.id}`, '.MessageCreated', (e) => {
  20. if (e?.data?.role !== 'human') reload()
  21. });
  22. useEcho(`App.Models.Chat.${chat.id}`, '.MessageUpdated', () => reload());
  23. const reload = () => {
  24. router.reload({preserveScroll: true, only: ['chat'], onSuccess: () => scroll()})
  25. }
  26. const scroll = () => {
  27. window.scrollBy({
  28. top: document.body.scrollHeight,
  29. behavior: 'smooth'
  30. });
  31. }
  32. onMounted(() => nextTick(() => scroll()))
  33. </script>
  34. <template>
  35. <AppLayout page-class="">
  36. <div class="max-w-5xl mx-auto flex flex-col h-full">
  37. <div class="flex-1 my-2">
  38. <div class="space-y-4">
  39. <div v-for="message in chat.messages">
  40. <div v-if="message.role === 'human'" class="flex">
  41. <div class="ml-auto border bg-card text-card-foreground p-2 rounded-lg max-w-[75%] text-sm"
  42. v-html="message.content.replaceAll('\n', '<br />')"></div>
  43. </div>
  44. <Message v-else :message="message"/>
  45. </div>
  46. </div>
  47. </div>
  48. <div class="shrink-0 sticky bottom-2 mt-100">
  49. <!-- // TODO: If Last Message Not In Canceled Failed or Completed -->
  50. <ChatInput v-model="form.message" :disabled="form.processing" @submit="submit"/>
  51. </div>
  52. </div>
  53. </AppLayout>
  54. </template>
  55. <style scoped>
  56. </style>