Skip to content

Commit

Permalink
Merge branch 'dev' into loader-frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
Saodus committed Dec 17, 2024
2 parents 6ddfff6 + 4e080b2 commit ed9f219
Show file tree
Hide file tree
Showing 14 changed files with 423 additions and 225 deletions.
104 changes: 77 additions & 27 deletions app/Http/Controllers/ChatController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,43 @@

use Illuminate\Http\Request;

/**
* Controller for the chat system (discussions, messages, capsules, etc.).
*/
class ChatController extends Controller
{
/**
* Show the dashboard with the list of discussions and pass the friends list to the view.
*/
public function index()
{
// Get the list of discussions with the last message and the members
$discussions = auth()->user()->chats()
->with(['messages' => function ($query) {
$query->latest()->limit(1);
}, 'members']) // Assure-toi que la relation members est chargée
}, 'members'])
->latest()
->get()
->map(function ($discussion) {
// Vérifie si la discussion est un chat individuel (2 membres)
// We check if the discussion is a private chat
if ($discussion->members->count() == 2) {
// Détermine l'image en fonction des membres
// And set the profile picture of the other member as the discussion picture
$otherMember = $discussion->members->first()->id == auth()->id()
? $discussion->members->skip(1)->first()
: $discussion->members->first();

// Ajoute l'image sélectionnée comme propriété de la discussion
$discussion->discussionPicture = $otherMember->image
? asset('storage/' . $otherMember->image)
: asset('source/assets/avatar/avatar.png');
} else {
// Utilise une image par défaut pour les groupes
// We use a default group picture if the discussion is a group chat
$discussion->discussionPicture = asset('source/assets/images/group.png');
}

return $discussion;
});

// Récupérer les amis acceptés
// Get the list of friends (accepted friendships)
$friends = Friendship::where(function ($query) {
$query->where('user_id', auth()->id())
->orWhere('friend_id', auth()->id());
Expand All @@ -59,28 +65,40 @@ public function index()
]);
}

/**
* Return the messages of a discussion (with the closed capsules and opened capsules)
*
* @param int $chatId The ID of the discussion
* @return \Illuminate\Http\JsonResponse A JSON response containing the array of messages.
*/
public function getMessages($chatId)
{
// We get all the messages (including the closed capsules) of the discussion
$messages = Message::where('chat_id', $chatId)
->orderBy('created_at', 'asc')
->with('user')
->get();

// We get the opened capsules of the discussion
$opened_capsule = Message::where('chat_id', $chatId)
->where('created_at', '<', DB::raw('opening_date'))
->where('opening_date', '<', now('Europe/Paris'))
->orderBy('created_at', 'asc')
->with('user')
->get();

// Pour tout les opened_capsule, modifier l'id du message
// For all the opened capsules, we modify the ID to avoid conflicts with the other messages
// and we change the creation date to the opening date as the messages will be sorted by creation date
// We do that to display the opened capsules at the right place in the chat
$opened_capsule->each(function ($message) {
$message->id += 10000000;
// On modifie la date de création pour qu'elle corresponde à la date d'ouverture, pour que le message soit affiché au bon endroit dans la conversation
$message->created_at = $message->opening_date;
});

// Pour tout les messages qui ont une date de création plus petite que la opening_date, modifier le message
// For all messages that have a creation date less than the opening_date, modify the message
// If the creation date is less than the opening date, it means that the message is a capsule,
// so we change the message to a locked message and we change the media_url to a default image
// to avoid sending the message and media of the capsule to the client before the opening date
$messages->each(function ($message) {
if ($message->created_at < $message->opening_date) {
$prettyDate = \Carbon\Carbon::parse($message->opening_date);
Expand All @@ -90,20 +108,27 @@ public function getMessages($chatId)
}
});

// To avoid error in the format of the returned JSON
$opened_capsule = $opened_capsule->toArray();
$messages = $messages->toArray();

// Fusionner les messages et les capsules ouvertes
$messages = array_merge($messages, $opened_capsule);

// Trier les messages par date de création
// We sort the messages by creation date
usort($messages, function ($a, $b) {
return $a['created_at'] <=> $b['created_at'];
});

return response()->json(['messages' => $messages]);
}

/**
* Store a new message sent by the user in a specific chat.
*
* @param \Illuminate\Http\Request $request The request containing the message content.
* @param int $chatId The ID of the chat.
* @return \Illuminate\Http\JsonResponse A JSON response containing the message (or an error).
*/
public function storeMessage(Request $request, $chatId)
{
$request->validate([
Expand All @@ -116,6 +141,7 @@ public function storeMessage(Request $request, $chatId)
return response()->json(['error' => 'Chat not found or unauthorized.'], 404);
}

// Logic to check if the user is blocked by the other member or if the other member is blocked
if ($chat->members->count() == 2) {
$otherMember = $chat->members->firstWhere('id', '!=', auth()->id());

Expand All @@ -132,6 +158,7 @@ public function storeMessage(Request $request, $chatId)
}
}

// Create a new message and save it in the database
$message = new Message();
$message->user_id = auth()->id();
$message->chat_id = $chatId;
Expand All @@ -141,64 +168,80 @@ public function storeMessage(Request $request, $chatId)
return response()->json(['message' => $message]);
}

