Files
nnterp-react-admin/app/Http/Controllers/MessengerController.php
2026-03-13 20:49:46 +08:00

518 lines
20 KiB
PHP

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Inertia\Inertia;
use App\Models\User;
use App\Models\ChMessage as Message;
use App\Models\ChFavorite as Favorite;
use App\Models\ChPinned as Pinned;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Carbon\Carbon;
use App\Events\MessageSent;
use App\Events\UserOnline;
use App\Events\UserOffline;
class MessengerController extends Controller
{
public function index(Request $request)
{
$user = Auth::user();
if ($user->can('manage-messenger')) {
// Get all users except current user with their last message and online status
$userQuery = User::where('id', '!=', $user->id);
// Filter by user permissions
if ($user->type === 'superadmin') {
$userQuery->where('creator_id', creatorId());
} elseif ($user->can('manage-any-users')) {
// Company or user with manage-any-users can see own + team users
$userQuery->where('created_by', creatorId());
} elseif ($user->can('manage-own-users')) {
// Company or user with manage-own-users can see own users
$userQuery->where('creator_id', $user->id);
} else {
// Default: only own users
$userQuery->where('id', creatorId());
}
$users = $userQuery->select('id', 'name', 'email', 'avatar', 'active_status')
->get()
->map(function ($chatUser) use ($user) {
$lastMessage = Message::where(function ($query) use ($user, $chatUser) {
$query->where('from_id', $user->id)->where('to_id', $chatUser->id);
})->orWhere(function ($query) use ($user, $chatUser) {
$query->where('from_id', $chatUser->id)->where('to_id', $user->id);
})->latest()->first();
$unreadCount = Message::where('from_id', $chatUser->id)
->where('to_id', $user->id)
->where('seen', 0)
->count();
// Check if user is online using cache
$cacheKey = "user_online_{$chatUser->id}";
$isOnline = Cache::has($cacheKey);
return [
'id' => $chatUser->id,
'name' => $chatUser->name,
'email' => $chatUser->email,
'avatar' => $chatUser->avatar,
'last_message' => $lastMessage,
'unread_count' => $unreadCount,
'is_online' => $isOnline,
'last_seen_at' => $chatUser->last_seen_at,
];
});
// Get messages for selected user
$messages = [];
$selectedUserId = $request->get('user_id');
if ($selectedUserId) {
$selectedUser = User::find($selectedUserId);
if ($selectedUser) {
// Mark messages as read
Message::where('from_id', $selectedUserId)
->where('to_id', $user->id)
->where('seen', 0)
->update(['seen' => 1]);
// Get conversation messages
$messages = Message::where(function ($query) use ($user, $selectedUserId) {
$query->where('from_id', $user->id)->where('to_id', $selectedUserId);
})->orWhere(function ($query) use ($user, $selectedUserId) {
$query->where('from_id', $selectedUserId)->where('to_id', $user->id);
})->where(function ($query) use ($user) {
// Filter out messages deleted by current user
$query->where(function ($q) use ($user) {
$q->where('from_id', $user->id)->where('deleted_by_sender', false);
})->orWhere(function ($q) use ($user) {
$q->where('to_id', $user->id)->where('deleted_by_receiver', false);
});
})->with(['fromUser', 'toUser'])
->orderBy('created_at', 'asc')
->get()
->map(function ($message) {
return [
'id' => $message->id,
'sender_id' => $message->from_id,
'receiver_id' => $message->to_id,
'message' => $message->body,
'is_read' => $message->seen,
'created_at' => $message->created_at->toISOString(),
'updated_at' => $message->updated_at->toISOString(),
'sender' => [
'id' => $message->fromUser->id,
'name' => $message->fromUser->name,
'email' => $message->fromUser->email,
'avatar' => $message->fromUser->avatar,
],
'receiver' => [
'id' => $message->toUser->id,
'name' => $message->toUser->name,
'email' => $message->toUser->email,
'avatar' => $message->toUser->avatar,
],
];
});
}
}
return Inertia::render('messenger/index', [
'users' => $users,
'messages' => $messages,
'selectedUserId' => $selectedUserId ? (int) $selectedUserId : null,
]);
} else {
return back()->with('error', __('Permission denied'));
}
}
public function send(Request $request)
{
if (Auth::user()->can('send-messages')) {
$request->validate([
'receiver_id' => 'required|exists:users,id',
'message' => 'nullable|string|max:1000',
'attachment' => 'nullable|file',
], [
'receiver_id.required' => __('Please select a user to send message to.'),
'receiver_id.exists' => __('Selected user does not exist.'),
'message.max' => __('Message cannot exceed 1000 characters.'),
]);
$user = Auth::user();
$receiverId = $request->receiver_id;
$messageText = $request->message;
$attachmentPath = null;
// Handle file attachment
if ($request->hasFile('attachment')) {
$file = $request->file('attachment');
$filenameWithExt = $file->getClientOriginalName();
$filename = pathinfo($filenameWithExt, PATHINFO_FILENAME);
$extension = $file->getClientOriginalExtension();
$fileNameToStore = $filename . '_' . time() . '.' . $extension;
$upload = upload_file($request, 'attachment', $fileNameToStore, 'messenger');
if ($upload['flag'] == 1) {
$attachmentPath = $upload['url'];
} else {
return back()->with('error', $upload['msg']);
}
}
// Validate that either message or attachment is provided
if (!$messageText && !$attachmentPath) {
return back()->with('error', __('Please provide a message or attachment.'));
}
// Create new message
$message = Message::create([
'from_id' => $user->id,
'to_id' => $receiverId,
'body' => $messageText ?: '',
'attachment' => $attachmentPath,
'seen' => 0,
]);
// Load relationships for broadcasting
$message->load(['fromUser', 'toUser']);
// Dispatch event for real-time updates
event(new MessageSent($message, $receiverId));
return response()->json(['success' => true, 'message' => __('Message sent successfully!')]);
} else {
return back()->with('error', __('Permission denied'));
}
}
public function getContacts()
{
$user = Auth::user();
// Apply same permission filtering as index method
$userQuery = User::where('id', '!=', $user->id);
if ($user->type === 'superadmin') {
$userQuery->where('creator_id', creatorId());
} elseif ($user->can('manage-any-users')) {
$userQuery->where('created_by', creatorId());
} elseif ($user->can('manage-own-users')) {
$userQuery->where('creator_id', $user->id);
} else {
$userQuery->where('id', creatorId());
}
$users = $userQuery->select('id', 'name', 'email', 'avatar', 'last_seen_at')
->get()
->map(function ($chatUser) {
$cacheKey = "user_online_{$chatUser->id}";
$isOnline = Cache::has($cacheKey);
return [
'id' => $chatUser->id,
'name' => $chatUser->name,
'email' => $chatUser->email,
'avatar' => $chatUser->avatar,
'is_online' => $isOnline,
'last_seen_at' => $chatUser->last_seen_at,
];
});
return response()->json($users);
}
public function getMessages($userId)
{
$user = Auth::user();
$perPage = request('per_page', 20);
$page = request('page', 1);
$messages = Message::where(function ($query) use ($user, $userId) {
$query->where('from_id', $user->id)->where('to_id', $userId);
})->orWhere(function ($query) use ($user, $userId) {
$query->where('from_id', $userId)->where('to_id', $user->id);
})->where(function ($query) use ($user) {
// Filter out messages deleted by current user
$query->where(function ($q) use ($user) {
$q->where('from_id', $user->id)->where('deleted_by_sender', false);
})->orWhere(function ($q) use ($user) {
$q->where('to_id', $user->id)->where('deleted_by_receiver', false);
});
})->orderBy('created_at', 'desc')
->paginate($perPage, ['*'], 'page', $page);
// Mark messages as read only on first page
if ($page == 1) {
Message::where('from_id', $userId)
->where('to_id', $user->id)
->where('seen', 0)
->update(['seen' => 1]);
}
$formattedMessages = $messages->getCollection()->map(function ($message) {
$fromUser = User::find($message->from_id);
$toUser = User::find($message->to_id);
return [
'id' => $message->id,
'from_id' => $message->from_id,
'to_id' => $message->to_id,
'body' => $message->body,
'attachment' => $message->attachment,
'seen' => $message->seen,
'created_at' => $message->created_at->toISOString(),
'updated_at' => $message->updated_at->toISOString(),
'from_user' => [
'id' => $fromUser->id,
'name' => $fromUser->name,
'email' => $fromUser->email,
'avatar' => $fromUser->avatar,
],
'to_user' => [
'id' => $toUser->id,
'name' => $toUser->name,
'email' => $toUser->email,
'avatar' => $toUser->avatar,
],
];
})->reverse()->values(); // Convert to chronological order (oldest first) for display
return response()->json([
'data' => $formattedMessages,
'current_page' => $messages->currentPage(),
'last_page' => $messages->lastPage(),
'per_page' => $messages->perPage(),
'total' => $messages->total(),
'next_page_url' => $messages->nextPageUrl(),
'prev_page_url' => $messages->previousPageUrl(),
]);
}
public function toggleFavorite(Request $request)
{
if (Auth::user()->can('toggle-favorite-messages')) {
$request->validate([
'user_id' => 'required|exists:users,id',
], [
'user_id.required' => __('User ID is required.'),
'user_id.exists' => __('Selected user does not exist.'),
]);
$user = Auth::user();
$favoriteUserId = $request->user_id;
$favorite = Favorite::where('user_id', $user->id)
->where('favorite_id', $favoriteUserId)
->first();
if ($favorite) {
$favorite->delete();
$isFavorite = false;
} else {
Favorite::create([
'user_id' => $user->id,
'favorite_id' => $favoriteUserId,
]);
$isFavorite = true;
}
return response()->json(['is_favorite' => $isFavorite]);
} else {
return back()->with('error', __('Permission denied'));
}
}
public function getFavorites()
{
$user = Auth::user();
$favoriteIds = Favorite::where('user_id', $user->id)
->pluck('favorite_id')
->toArray();
return response()->json($favoriteIds);
}
public function editMessage(Request $request, $messageId)
{
if (Auth::user()->can('edit-messages')) {
$request->validate([
'message' => 'required|string|max:1000',
], [
'message.required' => __('Message cannot be empty.'),
'message.max' => __('Message cannot exceed 1000 characters.'),
]);
$user = Auth::user();
$message = Message::where('id', $messageId)
->where('from_id', $user->id)
->first();
if (!$message) {
return response()->json(['error' => __('Message not found')], 404);
}
$message->update(['body' => $request->message]);
return response()->json(['success' => true, 'message' => __('Message updated successfully.')]);
} else {
return back()->with('error', __('Permission denied'));
}
}
public function deleteMessage($messageId)
{
$user = Auth::user();
if ($user->can('delete-messages')) {
$message = Message::find($messageId);
if (!$message || ($message->from_id !== $user->id && $message->to_id !== $user->id)) {
return response()->json(['error' => __('Message not found')], 404);
}
// Mark as deleted by current user
if ($message->from_id === $user->id) {
$message->deleted_by_sender = true;
} else {
$message->deleted_by_receiver = true;
}
// If both users deleted, hard delete
if ($message->deleted_by_sender && $message->deleted_by_receiver) {
$message->delete();
} else {
$message->save();
}
return response()->json(['success' => true]);
} else {
return back()->with('error', __('Permission denied'));
}
}
public function updatePresence(Request $request)
{
$user = Auth::user();
$wasOnline = Cache::has("user_online_{$user->id}");
Cache::put("user_online_{$user->id}", true, 300);
// Broadcast online status if user wasn't online before
if (!$wasOnline) {
broadcast(new UserOnline($user->id));
}
return response()->json(['status' => 'online']);
}
public function setOffline(Request $request)
{
$user = Auth::user();
$wasOnline = Cache::has("user_online_{$user->id}");
Cache::forget("user_online_{$user->id}");
// Broadcast offline status if user was online
if ($wasOnline) {
broadcast(new UserOffline($user->id));
}
return response()->json(['status' => 'offline']);
}
public function getOnlineUsers()
{
$user = Auth::user();
$userQuery = User::where('id', '!=', $user->id);
if ($user->type === 'superadmin') {
$userQuery->where('creator_id', creatorId());
} elseif ($user->can('manage-any-users')) {
$userQuery->where('created_by', creatorId());
} elseif ($user->can('manage-own-users')) {
$userQuery->where('creator_id', $user->id);
} else {
$userQuery->where('id', creatorId());
}
$users = $userQuery->get(['id', 'name'])->map(function ($chatUser) {
return [
'id' => $chatUser->id,
'name' => $chatUser->name,
'is_online' => Cache::has("user_online_{$chatUser->id}")
];
});
return response()->json($users);
}
public function togglePin(Request $request)
{
if (Auth::user()->can('toggle-pinned-messages')) {
$request->validate([
'user_id' => 'required|exists:users,id',
], [
'user_id.required' => __('User ID is required.'),
'user_id.exists' => __('Selected user does not exist.'),
]);
$user = Auth::user();
$pinnedUserId = $request->user_id;
$pinned = Pinned::where('user_id', $user->id)
->where('pinned_id', $pinnedUserId)
->first();
if ($pinned) {
$pinned->delete();
$isPinned = false;
} else {
$pinnedCount = Pinned::where('user_id', $user->id)->count();
if ($pinnedCount >= 3) {
return response()->json(['error' => __('You can only pin up to 3 chats')], 400);
}
Pinned::create([
'user_id' => $user->id,
'pinned_id' => $pinnedUserId,
]);
$isPinned = true;
}
return response()->json(['is_pinned' => $isPinned]);
} else {
return back()->with('error', __('Permission denied'));
}
}
public function getPinned()
{
$user = Auth::user();
$pinnedIds = Pinned::where('user_id', $user->id)
->pluck('pinned_id')
->toArray();
return response()->json($pinnedIds);
}
public function checkNewMessages()
{
$lastCheck = request('last_check', now()->subMinutes(1));
$newMessages = Message::where('to_id', Auth::id())
->where('created_at', '>', $lastCheck)
->with(['fromUser:id,name,email,avatar'])
->get();
$hasNewMessages = $newMessages->count() > 0;
return response()->json([
'has_new_messages' => $hasNewMessages,
'timestamp' => now()->toISOString(),
'new_messages_count' => $newMessages->count()
]);
}
}