better system messages, display name relocation
linter / quality (push) Failing after 1m6s
tests / ci (8.3) (push) Failing after 54s
tests / ci (8.4) (push) Failing after 1m8s
tests / ci (8.5) (push) Failing after 1m13s

This commit is contained in:
Daan Meijer
2026-06-17 11:00:35 +02:00
parent 0fee3c1972
commit c6d482e3de
22 changed files with 689 additions and 204 deletions
+6 -1
View File
@@ -56,7 +56,12 @@ class DynamicController extends Controller
'ledgers.media',
'participants' => fn($query) => $query->withPivot('display_name'),
'chat.messages.user',
'chat.messages.media'
'chat.messages.media',
'chat.messages.subject' => function ($morphTo) {
$morphTo->morphWith([
\App\Models\Mutation::class => ['ledger'],
]);
}
]);
$isOwner = $dynamic->participants()
@@ -116,7 +116,7 @@ class DynamicInvitationController extends Controller
// Log to Dynamic chat activity log!
$dynamic->chat->messages()->create([
'user_id' => null,
'content' => "{$request->user()->name} joined the Dynamic as a " . strtoupper($invitation->role),
'content' => "<user:{$request->user()->id}> joined the Dynamic as a " . strtoupper($invitation->role),
'subject_id' => $request->user()->id,
'subject_type' => \App\Models\User::class,
]);
+6 -1
View File
@@ -74,7 +74,12 @@ class LedgerController extends Controller
'mutations.user',
'mutations.media',
'mutations.chat.messages.user',
'mutations.chat.messages.media'
'mutations.chat.messages.media',
'mutations.chat.messages.subject' => function ($morphTo) {
$morphTo->morphWith([
\App\Models\Mutation::class => ['ledger'],
]);
}
]);
$isOwner = $dynamic->participants()
+12 -30
View File
@@ -67,30 +67,6 @@ class MutationController extends Controller
return $mutation;
});
// Log to Mutation and Dynamic chats
$user = $request->user();
$mutationMsg = $mutation->chat->messages()->create([
'user_id' => $user->id,
'content' => $status === 'approved'
? "System: Entry was created by {$user->name}."
: "System: Suggestion was created by {$user->name}.",
]);
broadcast(new \App\Events\MessageSent($mutationMsg));
if ($status === 'approved') {
$dynamicMsg = $dynamic->chat->messages()->create([
'user_id' => $user->id,
'content' => "System: {$user->name} added entry \"{$mutation->description}\" for " . ($mutation->amount >= 0 ? '+' : '') . "{$mutation->amount} points on \"{$ledger->name}\" ledger.",
]);
} else {
$dynamicMsg = $dynamic->chat->messages()->create([
'user_id' => $user->id,
'content' => "System: {$user->name} suggested \"{$mutation->description}\" for " . ($mutation->amount >= 0 ? '+' : '') . "{$mutation->amount} points on \"{$ledger->name}\" ledger.",
]);
}
broadcast(new \App\Events\MessageSent($dynamicMsg));
// Broadcast the real-time creation event!
broadcast(new \App\Events\MutationCreated($mutation));
@@ -151,20 +127,26 @@ class MutationController extends Controller
$statusText = strtoupper($newStatus);
$mutationMsg = $mutation->chat->messages()->create([
'user_id' => $user->id,
'content' => "System: Suggestion was {$statusText} by {$user->name}.",
'user_id' => null,
'content' => "Suggestion was {$statusText} by <user:{$user->id}>.",
'subject_id' => $mutation->id,
'subject_type' => Mutation::class,
]);
broadcast(new \App\Events\MessageSent($mutationMsg));
if ($newStatus === 'approved') {
$dynamicMsg = $dynamic->chat->messages()->create([
'user_id' => $user->id,
'content' => "System: {$user->name} APPROVED the suggestion \"{$mutation->description}\" for " . ($mutation->amount >= 0 ? '+' : '') . "{$mutation->amount} points on \"{$ledger->name}\" ledger.",
'user_id' => null,
'content' => "<user:{$user->id}> APPROVED the suggestion \"{$mutation->description}\" for " . ($mutation->amount >= 0 ? '+' : '') . "{$mutation->amount} points on \"{$ledger->name}\" ledger.",
'subject_id' => $mutation->id,
'subject_type' => Mutation::class,
]);
} else {
$dynamicMsg = $dynamic->chat->messages()->create([
'user_id' => $user->id,
'content' => "System: {$user->name} REJECTED the suggestion \"{$mutation->description}\" on \"{$ledger->name}\" ledger.",
'user_id' => null,
'content' => "<user:{$user->id}> REJECTED the suggestion \"{$mutation->description}\" on \"{$ledger->name}\" ledger.",
'subject_id' => $mutation->id,
'subject_type' => Mutation::class,
]);
}
broadcast(new \App\Events\MessageSent($dynamicMsg));
@@ -3,10 +3,40 @@
namespace App\Http\Controllers;
use App\Models\Dynamic;
use App\Models\User;
use Illuminate\Http\Request;
use Inertia\Inertia;
class ParticipantController extends Controller
{
public function show(Request $request, Dynamic $dynamic, User $user)
{
// Ensure both the authenticated user and the target user are in the dynamic
if (!$dynamic->participants()->where('user_id', $request->user()->id)->exists()) {
abort(403);
}
$participant = $dynamic->participants()->where('user_id', $user->id)->firstOrFail();
$mutations = $user->mutations()
->whereHas('ledger', fn($query) => $query->where('dynamic_id', $dynamic->id))
->with('ledger')
->latest('id')
->take(10)
->get();
return Inertia::render('Participants/Show', [
'dynamic' => $dynamic,
'participant' => [
'id' => $user->id,
'name' => $user->name,
'display_name' => $participant->pivot->display_name,
'role' => $participant->pivot->role,
],
'mutations' => $mutations,
]);
}
public function update(Request $request, Dynamic $dynamic)
{
$request->validate([
@@ -19,9 +19,14 @@ class ProfileController extends Controller
*/
public function edit(Request $request): Response
{
$dynamics = $request->user()->dynamics()
->withPivot('display_name')
->get();
return Inertia::render('settings/Profile', [
'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail,
'status' => $request->session()->get('status'),
'dynamics' => $dynamics,
]);
}
+33
View File
@@ -46,6 +46,39 @@ class Mutation extends Model
{
static::created(function (Mutation $mutation) {
$mutation->chat()->create([]);
// Create system messages automatically!
$user = $mutation->user;
$ledger = $mutation->ledger;
$dynamic = $ledger->dynamic;
$status = $mutation->status;
$mutationMsg = $mutation->chat->messages()->create([
'user_id' => null,
'content' => $status === 'approved'
? "Entry was created by <user:{$user->id}>."
: "Suggestion was created by <user:{$user->id}>.",
'subject_id' => $mutation->id,
'subject_type' => Mutation::class,
]);
broadcast(new \App\Events\MessageSent($mutationMsg));
if ($status === 'approved') {
$dynamicMsg = $dynamic->chat->messages()->create([
'user_id' => null,
'content' => "<user:{$user->id}> added entry \"{$mutation->description}\" for " . ($mutation->amount >= 0 ? '+' : '') . "{$mutation->amount} points on \"{$ledger->name}\" ledger.",
'subject_id' => $mutation->id,
'subject_type' => Mutation::class,
]);
} else {
$dynamicMsg = $dynamic->chat->messages()->create([
'user_id' => null,
'content' => "<user:{$user->id}> suggested \"{$mutation->description}\" for " . ($mutation->amount >= 0 ? '+' : '') . "{$mutation->amount} points on \"{$ledger->name}\" ledger.",
'subject_id' => $mutation->id,
'subject_type' => Mutation::class,
]);
}
broadcast(new \App\Events\MessageSent($dynamicMsg));
});
}
}
+18 -3
View File
@@ -52,20 +52,35 @@ class ActivityService
{
if ($entity instanceof Dynamic) {
$chatId = $entity->chat->id;
$dynamic = $entity;
} elseif ($entity instanceof Ledger) {
$chatId = $entity->dynamic->chat->id;
$dynamic = $entity->dynamic;
$chatId = $dynamic->chat->id;
} else {
return [];
}
$participants = $dynamic->participants()->withPivot('display_name')->get();
$participantsMap = $participants->reduce(function ($acc, $p) {
$acc[$p->id] = $p->pivot->display_name ?? $p->name;
return $acc;
}, []);
$messages = Message::where('chat_id', $chatId)
->with(['user', 'subject'])
->latest()
->get();
return $messages->map(function ($message) {
return $messages->map(function ($message) use ($participantsMap) {
$messageData = $message->toArray();
$messageData['url'] = $this->getUrlForMessage($message);
// Resolve <user:id> placeholders to actual names/display names
$messageData['content'] = preg_replace_callback('/<user:(\d+)>/', function ($matches) use ($participantsMap) {
$userId = $matches[1];
return $participantsMap[$userId] ?? "User #{$userId}";
}, $message->content);
return $messageData;
})->all();
}
@@ -116,7 +131,7 @@ class ActivityService
'url' => $url,
'unread_count' => count($unread),
'context_activities' => $context,
'new_activities' => $unread,
'new_activities' => array_reverse($unread),
];
}
}