/**
* Store a new capsule sent by the user in a specific chat.
*
* @param \Illuminate\Http\Request $request The request containing the message content and the media file.
* @param int $chatId The ID of the chat.
* @return \Illuminate\Http\JsonResponse A JSON response containing the capsule (or an error).
*/
public function storeCapsule(Request $request, $chatId)
{
// Validation
// We authorize file up to 1GB and only the following formats
$request->validate([
'file' => 'required|file|mimes:jpeg,png,jpg,gif,mp3,mp4,mov|max:1048576',
'message' => 'required|string'
]);

// Vérification de la présence du fichier
// Check if the file is valid
if ($request->hasFile('file') && $request->file('file')->isValid()) {
$media = $request->file('file');
$mediaName = time() . '_' . $media->getClientOriginalName();

// Déplacer le fichier vers le répertoire public/source/media
// Generate a unique name for the file and move it to the media folder
$mediaName = time() . '_' . $media->getClientOriginalName();
$media->move(public_path('source/media'), $mediaName);

// Créer un nouveau message et l'enregistrer dans la base de données
// Create a new message and save it in the database (with the media URL)
$message = new Message();
$message->user_id = auth()->id();
$message->chat_id = $chatId;
$message->message = $request->message;
$message->media_url = $mediaName;

// Vérifier que la date d'ouverture est définie
// Setting the opening date to now (to open it instantly) if the user didn't set one
$message->opening_date = $request->filled('date_time') ? $request->date_time : now("Europe/Paris");

$message->save();

return response()->json(['message' => $message]);
}

return response()->json(['error' => 'Le fichier n\'a pas été envoyé ou est invalide.'], 400);
return response()->json(['error' => 'The file was not sent or is invalid.'], 400);
}

/**
* Store a new chat with the name and the friends selected by the user.
*
* @param \Illuminate\Http\Request $request The request containing the chat name and the friends.
* @return \Illuminate\Http\RedirectResponse A redirect response to the dashboard with a success message.
*/
public function storeChat(Request $request)
{
// Valider les champs du formulaire
$request->validate([
'chat_name' => 'required|string|max:255',
'friends' => 'required|array|min:1', // Minimum 1 ami sélectionné
'friends.*' => 'exists:users,id', // Vérifie si les amis existent dans la table users
'friends' => 'required|array|min:1', // Minimum 1 friend selected
'friends.*' => 'exists:users,id', // Checking if the friends exist in the database
]);

// Créer la discussion dans la table 'chats'
$chat = new Chat(); // Assurez-vous d'importer App\Models\Chat en haut
// Create a new chat and save it in the database
$chat = new Chat();
$chat->name = $request->chat_name;
$chat->save();

// Associer l'utilisateur actuel à la discussion
// Link the user who created the chat to the discussion
$chat->users()->attach(auth()->id());

// Associer les amis sélectionnés à la discussion
// Link the friends selected by the user to the discussion
$chat->users()->attach($request->friends);

// Rediriger avec un message de succès
return redirect()->route('dashboard')->with('success', 'Discussion créée avec succès !');
}

/**
* Leave a chat (remove the user from the chat).
*
* @param int $chatId The ID of the chat.
* @return \Illuminate\Http\JsonResponse A JSON response with a message.
*/
public function leaveChat($chatId)
{
$chat = Chat::find($chatId);
Expand All @@ -212,6 +255,13 @@ public function leaveChat($chatId)
return response()->json(['message' => 'You left the chat.']);
}

/**
* Delete a message from a discussion.
*
* @param int $discussionId The ID of the discussion.
* @param int $messageId The ID of the message.
* @return \Illuminate\Http\JsonResponse A JSON response with a message (or an error).
*/
public function deleteMessage($discussionId, $messageId)
{
$message = Message::where('id', $messageId)->where('chat_id', $discussionId)->first();
Expand Down
Binary file added public/source/assets/images/info_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/source/assets/images/profile.png
Binary file not shown.
Binary file added public/source/media/1734346086_1159477.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/source/media/1734346210_1159477.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion resources/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@
100% {
transform: rotate(360deg);
}
}
}
97 changes: 12 additions & 85 deletions resources/views/components/messaging/chatbar.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,114 +13,41 @@ class="rounded-full secondary-background-app p-2 flex items-center justify-cente
</span>

