Compare commits

..

2 Commits

Author SHA1 Message Date
Daan Meijer
9c270973ed chat improvements
Some checks failed
linter / quality (push) Failing after 1m3s
tests / ci (8.3) (push) Failing after 50s
tests / ci (8.4) (push) Failing after 1m4s
tests / ci (8.5) (push) Failing after 1m5s
2026-06-22 16:50:08 +02:00
Daan Meijer
de88658a48 show messages directly when sending them 2026-06-22 16:27:54 +02:00
5 changed files with 68 additions and 18 deletions

View File

@ -62,7 +62,7 @@ class DynamicController extends Controller
'dynamic' => new DynamicResource($dynamic), 'dynamic' => new DynamicResource($dynamic),
'ledgers' => LedgerResource::collection($dynamic->ledgers), 'ledgers' => LedgerResource::collection($dynamic->ledgers),
'participants' => UserResource::collection($dynamic->participants), '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' => [ 'can' => [
'update' => $request->user()->can('update', $dynamic), 'update' => $request->user()->can('update', $dynamic),
], ],
@ -73,7 +73,7 @@ class DynamicController extends Controller
{ {
$this->authorize('view', $dynamic); $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));
} }
/** /**

View File

@ -87,7 +87,7 @@ class LedgerController extends Controller
'ledger' => new LedgerResource($ledger), 'ledger' => new LedgerResource($ledger),
'mutations' => MutationResource::collection($ledger->mutations), 'mutations' => MutationResource::collection($ledger->mutations),
'participants' => UserResource::collection($dynamic->participants), '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' => [ 'can' => [
'update' => $request->user()->can('update', $ledger), 'update' => $request->user()->can('update', $ledger),
'close' => $request->user()->can('close', $ledger), 'close' => $request->user()->can('close', $ledger),
@ -99,7 +99,7 @@ class LedgerController extends Controller
{ {
$this->authorize('view', $ledger); $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));
} }
/** /**

View File

@ -14,6 +14,8 @@ class Message extends Model
/** @use HasFactory<MessageFactory> */ /** @use HasFactory<MessageFactory> */
use HasFactory; use HasFactory;
const PAGINATION_COUNT = 6;
protected $fillable = [ protected $fillable = [
'chat_id', 'chat_id',
'user_id', 'user_id',

View File

@ -2,7 +2,7 @@
import { useForm, usePage, router } from '@inertiajs/vue3'; import { useForm, usePage, router } from '@inertiajs/vue3';
import { useEcho, echoIsConfigured, configureEcho } from '@laravel/echo-vue'; import { useEcho, echoIsConfigured, configureEcho } from '@laravel/echo-vue';
import { Paperclip, Info } from '@lucide/vue'; import { Paperclip, Info } from '@lucide/vue';
import { ref, computed } from 'vue'; import { ref, computed, watch } from 'vue';
import { route } from 'ziggy-js'; import { route } from 'ziggy-js';
const props = withDefaults( const props = withDefaults(
@ -33,38 +33,85 @@ const props = withDefaults(
} | null; } | null;
}>; }>;
dynamicId: string; dynamicId: string;
ledgerId?: string | null;
initialMessages?: { initialMessages?: {
data: Array<any>; data: Array<any>;
next_page_url: string | null; next_page_url?: string | null;
links?: {
next: string | null;
} | null;
current_page?: number;
meta?: {
current_page: number;
} | null;
} | null; } | null;
}>(), }>(),
{ {
participants: () => [], participants: () => [],
initialMessages: null, 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( const messages = ref(
props.initialMessages props.initialMessages
? props.initialMessages.data.slice().reverse() ? props.initialMessages.data.slice().reverse()
: (props.chat.messages || []).slice() : (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 && getCurrentPage(newVal) === 1) {
messages.value = newVal.data.slice().reverse();
nextPageUrl.value = getNextPageUrl(newVal);
currentPageNum.value = 1;
}
},
{ deep: true }
);
watch(
() => props.chat.messages,
(newVal) => {
if (!props.initialMessages && newVal) {
messages.value = newVal.slice();
}
},
{ deep: true }
);
function loadMoreMessages() { function loadMoreMessages() {
if (!nextPageUrl.value) { if (!nextPageUrl.value) {
return; return;
} }
router.get(nextPageUrl.value, {}, { currentPageNum.value++;
preserveState: true,
preserveScroll: true, const apiRouteName = props.ledgerId ? 'dynamics.ledgers.messages' : 'dynamics.messages';
only: ['messages'], const apiParams = props.ledgerId ? [props.dynamicId, props.ledgerId] : [props.dynamicId];
onSuccess: (page) => { const url = route(apiRouteName, [...apiParams, { page: currentPageNum.value }]);
const newMessages = page.props.messages as { data: Array<any>; next_page_url: string | null };
messages.value = [...newMessages.data.reverse(), ...messages.value]; fetch(url)
nextPageUrl.value = newMessages.next_page_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--;
}); });
} }
@ -217,6 +264,7 @@ function isOwnMessage(messageUserId: number | null): boolean {
function submit() { function submit() {
form.post(route('chats.messages.store', props.chat.id), { form.post(route('chats.messages.store', props.chat.id), {
preserveScroll: true,
onSuccess: () => { onSuccess: () => {
form.reset(); form.reset();

View File

@ -275,7 +275,7 @@ function isOwnerUser(userId: number): boolean {
@open-lightbox="openLightbox" @open-lightbox="openLightbox"
/> />
<Chat :chat="dynamic.chat" :initial-messages="messages" :participants="dynamic.participants" :dynamic-id="dynamic.id" /> <Chat :chat="dynamic.chat" :initial-messages="messages" :participants="dynamic.participants" :dynamic-id="dynamic.id" :ledger-id="ledger.id" />
</div> </div>
</div> </div>