From 9c270973ed0fad05b312fdef4c844c68487e3896 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Mon, 22 Jun 2026 16:50:08 +0200 Subject: [PATCH] chat improvements --- app/Http/Controllers/DynamicController.php | 4 +- app/Http/Controllers/LedgerController.php | 4 +- app/Models/Message.php | 2 + resources/js/components/Chat.vue | 55 ++++++++++++++++------ resources/js/pages/Ledgers/Show.vue | 2 +- 5 files changed, 48 insertions(+), 19 deletions(-) diff --git a/app/Http/Controllers/DynamicController.php b/app/Http/Controllers/DynamicController.php index da2d55a..4f42dc1 100644 --- a/app/Http/Controllers/DynamicController.php +++ b/app/Http/Controllers/DynamicController.php @@ -62,7 +62,7 @@ class DynamicController extends Controller 'dynamic' => new DynamicResource($dynamic), 'ledgers' => LedgerResource::collection($dynamic->ledgers), 'participants' => UserResource::collection($dynamic->participants), - 'messages' => MessageResource::collection($dynamic->chat->messages()->with(['user', 'media'])->latest()->paginate(10)), + 'messages' => MessageResource::collection($dynamic->chat->messages()->with(['user', 'media'])->latest()->paginate(\App\Models\Message::PAGINATION_COUNT)), 'can' => [ 'update' => $request->user()->can('update', $dynamic), ], @@ -73,7 +73,7 @@ class DynamicController extends Controller { $this->authorize('view', $dynamic); - return MessageResource::collection($dynamic->chat->messages()->with(['user', 'media'])->latest()->paginate(10)); + return MessageResource::collection($dynamic->chat->messages()->with(['user', 'media'])->latest()->paginate(\App\Models\Message::PAGINATION_COUNT)); } /** diff --git a/app/Http/Controllers/LedgerController.php b/app/Http/Controllers/LedgerController.php index 6275250..3d5ebc6 100644 --- a/app/Http/Controllers/LedgerController.php +++ b/app/Http/Controllers/LedgerController.php @@ -87,7 +87,7 @@ class LedgerController extends Controller 'ledger' => new LedgerResource($ledger), 'mutations' => MutationResource::collection($ledger->mutations), 'participants' => UserResource::collection($dynamic->participants), - 'messages' => MessageResource::collection($dynamic->chat->messages()->with(['user', 'media'])->latest()->paginate(10)), + 'messages' => MessageResource::collection($dynamic->chat->messages()->with(['user', 'media'])->latest()->paginate(\App\Models\Message::PAGINATION_COUNT)), 'can' => [ 'update' => $request->user()->can('update', $ledger), 'close' => $request->user()->can('close', $ledger), @@ -99,7 +99,7 @@ class LedgerController extends Controller { $this->authorize('view', $ledger); - return MessageResource::collection($dynamic->chat->messages()->with(['user', 'media'])->latest()->paginate(10)); + return MessageResource::collection($dynamic->chat->messages()->with(['user', 'media'])->latest()->paginate(\App\Models\Message::PAGINATION_COUNT)); } /** diff --git a/app/Models/Message.php b/app/Models/Message.php index 471f02c..b6cc279 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -14,6 +14,8 @@ class Message extends Model /** @use HasFactory */ use HasFactory; + const PAGINATION_COUNT = 6; + protected $fillable = [ 'chat_id', 'user_id', diff --git a/resources/js/components/Chat.vue b/resources/js/components/Chat.vue index a0b13ca..03e2c95 100644 --- a/resources/js/components/Chat.vue +++ b/resources/js/components/Chat.vue @@ -33,30 +33,49 @@ const props = withDefaults( } | null; }>; dynamicId: string; + ledgerId?: string | null; initialMessages?: { data: Array; - next_page_url: string | null; + next_page_url?: string | null; + links?: { + next: string | null; + } | null; + current_page?: number; + meta?: { + current_page: number; + } | null; } | null; }>(), { participants: () => [], initialMessages: null, + ledgerId: null, } ); +const getNextPageUrl = (paginator: any) => { + return paginator?.links?.next ?? paginator?.next_page_url ?? null; +}; + +const getCurrentPage = (paginator: any) => { + return paginator?.meta?.current_page ?? paginator?.current_page ?? 1; +}; + const messages = ref( props.initialMessages ? props.initialMessages.data.slice().reverse() : (props.chat.messages || []).slice() ); -const nextPageUrl = ref(props.initialMessages?.next_page_url || null); +const nextPageUrl = ref(getNextPageUrl(props.initialMessages)); +const currentPageNum = ref(1); watch( () => props.initialMessages, (newVal) => { - if (newVal) { + if (newVal && getCurrentPage(newVal) === 1) { messages.value = newVal.data.slice().reverse(); - nextPageUrl.value = newVal.next_page_url; + nextPageUrl.value = getNextPageUrl(newVal); + currentPageNum.value = 1; } }, { deep: true } @@ -77,16 +96,23 @@ function loadMoreMessages() { return; } - router.get(nextPageUrl.value, {}, { - preserveState: true, - preserveScroll: true, - only: ['messages'], - onSuccess: (page) => { - const newMessages = page.props.messages as { data: Array; next_page_url: string | null }; - messages.value = [...newMessages.data.reverse(), ...messages.value]; - nextPageUrl.value = newMessages.next_page_url; - }, - }); + currentPageNum.value++; + + const apiRouteName = props.ledgerId ? 'dynamics.ledgers.messages' : 'dynamics.messages'; + const apiParams = props.ledgerId ? [props.dynamicId, props.ledgerId] : [props.dynamicId]; + const url = route(apiRouteName, [...apiParams, { page: currentPageNum.value }]); + + fetch(url) + .then((res) => res.json()) + .then((json) => { + const data = json?.data || []; + messages.value = [...data.slice().reverse(), ...messages.value]; + nextPageUrl.value = getNextPageUrl(json); + }) + .catch((err) => { + console.error('Failed to load older messages:', err); + currentPageNum.value--; + }); } if (!echoIsConfigured()) { @@ -238,6 +264,7 @@ function isOwnMessage(messageUserId: number | null): boolean { function submit() { form.post(route('chats.messages.store', props.chat.id), { + preserveScroll: true, onSuccess: () => { form.reset(); diff --git a/resources/js/pages/Ledgers/Show.vue b/resources/js/pages/Ledgers/Show.vue index 58f0b5e..1ab0a9b 100644 --- a/resources/js/pages/Ledgers/Show.vue +++ b/resources/js/pages/Ledgers/Show.vue @@ -275,7 +275,7 @@ function isOwnerUser(userId: number): boolean { @open-lightbox="openLightbox" /> - +