<x-modal name="create-file-modal" focusable>
<form method="post" class="p-6" x-data="capsuleForm()" id="create-file-modal-form" @submit.prevent="submitForm">
<form method="post" class="p-6" x-data="capsuleForm()" id="create-file-modal-form" @submit.prevent="submitForm" enctype="multipart/form-data" x-on:click.away="$dispatch('close'); document.getElementById('create-file-modal-form').reset(); document.getElementById('file-info').innerHTML = '<p class=&quot;text-gray-300 font-medium&quot;>Glissez et déposez votre fichier ici ou</p><p class=&quot;text-blue-400 underline&quot;>cliquez pour sélectionner un fichier</p>';">
@csrf
<input type="hidden" id="discussion-id" name="discussion_id" value="CHAT_ID">
<h3 class="text-lg font-semibold text-gray-300 mb-5">Créer une capsule</h3>
<!-- Champ pour le fichier de la capsule -->
<div class="flex items-center justify-center bg-gray-800">
<div class="border-dashed border-4 border-gray-500 rounded-lg p-8 bg-gray-900 hover:bg-gray-700 transition duration-300" ondrop="handleDrop(event)" ondragover="handleDragOver(event)">
<label for="file" class="flex flex-col items-center justify-center cursor-pointer">
<div id="file-info">
<!-- Field for the file of the capsule -->
<div class="flex items-center justify-center h-full w-full">
<div class="border-dashed border-4 border-gray-500 rounded-lg bg-gray-900 hover:bg-gray-700 transition duration-300" ondrop="handleDrop(event)" ondragover="handleDragOver(event)">
<label for="file" class="flex flex-col items-center justify-center cursor-pointer h-full w-full">
<div id="file-info" class="text-center mt-6">
<p class="text-gray-300 font-medium">Glissez et déposez votre fichier ici ou</p>
<p class="text-blue-400 underline">cliquez pour sélectionner un fichier</p>
</div>
</label>
<input id="file" name="file" type="file" class="hidden" onchange="updateFileName()" x-model="file">
<input id="file" name="file" type="file" class="inset-0 opacity-0 cursor-pointer" accept=".jpeg,.png,.jpg,.gif,.mp3,.mp4,.mov" onchange="updateFileName()" x-model="file" required>
</div>
</div>
<script>
function updateFileName() {
const fileInput = document.getElementById('file');
const fileInfo = document.getElementById('file-info');
if (fileInput.files.length > 0) {
const fileName = fileInput.files[0].name;
fileInfo.innerHTML = `<p class='text-green-400 font-medium'>Fichier sélectionné : ${fileName}</p>`;
}
}
function handleDragOver(event) {
event.preventDefault();
}
function handleDrop(event) {
event.preventDefault();
const fileInput = document.getElementById('file');
const fileInfo = document.getElementById('file-info');
if (event.dataTransfer.files.length > 0) {
const file = event.dataTransfer.files[0];
fileInput.files = event.dataTransfer.files;
fileInfo.innerHTML = `<p class='text-green-400 font-medium'>Fichier sélectionné : ${file.name}</p>`;
}
}
</script>
<!-- Champ pour le message de la capsule -->
<!-- Field for the message of the capsule -->
<div class="mb-4">
<x-input-label for="message" value="Message" />
<x-text-input id="message" name="message" type="text" class="block w-full mt-1"
x-model="chatMessage" />
<x-text-input id="message" name="message" type="text" class="block w-full mt-1" x-model="chatMessage" required />
<div class="mb-4">
<x-input-label for="date-time" value="Date et heure d'ouverture de la capsule (laisser vide pour ouverture instantanée)" />
<input type="datetime-local" id="date-time" name="date_time" class="block w-full mt-1 border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" />
</div>
</div>
<!-- Boutons d'action -->
<!-- Action buttons -->
<div class="mt-6 flex justify-end">
<x-secondary-button x-on:click="$dispatch('close')">
<x-secondary-button x-on:click="$dispatch('close'); document.getElementById('create-file-modal-form').reset(); document.getElementById('file-info').innerHTML = '<p class=&quot;text-gray-300 font-medium&quot;>Glissez et déposez votre fichier ici ou</p><p class=&quot;text-blue-400 underline&quot;>cliquez pour sélectionner un fichier</p>';">
Annuler
</x-secondary-button>
<x-primary-button class="ml-3" x-on:click.prevent="submitForm">
<x-primary-button class="ml-3" type="submit">
Envoyer la capsule
</x-primary-button>
</div>
</form>
</x-modal>
</div>
</div>

<script>
function capsuleForm() {
return {
chatMessage: '',
file: null,
submitForm() {
event.preventDefault();
// Récupérer dynamiquement l'ID de la discussion depuis le champ caché
const discussionId = document.getElementById('discussion-id').value;
// Créer l'objet FormData avec les données du formulaire
const formData = new FormData(document.getElementById('create-file-modal-form'));
// Effectuer la requête AJAX avec `fetch`
fetch(`/chat/${discussionId}/capsule`, {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
})
.then(response => response.json())
.then(data => {
if (data.message.id) {
// Fermer le modal et réinitialiser le formulaire
this.chatMessage = '';
this.file = null;
document.getElementById('file-info').innerHTML = `<p class='text-gray-300 font-medium'>Glissez et déposez votre fichier ici ou</p><p class='text-blue-400 underline'>cliquez pour sélectionner un fichier</p>`;
document.getElementById('date-time').value = '';
this.$dispatch('close');
} else {
console.log(data)
alert('Erreur lors de l\'envoi de la capsule');
}
})
.catch(error => {
alert('Erreur lors de l\'envoi');
console.error(error);
});
}
};
}
</script>
Loading

0 comments on commit ed9f219

Please sign in to comment.