diff --git a/app/Events/ChatMessageDeleted.php b/app/Events/ChatMessageDeleted.php index c511060..a84b456 100644 --- a/app/Events/ChatMessageDeleted.php +++ b/app/Events/ChatMessageDeleted.php @@ -32,8 +32,10 @@ public function __construct(int $messageId, ?string $deletedAt = null) */ public function broadcastOn(): array { + $channel = app()->environment('production') ? 'global-chat' : app()->environment().'.global-chat'; + return [ - new Channel('global-chat'), + new Channel($channel), ]; } diff --git a/app/Events/ChatMessageReacted.php b/app/Events/ChatMessageReacted.php index 8f6262e..eb888cc 100644 --- a/app/Events/ChatMessageReacted.php +++ b/app/Events/ChatMessageReacted.php @@ -32,8 +32,10 @@ public function __construct(int $messageId, array $reactions) */ public function broadcastOn(): array { + $channel = app()->environment('production') ? 'global-chat' : app()->environment().'.global-chat'; + return [ - new Channel('global-chat'), + new Channel($channel), ]; } diff --git a/app/Events/ChatMessageSent.php b/app/Events/ChatMessageSent.php index de7c362..debc56a 100644 --- a/app/Events/ChatMessageSent.php +++ b/app/Events/ChatMessageSent.php @@ -50,8 +50,10 @@ public function __construct(ChatMessage $chatMessage) */ public function broadcastOn(): array { + $channel = app()->environment('production') ? 'global-chat' : app()->environment().'.global-chat'; + return [ - new Channel('global-chat'), + new Channel($channel), ]; } diff --git a/app/Events/ChatSettingsUpdated.php b/app/Events/ChatSettingsUpdated.php index 4f917fb..cd0ec48 100644 --- a/app/Events/ChatSettingsUpdated.php +++ b/app/Events/ChatSettingsUpdated.php @@ -38,8 +38,10 @@ public function __construct() */ public function broadcastOn(): array { + $channel = app()->environment('production') ? 'global-chat' : app()->environment().'.global-chat'; + return [ - new Channel('global-chat'), + new Channel($channel), ]; } diff --git a/app/Http/Controllers/ChatController.php b/app/Http/Controllers/ChatController.php index cf50ac3..4b4c089 100644 --- a/app/Http/Controllers/ChatController.php +++ b/app/Http/Controllers/ChatController.php @@ -79,6 +79,8 @@ public function index(Request $request) $allowedEmojis = (array) AppSetting::get('global_chat_allowed_emojis', ['👍', '❤️', '🔥', '😂', '🎉', '😮', '😢', '👏']); + $channelName = app()->environment('production') ? 'global-chat' : app()->environment().'.global-chat'; + if (! $request->wantsJson() && ! $request->is('api/*')) { return Inertia::render('Chat/Index', [ 'chatState' => [ @@ -92,6 +94,7 @@ public function index(Request $request) 'can_delete' => (bool) $user?->can('manage chat'), 'reaction_emojis' => $allowedEmojis, 'messages' => $messages, + 'channel_name' => $channelName, 'pusher_key' => config('broadcasting.connections.pusher.key'), 'pusher_cluster' => config('broadcasting.connections.pusher.options.cluster', 'ap2'), ], @@ -109,6 +112,7 @@ public function index(Request $request) 'can_delete' => (bool) $user?->can('manage chat'), 'reaction_emojis' => $allowedEmojis, 'messages' => $messages, + 'channel_name' => $channelName, 'pusher_key' => config('broadcasting.connections.pusher.key'), 'pusher_cluster' => config('broadcasting.connections.pusher.options.cluster', 'ap2'), ]); @@ -362,14 +366,19 @@ public function toggleReaction(Request $request, ChatMessage $message) ], 422); } - $existing = $message->reactions() + $existingReaction = $message->reactions() ->where('user_id', $user->id) - ->where('emoji', $emoji) ->first(); - if ($existing) { - $existing->delete(); + if ($existingReaction && $existingReaction->emoji === $emoji) { + // Same emoji clicked again -> toggle off / remove + $existingReaction->delete(); } else { + // Different emoji or no previous reaction -> remove existing and add new + if ($existingReaction) { + $existingReaction->delete(); + } + $message->reactions()->create([ 'user_id' => $user->id, 'emoji' => $emoji, diff --git a/app/Http/Requests/User/StoreUserRequest.php b/app/Http/Requests/User/StoreUserRequest.php index 6c40ad0..ca451fe 100644 --- a/app/Http/Requests/User/StoreUserRequest.php +++ b/app/Http/Requests/User/StoreUserRequest.php @@ -25,6 +25,7 @@ public function rules(): array return [ 'name' => ['required', 'string', 'max:255'], + 'username' => ['nullable', 'string', 'min:3', 'max:30', 'regex:/^[a-zA-Z0-9_]+$/', 'unique:users,username'], 'email' => ['required', 'email', 'unique:users,email'], 'role' => ['nullable', 'string'], 'permissions' => ['nullable', 'array'], diff --git a/app/Http/Requests/User/UpdateUserRequest.php b/app/Http/Requests/User/UpdateUserRequest.php index 2c6a2ab..8a2d28b 100644 --- a/app/Http/Requests/User/UpdateUserRequest.php +++ b/app/Http/Requests/User/UpdateUserRequest.php @@ -26,6 +26,15 @@ public function rules(): array $rules = [ 'name' => ['sometimes', 'string', 'max:255'], + 'username' => [ + 'sometimes', + 'nullable', + 'string', + 'min:3', + 'max:30', + 'regex:/^[a-zA-Z0-9_]+$/', + 'unique:users,username,'.$user->id, + ], 'email' => ['sometimes', 'email', 'unique:users,email,'.$user->id], 'file' => ['sometimes', 'nullable', 'image', 'max:2048'], 'about' => ['sometimes', 'nullable', 'string'], diff --git a/resources/js/components/ChatBanModal.vue b/resources/js/components/ChatBanModal.vue index 7f296d6..6eb9fad 100644 --- a/resources/js/components/ChatBanModal.vue +++ b/resources/js/components/ChatBanModal.vue @@ -24,11 +24,13 @@ const banUntilInput = ref(''); const isSubmittingBan = ref(false); const banPresets = [ - { label: '1 Hour', hours: 1 }, - { label: '24 Hours', hours: 24 }, - { label: '3 Days', hours: 72 }, - { label: '7 Days', hours: 168 }, - { label: '30 Days', hours: 720 }, + { label: '3 Mins', minutes: 3 }, + { label: '5 Mins', minutes: 5 }, + { label: '15 Mins', minutes: 15 }, + { label: '1 Hour', minutes: 60 }, + { label: '24 Hours', minutes: 24 * 60 }, + { label: '3 Days', minutes: 3 * 24 * 60 }, + { label: '7 Days', minutes: 7 * 24 * 60 }, ]; const formatForDatetimeLocal = (dateString?: string | null) => { @@ -70,8 +72,8 @@ const formatDate = (isoString?: string | null) => { } }; -const applyBanPreset = (hours: number) => { - const futureDate = new Date(Date.now() + hours * 60 * 60 * 1000); +const applyBanPreset = (minutes: number) => { + const futureDate = new Date(Date.now() + minutes * 60 * 1000); banUntilInput.value = formatForDatetimeLocal(futureDate.toISOString()); }; @@ -209,7 +211,7 @@ const submitBan = () => { v-for="preset in banPresets" :key="preset.label" type="button" - @click="applyBanPreset(preset.hours)" + @click="applyBanPreset(preset.minutes)" class="cursor-pointer rounded-lg border border-slate-200 bg-slate-50 px-2.5 py-1 text-xs font-semibold text-slate-700 transition hover:border-rose-200 hover:bg-rose-50 hover:text-rose-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-rose-950/40 dark:hover:text-rose-400" > +{{ preset.label }} diff --git a/resources/js/components/Footer.vue b/resources/js/components/Footer.vue index 48ef995..4b48af3 100644 --- a/resources/js/components/Footer.vue +++ b/resources/js/components/Footer.vue @@ -75,17 +75,6 @@ const isFullFooter = computed(() => { - -
{
-

- A concern of - Tajim -

-

diff --git a/resources/js/components/admin/UserRow.vue b/resources/js/components/admin/UserRow.vue index 114087d..c3c9f31 100644 --- a/resources/js/components/admin/UserRow.vue +++ b/resources/js/components/admin/UserRow.vue @@ -1,6 +1,8 @@ @@ -1423,17 +1447,25 @@ onUnmounted(() => {

- -
+ +
- + - + - + - +
- -
+ +
+
- +
+ +
+ @ + +
+

+ {{ form.errors.username }} +

+
+ +
- -
-
-
-

- - Global Chat Moderation -

-

- Restrict this user from sending messages in the - Global Student Chat until a specified date & time. -

-
- - - - Chat Banned - - - - Active - -
- - -
- -
- - - -
-
- - -
- - -

- {{ form.errors.chat_banned_until }} -

-

- Leave blank if the user should not be banned. -

-
-
-
diff --git a/resources/js/pages/platform/AboutUs.vue b/resources/js/pages/platform/AboutUs.vue index 92851bd..4e4cc1c 100644 --- a/resources/js/pages/platform/AboutUs.vue +++ b/resources/js/pages/platform/AboutUs.vue @@ -1,14 +1,10 @@