518 lines
20 KiB
PHP
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()
|
|
]);
|
|
}
|
|
} |