From 12d96f16549bf7df5a472461f386a578392123a7 Mon Sep 17 00:00:00 2001 From: sarahelsagheir <53413980+sarahelsagheir@users.noreply.github.com> Date: Tue, 10 Sep 2024 21:39:08 +0300 Subject: [PATCH 1/2] add voice message --- src/ChatifyMessenger.php | 30 +- src/Http/Controllers/MessagesController.php | 204 +- src/assets/css/style.css | 141 + src/assets/js/code.js | 2836 ++++++++++--------- src/assets/js/voiceMessage.js | 372 +++ src/config/chatify.php | 1 + src/views/layouts/footerLinks.blade.php | 4 +- src/views/layouts/headLinks.blade.php | 1 + src/views/layouts/messageCard.blade.php | 25 +- src/views/layouts/sendForm.blade.php | 21 +- 10 files changed, 2167 insertions(+), 1468 deletions(-) create mode 100644 src/assets/js/voiceMessage.js diff --git a/src/ChatifyMessenger.php b/src/ChatifyMessenger.php index a89b0919..a5f2fcba 100644 --- a/src/ChatifyMessenger.php +++ b/src/ChatifyMessenger.php @@ -23,6 +23,7 @@ public function getMaxUploadSize() return config('chatify.attachments.max_upload_size') * 1048576; } + public function __construct() { $this->pusher = new Pusher( @@ -150,7 +151,22 @@ public function parseMessage($prefetchedMessage = null, $id = null) $attachment = $attachmentOBJ->new_name; $attachment_title = htmlentities(trim($attachmentOBJ->old_name), ENT_QUOTES, 'UTF-8'); $ext = pathinfo($attachment, PATHINFO_EXTENSION); - $attachment_type = in_array($ext, $this->getAllowedImages()) ? 'image' : 'file'; + if (isset($msg->attachment)) { + $attachmentOBJ = json_decode($msg->attachment); + $attachment = $attachmentOBJ->new_name; + $attachment_title = htmlentities(trim($attachmentOBJ->old_name), ENT_QUOTES, 'UTF-8'); + $ext = pathinfo($attachment, PATHINFO_EXTENSION); + // + if( in_array($ext, $this->getAllowedImages())) { + $attachment_type = 'image'; + }else if(in_array($ext, $this->getAllowedVoiceMessages())){ + $attachment_type = 'audio'; + }else{ + + $attachment_type = 'file'; + } + + } } return [ 'id' => $msg->id, @@ -442,4 +458,16 @@ public function getAttachmentUrl($attachment_name) { return self::storage()->url(config('chatify.attachments.folder') . '/' . $attachment_name); } + + + /** + * This method returns the allowed voice messages extensions + * + * @return array + */ + public function getAllowedVoiceMessages() + { + return config('chatify.attachments.allowed_voice_messages'); + } + } diff --git a/src/Http/Controllers/MessagesController.php b/src/Http/Controllers/MessagesController.php index 10964faf..1fa3fe50 100644 --- a/src/Http/Controllers/MessagesController.php +++ b/src/Http/Controllers/MessagesController.php @@ -88,77 +88,159 @@ public function download($fileName) return abort(404, "Sorry, File does not exist in our server or may have been deleted!"); } - /** - * Send a message to database - * - * @param Request $request - * @return JsonResponse - */ - public function send(Request $request) - { - // default variables - $error = (object)[ - 'status' => 0, - 'message' => null - ]; - $attachment = null; - $attachment_title = null; +/** + * Send a message to database + * + * + * @param Request $request + * @return JsonResponse + */ +public function send(Request $request) +{ + // Initialize error object and attachment variables + $error = (object) [ + 'status' => 0, + 'message' => null + ]; + $attachment = null; + $attachment_title = null; + $isVoiceMessage = $request->hasFile('audio'); + + // Process the message based on its type + if ($isVoiceMessage) { + $this->handleVoiceMessage($request, $error, $attachment, $attachment_title); + } else { + $this->handleTextOrFileMessage($request, $error, $attachment, $attachment_title); + } - // if there is attachment [file] - if ($request->hasFile('file')) { - // allowed extensions - $allowed_images = Chatify::getAllowedImages(); - $allowed_files = Chatify::getAllowedFiles(); - $allowed = array_merge($allowed_images, $allowed_files); + // If no error occurred, create the message and push notification + if (!$error->status) { + $message = $this->createMessage($request, $isVoiceMessage, $attachment, $attachment_title); + $messageData = Chatify::parseMessage($message); + $this->pushNotification($request, $messageData); + } - $file = $request->file('file'); - // check file size - if ($file->getSize() < Chatify::getMaxUploadSize()) { - if (in_array(strtolower($file->extension()), $allowed)) { - // get attachment name - $attachment_title = $file->getClientOriginalName(); - // upload attachment and store the new name - $attachment = Str::uuid() . "." . $file->extension(); - $file->storeAs(config('chatify.attachments.folder'), $attachment, config('chatify.storage_disk_name')); - } else { - $error->status = 1; - $error->message = "File extension not allowed!"; - } + // Return JSON response + return Response::json([ + 'status' => '200', + 'error' => $error, + 'message' => Chatify::messageCard(@$messageData), + 'tempID' => $request['temporaryMsgId'], + ]); +} + +/** + * Handle Voice Message + + * + * @param Request $request + * @param object $error + * @param string $attachment + * @param string $attachment_title + * @return void + */ +private function handleVoiceMessage(Request $request, &$error, &$attachment, &$attachment_title) +{ + $allowed_voice_messages = Chatify::getAllowedVoiceMessages(); + + if ($request->hasFile('audio')) { + $file = $request->file('audio'); + if ($file->getSize() < Chatify::getMaxUploadSize()) { + if (in_array(strtolower($file->extension()), $allowed_voice_messages)) { + $this->storeAttachment($file, $attachment, $attachment_title); } else { - $error->status = 1; - $error->message = "File size you are trying to upload is too large!"; + $this->setError($error, "File extension not allowed!"); } + } else { + $this->setError($error, "File size you are trying to upload is too large!"); } + } +} - if (!$error->status) { - $message = Chatify::newMessage([ - 'from_id' => Auth::user()->id, - 'to_id' => $request['id'], - 'body' => htmlentities(trim($request['message']), ENT_QUOTES, 'UTF-8'), - 'attachment' => ($attachment) ? json_encode((object)[ - 'new_name' => $attachment, - 'old_name' => htmlentities(trim($attachment_title), ENT_QUOTES, 'UTF-8'), - ]) : null, - ]); - $messageData = Chatify::parseMessage($message); - if (Auth::user()->id != $request['id']) { - Chatify::push("private-chatify.".$request['id'], 'messaging', [ - 'from_id' => Auth::user()->id, - 'to_id' => $request['id'], - 'message' => Chatify::messageCard($messageData, true) - ]); +/** + * Handle Text or File Message + * @param Request $request + * @param object $error + * @param string $attachment + * @param string $attachment_title + * @return void + */ +private function handleTextOrFileMessage(Request $request, &$error, &$attachment, &$attachment_title) +{ + if ($request->hasFile('file')) { + $allowed = array_merge(Chatify::getAllowedImages(), Chatify::getAllowedFiles()); + $file = $request->file('file'); + + if ($file->getSize() < Chatify::getMaxUploadSize()) { + if (in_array(strtolower($file->extension()), $allowed)) { + $this->storeAttachment($file, $attachment, $attachment_title); + } else { + $this->setError($error, "File extension not allowed!"); } + } else { + $this->setError($error, "File size you are trying to upload is too large!"); } - - // send the response - return Response::json([ - 'status' => '200', - 'error' => $error, - 'message' => Chatify::messageCard(@$messageData), - 'tempID' => $request['temporaryMsgId'], - ]); } +} +/** + * Store Attachment + * + * @param UploadedFile $file + * @param string $attachment + * @param string $attachment_title + * @return void + */ +private function storeAttachment($file, &$attachment, &$attachment_title) +{ + $attachment = Str::uuid() . "." . $file->getClientOriginalExtension(); + $attachment_title = $file->getClientOriginalName(); + $file->storeAs(config('chatify.attachments.folder'), $attachment, config('chatify.storage_disk_name')); +} + +/** + * Set Error + * @param object $error + * @param string $message + * @return void + */ +private function setError(&$error, $message) +{ + $error->status = 1; + $error->message = $message; +} + +/** + * Create Message + * + * @param Request $request + * @param bool $isVoiceMessage + * @param string $attachment + * @param string $attachment_title + * @return mixed + */ +private function createMessage(Request $request, $isVoiceMessage, $attachment, $attachment_title) +{ + return Chatify::newMessage([ + 'from_id' => Auth::user()->id, + 'to_id' => $request->input('id'), + 'body' => $isVoiceMessage ? '' : htmlentities(trim($request['message']), ENT_QUOTES, 'UTF-8'), + 'attachment' => ($attachment) ? json_encode((object) [ + 'new_name' => $attachment, + 'old_name' => htmlentities(trim($attachment_title), ENT_QUOTES, 'UTF-8'), + ]) : null, + ]); +} + private function pushNotification(Request $request, $messageData) + { + if (Auth::user()->id != $request->input('id')) { + Chatify::push("private-chatify." . $request->input('id'), 'messaging', [ + 'from_id' => Auth::user()->id, + 'to_id' => $request->input('id'), + 'message' => Chatify::messageCard($messageData, true) + ]); + } + } /** * fetch [user/group] messages from database * diff --git a/src/assets/css/style.css b/src/assets/css/style.css index 4565b6b8..133e3112 100644 --- a/src/assets/css/style.css +++ b/src/assets/css/style.css @@ -1184,3 +1184,144 @@ div.loadingPlaceholder-date { border-radius: 4px; background: transparent; } + + +/* +***************************************** +* microphone +***************************************** +*/ +.microphone-button { + position: relative; + border: 2px solid var(--primary-color); + background-color: var(--primary-color); + border-radius: 50%; + padding: 15px; + cursor: pointer; + outline: none; + transition: transform 0.2s ease; +} +.microphone-icon { + position: relative; + font-size: 24px; + color: white; +} + +/* +***************************************** +* waveSurfer +***************************************** +*/ + + +.audio-wrap { + display: flex; + align-items: center; + border-radius: 20px; + justify-content: space-between; +} + +.controls { + display: flex; + justify-content: space-between; + align-items: center; + border-radius: 25%; + margin-bottom: 15px; + padding :15px; + width: 13em; + height: 3em; + border-radius: 25px; + box-shadow: 0px 6px 11px rgba(18, 67, 105, 0.03); + + +} +.controls .btn-toggle-play { + background-color: transparent; + border: none; + width: 10%; + height: 50px; + display: flex; + justify-content: center; + align-items: center; +} + + +.controls .btn-toggle-play i { + font-size: 20px; +} + +.waveform { + width: 80%; + overflow: hidden; + + +} + +.controls .duration { + font-size: 15px; + font-weight: 500; + margin-left: 15px; + width:10%; +} +/* + * Recording Slide Progress +*/ +.recording-ui { + width: 100%; + padding: 10px 0; +} + +.recording-slider-wrapper { + background-color: var(--primary-color); + border-radius: 18px; + display: flex; + align-items: center; + width: 100%; + position: relative; + overflow: hidden; +} + +.recording-slider-content { + display: flex; + align-items: center; + width: 100%; + position: relative; + z-index: 2; +} + +.recording-progress-bar { + position: absolute; + left: 0; + top: 0; + height: 100%; + background-color: rgba(241, 243, 245, 0.2); + transition: width 0.1s linear; + z-index: 1; +} + +.stop-button, +.trash-button, +.send-record-button { + background: none; + border: none; + cursor: pointer; + padding: 0 10px; + z-index: 2; +} + + +.fa-trash , .fa-paper-plane { + color: var(--primary-color) !important; +} +.fa-stop, .fa-play, .fa-pause{ +color: white !important; +font-size: 15px !important; +} + +.recording-time { + font-size: 14px; + color: #f4f6f8 !important; + margin: 0 10px; + min-width: 40px; + z-index: 2; +} diff --git a/src/assets/js/code.js b/src/assets/js/code.js index f899c8cd..be3d3c8b 100644 --- a/src/assets/js/code.js +++ b/src/assets/js/code.js @@ -3,95 +3,95 @@ * Global variables *------------------------------------------------------------- */ -var messenger, - typingTimeout, - typingNow = 0, - temporaryMsgId = 0, - defaultAvatarInSettings = null, - messengerColor, - dark_mode, - messages_page = 1; + var messenger, + typingTimeout, + typingNow = 0, + temporaryMsgId = 0, + defaultAvatarInSettings = null, + messengerColor, + dark_mode, + messages_page = 1; const messagesContainer = $(".messenger-messagingView .m-body"), - messengerTitleDefault = $(".messenger-headTitle").text(), - messageInputContainer = $(".messenger-sendCard"), - messageInput = $("#message-form .m-send"), - auth_id = $("meta[name=url]").attr("data-user"), - url = $("meta[name=url]").attr("content"), - messengerTheme = $("meta[name=messenger-theme]").attr("content"), - defaultMessengerColor = $("meta[name=messenger-color]").attr("content"), - csrfToken = $('meta[name="csrf-token"]').attr("content"); + messengerTitleDefault = $(".messenger-headTitle").text(), + messageInputContainer = $(".messenger-sendCard"), + messageInput = $("#message-form .m-send"), + auth_id = $("meta[name=url]").attr("data-user"), + url = $("meta[name=url]").attr("content"), + messengerTheme = $("meta[name=messenger-theme]").attr("content"), + defaultMessengerColor = $("meta[name=messenger-color]").attr("content"), + csrfToken = $('meta[name="csrf-token"]').attr("content"); const getMessengerId = () => $("meta[name=id]").attr("content"); const setMessengerId = (id) => $("meta[name=id]").attr("content", id); /** - *------------------------------------------------------------- - * Pusher initialization - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Pusher initialization +*------------------------------------------------------------- +*/ Pusher.logToConsole = chatify.pusher.debug; const pusher = new Pusher(chatify.pusher.key, { - encrypted: chatify.pusher.options.encrypted, - cluster: chatify.pusher.options.cluster, - wsHost: chatify.pusher.options.host, - wsPort: chatify.pusher.options.port, - wssPort: chatify.pusher.options.port, - forceTLS: chatify.pusher.options.useTLS, - authEndpoint: chatify.pusherAuthEndpoint, - auth: { - headers: { - "X-CSRF-TOKEN": csrfToken, - }, - }, + encrypted: chatify.pusher.options.encrypted, + cluster: chatify.pusher.options.cluster, + wsHost: chatify.pusher.options.host, + wsPort: chatify.pusher.options.port, + wssPort: chatify.pusher.options.port, + forceTLS: chatify.pusher.options.useTLS, + authEndpoint: chatify.pusherAuthEndpoint, + auth: { + headers: { + "X-CSRF-TOKEN": csrfToken, + }, + }, }); /** - *------------------------------------------------------------- - * Re-usable methods - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Re-usable methods +*------------------------------------------------------------- +*/ const escapeHtml = (unsafe) => { - return unsafe - .replace(/&/g, "&") - .replace(//g, ">"); + return unsafe + .replace(/&/g, "&") + .replace(//g, ">"); }; function actionOnScroll(selector, callback, topScroll = false) { - $(selector).on("scroll", function () { - let element = $(this).get(0); - const condition = topScroll - ? element.scrollTop == 0 - : element.scrollTop + element.clientHeight >= element.scrollHeight; - if (condition) { - callback(); - } - }); + $(selector).on("scroll", function () { + let element = $(this).get(0); + const condition = topScroll + ? element.scrollTop == 0 + : element.scrollTop + element.clientHeight >= element.scrollHeight; + if (condition) { + callback(); + } + }); } function routerPush(title, url) { - $("meta[name=url]").attr("content", url); - return window.history.pushState({}, title || document.title, url); + $("meta[name=url]").attr("content", url); + return window.history.pushState({}, title || document.title, url); } function updateSelectedContact(user_id) { user_id = user_id || getMessengerId(); -$(document).find(".messenger-list-item").removeClass("m-list-active"); -$(document) - .find( + $(document).find(".messenger-list-item").removeClass("m-list-active"); + $(document) + .find( ".messenger-list-item[data-contact=" + (user_id) + "]" - ) - .addClass("m-list-active"); + ) + .addClass("m-list-active"); - if (user_id != 0) { - IDinfo(user_id); - } + if (user_id != 0) { + IDinfo(user_id); } + } /** - *------------------------------------------------------------- - * Global Templates - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Global Templates +*------------------------------------------------------------- +*/ // Loading svg function loadingSVG(size = "25px", className = "", style = "") { - return ` + return ` @@ -105,18 +105,18 @@ function loadingSVG(size = "25px", className = "", style = "") { `; } function loadingWithContainer(className) { - return `
${loadingSVG( - "25px", - "", - "margin:auto" - )}
`; + return `
${loadingSVG( + "25px", + "", + "margin:auto" + )}
`; } // loading placeholder for users list item function listItemLoading(items) { - let template = ""; - for (let i = 0; i < items; i++) { - template += ` + let template = ""; + for (let i = 0; i < items; i++) { + template += `
@@ -133,15 +133,15 @@ function listItemLoading(items) {
`; - } - return template; + } + return template; } // loading placeholder for avatars function avatarLoading(items) { - let template = ""; - for (let i = 0; i < items; i++) { - template += ` + let template = ""; + for (let i = 0; i < items; i++) { + template += `
@@ -156,471 +156,505 @@ function avatarLoading(items) {
`; - } - return template; + } + return template; } // While sending a message, show this temporary message card. function sendTempMessageCard(message, id) { - return ` -
-
-
- ${message} - - - -
-
-
+ return ` +
+
+
+ ${message} + + + +
+
+
`; } // upload image preview card. function attachmentTemplate(fileType, fileName, imgURL = null) { - if (fileType != "image") { - return ( - ` -
- -

` + - escapeHtml(fileName) + - `

-
+ if (fileType != "image") { + return ( + ` +
+ +

` + + escapeHtml(fileName) + + `

+
` - ); - } else { - return ( - ` + ); + } else { + return ( + `
- -
-

` + - escapeHtml(fileName) + - `

+ +
+

` + + escapeHtml(fileName) + + `

` - ); - } + ); + } } // Active Status Circle function activeStatusCircle() { - return ``; + return ``; } /** - *------------------------------------------------------------- - * Css Media Queries [For responsive design] - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Css Media Queries [For responsive design] +*------------------------------------------------------------- +*/ $(window).resize(function () { - cssMediaQueries(); + cssMediaQueries(); }); function cssMediaQueries() { - if (window.matchMedia("(min-width: 980px)").matches) { - $(".messenger-listView").removeAttr("style"); - } - if (window.matchMedia("(max-width: 980px)").matches) { - $("body") - .find(".messenger-list-item") - .find("tr[data-action]") - .attr("data-action", "1"); - $("body").find(".favorite-list-item").find("div").attr("data-action", "1"); - } else { - $("body") - .find(".messenger-list-item") - .find("tr[data-action]") - .attr("data-action", "0"); - $("body").find(".favorite-list-item").find("div").attr("data-action", "0"); - } + if (window.matchMedia("(min-width: 980px)").matches) { + $(".messenger-listView").removeAttr("style"); + } + if (window.matchMedia("(max-width: 980px)").matches) { + $("body") + .find(".messenger-list-item") + .find("tr[data-action]") + .attr("data-action", "1"); + $("body").find(".favorite-list-item").find("div").attr("data-action", "1"); + } else { + $("body") + .find(".messenger-list-item") + .find("tr[data-action]") + .attr("data-action", "0"); + $("body").find(".favorite-list-item").find("div").attr("data-action", "0"); + } } /** - *------------------------------------------------------------- - * App Modal - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* App Modal +*------------------------------------------------------------- +*/ let app_modal = function ({ - show = true, - name, - data = 0, - buttons = true, - header = null, - body = null, + show = true, + name, + data = 0, + buttons = true, + header = null, + body = null, }) { - const modal = $(".app-modal[data-name=" + name + "]"); - // header - header ? modal.find(".app-modal-header").html(header) : ""; - - // body - body ? modal.find(".app-modal-body").html(body) : ""; - - // buttons - buttons == true - ? modal.find(".app-modal-footer").show() - : modal.find(".app-modal-footer").hide(); - - // show / hide - if (show == true) { - modal.show(); - $(".app-modal-card[data-name=" + name + "]").addClass("app-show-modal"); - $(".app-modal-card[data-name=" + name + "]").attr("data-modal", data); - } else { - modal.hide(); - $(".app-modal-card[data-name=" + name + "]").removeClass("app-show-modal"); - $(".app-modal-card[data-name=" + name + "]").attr("data-modal", data); - } + const modal = $(".app-modal[data-name=" + name + "]"); + // header + header ? modal.find(".app-modal-header").html(header) : ""; + + // body + body ? modal.find(".app-modal-body").html(body) : ""; + + // buttons + buttons == true + ? modal.find(".app-modal-footer").show() + : modal.find(".app-modal-footer").hide(); + + // show / hide + if (show == true) { + modal.show(); + $(".app-modal-card[data-name=" + name + "]").addClass("app-show-modal"); + $(".app-modal-card[data-name=" + name + "]").attr("data-modal", data); + } else { + modal.hide(); + $(".app-modal-card[data-name=" + name + "]").removeClass("app-show-modal"); + $(".app-modal-card[data-name=" + name + "]").attr("data-modal", data); + } }; /** - *------------------------------------------------------------- - * Slide to bottom on [action] - e.g. [message received, sent, loaded] - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Slide to bottom on [action] - e.g. [message received, sent, loaded] +*------------------------------------------------------------- +*/ function scrollToBottom(container) { - $(container) - .stop() - .animate({ - scrollTop: $(container)[0].scrollHeight, - }); + $(container) + .stop() + .animate({ + scrollTop: $(container)[0].scrollHeight, + }); } /** - *------------------------------------------------------------- - * click and drag to scroll - function - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* click and drag to scroll - function +*------------------------------------------------------------- +*/ function hScroller(scroller) { - const slider = document.querySelector(scroller); - let isDown = false; - let startX; - let scrollLeft; - - slider.addEventListener("mousedown", (e) => { - isDown = true; - startX = e.pageX - slider.offsetLeft; - scrollLeft = slider.scrollLeft; - }); - slider.addEventListener("mouseleave", () => { - isDown = false; - }); - slider.addEventListener("mouseup", () => { - isDown = false; - }); - slider.addEventListener("mousemove", (e) => { - if (!isDown) return; - e.preventDefault(); - const x = e.pageX - slider.offsetLeft; - const walk = (x - startX) * 1; - slider.scrollLeft = scrollLeft - walk; - }); + const slider = document.querySelector(scroller); + let isDown = false; + let startX; + let scrollLeft; + + slider.addEventListener("mousedown", (e) => { + isDown = true; + startX = e.pageX - slider.offsetLeft; + scrollLeft = slider.scrollLeft; + }); + slider.addEventListener("mouseleave", () => { + isDown = false; + }); + slider.addEventListener("mouseup", () => { + isDown = false; + }); + slider.addEventListener("mousemove", (e) => { + if (!isDown) return; + e.preventDefault(); + const x = e.pageX - slider.offsetLeft; + const walk = (x - startX) * 1; + slider.scrollLeft = scrollLeft - walk; + }); } /** - *------------------------------------------------------------- - * Disable/enable message form fields, messaging container... - * on load info or if needed elsewhere. - * - * Default : true - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Disable/enable message form fields, messaging container... +* on load info or if needed elsewhere. +* +* Default : true +*------------------------------------------------------------- +*/ function disableOnLoad(disable = true) { - if (disable) { - // hide star button - $(".add-to-favorite").hide(); - // hide send card - $(".messenger-sendCard").hide(); - // add loading opacity to messages container - messagesContainer.css("opacity", ".5"); - // disable message form fields - messageInput.attr("readonly", "readonly"); - $("#message-form button").attr("disabled", "disabled"); - $(".upload-attachment").attr("disabled", "disabled"); - } else { - // show star button - if (getMessengerId() != auth_id) { - $(".add-to-favorite").show(); - } - // show send card - $(".messenger-sendCard").show(); - // remove loading opacity to messages container - messagesContainer.css("opacity", "1"); - // enable message form fields - messageInput.removeAttr("readonly"); - $("#message-form button").removeAttr("disabled"); - $(".upload-attachment").removeAttr("disabled"); - } + if (disable) { + // hide star button + $(".add-to-favorite").hide(); + // hide send card + $(".messenger-sendCard").hide(); + // add loading opacity to messages container + messagesContainer.css("opacity", ".5"); + // disable message form fields + messageInput.attr("readonly", "readonly"); + $("#message-form button").attr("disabled", "disabled"); + $(".upload-attachment").attr("disabled", "disabled"); + } else { + // show star button + if (getMessengerId() != auth_id) { + $(".add-to-favorite").show(); + } + // show send card + $(".messenger-sendCard").show(); + // remove loading opacity to messages container + messagesContainer.css("opacity", "1"); + // enable message form fields + messageInput.removeAttr("readonly"); + $("#message-form button").removeAttr("disabled"); + $(".upload-attachment").removeAttr("disabled"); + } } /** - *------------------------------------------------------------- - * Error message card - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Error message card +*------------------------------------------------------------- +*/ function errorMessageCard(id) { - messagesContainer - .find(".message-card[data-id=" + id + "]") - .addClass("mc-error"); - messagesContainer - .find(".message-card[data-id=" + id + "]") - .find("svg.loadingSVG") - .remove(); - messagesContainer - .find(".message-card[data-id=" + id + "] p") - .prepend(''); + messagesContainer + .find(".message-card[data-id=" + id + "]") + .addClass("mc-error"); + messagesContainer + .find(".message-card[data-id=" + id + "]") + .find("svg.loadingSVG") + .remove(); + messagesContainer + .find(".message-card[data-id=" + id + "] p") + .prepend(''); } /** - *------------------------------------------------------------- - * Fetch id data (user/group) and update the view - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Fetch id data (user/group) and update the view +*------------------------------------------------------------- +*/ function IDinfo(id) { - // clear temporary message id - temporaryMsgId = 0; - // clear typing now - typingNow = 0; - // show loading bar - NProgress.start(); - // disable message form - disableOnLoad(); - if (messenger != 0) { - // get shared photos - getSharedPhotos(id); - // Get info - $.ajax({ - url: url + "/idInfo", - method: "POST", - data: { _token: csrfToken, id }, - dataType: "JSON", - success: (data) => { - if (!data?.fetch) { - NProgress.done(); - NProgress.remove(); - return; - } - // avatar photo - $(".messenger-infoView") - .find(".avatar") - .css("background-image", 'url("' + data.user_avatar + '")'); - $(".header-avatar").css( - "background-image", - 'url("' + data.user_avatar + '")' - ); - // Show shared and actions - $(".messenger-infoView-btns .delete-conversation").show(); - $(".messenger-infoView-shared").show(); - // fetch messages - fetchMessages(id, true); - // focus on messaging input - messageInput.focus(); - // update info in view - $(".messenger-infoView .info-name").text(data.fetch.name); - $(".m-header-messaging .user-name").text(data.fetch.name); - // Star status - data.favorite > 0 - ? $(".add-to-favorite").addClass("favorite") - : $(".add-to-favorite").removeClass("favorite"); - // form reset and focus - $("#message-form").trigger("reset"); - cancelAttachment(); - messageInput.focus(); - }, - error: () => { - console.error("Couldn't fetch user data!"); - // remove loading bar - NProgress.done(); - NProgress.remove(); - }, - }); - } else { - // remove loading bar - NProgress.done(); - NProgress.remove(); - } + // clear temporary message id + temporaryMsgId = 0; + // clear typing now + typingNow = 0; + // show loading bar + NProgress.start(); + // disable message form + disableOnLoad(); + if (messenger != 0) { + // get shared photos + getSharedPhotos(id); + // Get info + $.ajax({ + url: url + "/idInfo", + method: "POST", + data: { _token: csrfToken, id }, + dataType: "JSON", + success: (data) => { + if (!data?.fetch) { + NProgress.done(); + NProgress.remove(); + return; + } + // avatar photo + $(".messenger-infoView") + .find(".avatar") + .css("background-image", 'url("' + data.user_avatar + '")'); + $(".header-avatar").css( + "background-image", + 'url("' + data.user_avatar + '")' + ); + // Show shared and actions + $(".messenger-infoView-btns .delete-conversation").show(); + $(".messenger-infoView-shared").show(); + // fetch messages + fetchMessages(id, true); + // focus on messaging input + messageInput.focus(); + // update info in view + $(".messenger-infoView .info-name").text(data.fetch.name); + $(".m-header-messaging .user-name").text(data.fetch.name); + // Star status + data.favorite > 0 + ? $(".add-to-favorite").addClass("favorite") + : $(".add-to-favorite").removeClass("favorite"); + // form reset and focus + $("#message-form").trigger("reset"); + cancelAttachment(); + messageInput.focus(); + }, + error: () => { + console.error("Couldn't fetch user data!"); + // remove loading bar + NProgress.done(); + NProgress.remove(); + }, + }); + } else { + // remove loading bar + NProgress.done(); + NProgress.remove(); + } } +// Function to handle sending audio messages +function sendAudioMessage(audioBlob, tempID, receiverId, csrfToken) { + const formData = new FormData(); + const fileName = 'audio.mp4'; + + formData.append('audio', audioBlob, fileName); + formData.append("temporaryMsgId", tempID); + formData.append("id", receiverId); + formData.append("_token", csrfToken); + + // Append a temporary message card for the voice message + messagesContainer.find(".messages").append( + sendTempMessageCard("Sending voice message", tempID) + ); + + return formData; +} /** - *------------------------------------------------------------- - * Send message function - *------------------------------------------------------------- - */ -function sendMessage() { - temporaryMsgId += 1; - let tempID = `temp_${temporaryMsgId}`; - let hasFile = !!$(".upload-attachment").val(); - const inputValue = $.trim(messageInput.val()); - if (inputValue.length > 0 || hasFile) { - const formData = new FormData($("#message-form")[0]); - formData.append("id", getMessengerId()); - formData.append("temporaryMsgId", tempID); - formData.append("_token", csrfToken); - $.ajax({ - url: $("#message-form").attr("action"), - method: "POST", - data: formData, - dataType: "JSON", - processData: false, - contentType: false, - beforeSend: () => { - // remove message hint - $(".messages").find(".message-hint").hide(); - // append a temporary message card - if (hasFile) { - messagesContainer - .find(".messages") - .append( - sendTempMessageCard( - inputValue + "\n" + loadingSVG("28px"), - tempID - ) - ); - } else { - messagesContainer - .find(".messages") - .append(sendTempMessageCard(inputValue, tempID)); - } - // scroll to bottom - scrollToBottom(messagesContainer); - messageInput.css({ height: "42px" }); - // form reset and focus - $("#message-form").trigger("reset"); - cancelAttachment(); - messageInput.focus(); - }, - success: (data) => { - if (data.error > 0) { - // message card error status - errorMessageCard(tempID); - console.error(data.error_msg); - } else { - // update contact item - updateContactItem(getMessengerId()); - // temporary message card - const tempMsgCardElement = messagesContainer.find( - `.message-card[data-id=${data.tempID}]` - ); - // add the message card coming from the server before the temp-card - tempMsgCardElement.before(data.message); - // then, remove the temporary message card - tempMsgCardElement.remove(); - // scroll to bottom - scrollToBottom(messagesContainer); - // send contact item updates - sendContactItemUpdates(true); - } - }, - error: () => { - // message card error status - errorMessageCard(tempID); - // error log - console.error( - "Failed sending the message! Please, check your server response." - ); - }, - }); - } - return false; +*------------------------------------------------------------- +* Send message function +*------------------------------------------------------------- +*/ + +function sendMessage(isVoiceMessage = false, audioBlob = null, duration = null) { + temporaryMsgId += 1; + const tempID = `temp_${temporaryMsgId}`; + const receiverId = getMessengerId(); + let formData; + + if (isVoiceMessage && audioBlob) { + formData = sendAudioMessage(audioBlob, tempID, receiverId, csrfToken); + } else { + // Existing code for handling text or file messages + let hasFile = !!$(".upload-attachment").val(); + const inputValue = $.trim(messageInput.val()); + + if (inputValue.length === 0 && !hasFile) { + return false; + } + + formData = new FormData($("#message-form")[0]); + formData.append("id", receiverId); + formData.append("temporaryMsgId", tempID); + formData.append("_token", csrfToken); + + if (hasFile) { + messagesContainer + .find(".messages") + .append( + sendTempMessageCard( + inputValue + "\n" + loadingSVG("28px"), + tempID + ) + ); + } else { + messagesContainer + .find(".messages") + .append(sendTempMessageCard(inputValue, tempID)); + } + + $("#message-form").trigger("reset"); + cancelAttachment(); + messageInput.focus(); + } + + scrollToBottom(messagesContainer); + + // Send the message via AJAX + $.ajax({ + url: $("#message-form").attr("action"), + method: "POST", + data: formData, + dataType: "JSON", + processData: false, + contentType: false, + beforeSend: () => { + $(".messages").find(".message-hint").hide(); + }, + success: (data) => { + if (data.error > 0) { + errorMessageCard(tempID); + console.error(data.error_msg); + } else { + updateContactItem(receiverId); + + const tempMsgCardElement = messagesContainer.find( + `.message-card[data-id=${data.tempID}]` + ); + + let $message = data.message.replace(/()\d{1,2}:\d{2}(<\/span>)/, `$1${duration}$2`); + tempMsgCardElement.before($message); + tempMsgCardElement.remove(); + + const actualMessageIdMatch = data.message.match(/data-id="([a-z0-9\-]+)"/); + const actualMessageId = actualMessageIdMatch ? actualMessageIdMatch[1] : null; + + if (actualMessageId) { + setTimeout(() => { + initializeAudioPlayer(`#player-${actualMessageId}`, actualMessageId); + }, 100); + } + + scrollToBottom(messagesContainer); + sendContactItemUpdates(true); + } + }, + error: () => { + errorMessageCard(tempID); + console.error("Failed sending the message! Please, check your server response."); + }, + }); + + return false; } - /** - *------------------------------------------------------------- - * Fetch messages from database - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Fetch messages from database +*------------------------------------------------------------- +*/ let messagesPage = 1; let noMoreMessages = false; let messagesLoading = false; function setMessagesLoading(loading = false) { - if (!loading) { - messagesContainer.find(".messages").find(".loading-messages").remove(); - NProgress.done(); - NProgress.remove(); - } else { - messagesContainer - .find(".messages") - .prepend(loadingWithContainer("loading-messages")); - } - messagesLoading = loading; + if (!loading) { + messagesContainer.find(".messages").find(".loading-messages").remove(); + NProgress.done(); + NProgress.remove(); + } else { + messagesContainer + .find(".messages") + .prepend(loadingWithContainer("loading-messages")); + } + messagesLoading = loading; } function fetchMessages(id, newFetch = false) { - if (newFetch) { - messagesPage = 1; - noMoreMessages = false; - } - if (messenger != 0 && !noMoreMessages && !messagesLoading) { - const messagesElement = messagesContainer.find(".messages"); - setMessagesLoading(true); - $.ajax({ - url: url + "/fetchMessages", - method: "POST", - data: { - _token: csrfToken, - id: id, - page: messagesPage, - }, - dataType: "JSON", - success: (data) => { - setMessagesLoading(false); - if (messagesPage == 1) { - messagesElement.html(data.messages); - scrollToBottom(messagesContainer); - } else { - const lastMsg = messagesElement.find( - messagesElement.find(".message-card")[0] - ); - const curOffset = - lastMsg.offset().top - messagesContainer.scrollTop(); - messagesElement.prepend(data.messages); - messagesContainer.scrollTop(lastMsg.offset().top - curOffset); - } - // trigger seen event - makeSeen(true); - // Pagination lock & messages page - noMoreMessages = messagesPage >= data?.last_page; - if (!noMoreMessages) messagesPage += 1; - // Enable message form if messenger not = 0; means if data is valid - if (messenger != 0) { - disableOnLoad(false); - } - }, - error: (error) => { - setMessagesLoading(false); - console.error(error); - }, - }); - } + if (newFetch) { + messagesPage = 1; + noMoreMessages = false; + } + if (messenger != 0 && !noMoreMessages && !messagesLoading) { + const messagesElement = messagesContainer.find(".messages"); + setMessagesLoading(true); + $.ajax({ + url: url + "/fetchMessages", + method: "POST", + data: { + _token: csrfToken, + id: id, + page: messagesPage, + }, + dataType: "JSON", + success: (data) => { + setMessagesLoading(false); + if (messagesPage == 1) { + messagesElement.html(data.messages); + //update Voice Message & intialize wavesurfer + rederWavesurfers(); + + scrollToBottom(messagesContainer); + } else { + const lastMsg = messagesElement.find( + messagesElement.find(".message-card")[0] + ); + const curOffset = + lastMsg.offset().top - messagesContainer.scrollTop(); + messagesElement.prepend(data.messages); + messagesContainer.scrollTop(lastMsg.offset().top - curOffset); + } + // trigger seen event + makeSeen(true); + // Pagination lock & messages page + noMoreMessages = messagesPage >= data?.last_page; + if (!noMoreMessages) messagesPage += 1; + // Enable message form if messenger not = 0; means if data is valid + if (messenger != 0) { + disableOnLoad(false); + } + }, + error: (error) => { + setMessagesLoading(false); + console.error(error); + }, + }); + } } /** - *------------------------------------------------------------- - * Cancel file attached in the message. - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Cancel file attached in the message. +*------------------------------------------------------------- +*/ function cancelAttachment() { - $(".messenger-sendCard").find(".attachment-preview").remove(); - $(".upload-attachment").replaceWith( - $(".upload-attachment").val("").clone(true) - ); + $(".messenger-sendCard").find(".attachment-preview").remove(); + $(".upload-attachment").replaceWith( + $(".upload-attachment").val("").clone(true) + ); } /** - *------------------------------------------------------------- - * Cancel updating avatar in settings - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Cancel updating avatar in settings +*------------------------------------------------------------- +*/ function cancelUpdatingAvatar() { - $(".upload-avatar-preview").css("background-image", defaultAvatarInSettings); - $(".upload-avatar").replaceWith($(".upload-avatar").val("").clone(true)); + $(".upload-avatar-preview").css("background-image", defaultAvatarInSettings); + $(".upload-avatar").replaceWith($(".upload-avatar").val("").clone(true)); } /** - *------------------------------------------------------------- - * Pusher channels and event listening.. - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Pusher channels and event listening.. +*------------------------------------------------------------- +*/ // subscribe to the channel const channelName = "private-chatify"; @@ -629,76 +663,76 @@ var clientSendChannel; var clientListenChannel; function initClientChannel() { - if (getMessengerId()) { - clientSendChannel = pusher.subscribe(`${channelName}.${getMessengerId()}`); - clientListenChannel = pusher.subscribe(`${channelName}.${auth_id}`); - } + if (getMessengerId()) { + clientSendChannel = pusher.subscribe(`${channelName}.${getMessengerId()}`); + clientListenChannel = pusher.subscribe(`${channelName}.${auth_id}`); + } } initClientChannel(); // Listen to messages, and append if data received channel.bind("messaging", function (data) { - if (data.from_id == getMessengerId() && data.to_id == auth_id) { - $(".messages").find(".message-hint").remove(); - messagesContainer.find(".messages").append(data.message); - scrollToBottom(messagesContainer); - makeSeen(true); - // remove unseen counter for the user from the contacts list - $(".messenger-list-item[data-contact=" + getMessengerId() + "]") - .find("tr>td>b") - .remove(); - } - - playNotificationSound( - "new_message", - !(data.from_id == getMessengerId() && data.to_id == auth_id) - ); + if (data.from_id == getMessengerId() && data.to_id == auth_id) { + $(".messages").find(".message-hint").remove(); + messagesContainer.find(".messages").append(data.message); + scrollToBottom(messagesContainer); + makeSeen(true); + // remove unseen counter for the user from the contacts list + $(".messenger-list-item[data-contact=" + getMessengerId() + "]") + .find("tr>td>b") + .remove(); + } + + playNotificationSound( + "new_message", + !(data.from_id == getMessengerId() && data.to_id == auth_id) + ); }); // listen to typing indicator clientListenChannel.bind("client-typing", function (data) { - if (data.from_id == getMessengerId() && data.to_id == auth_id) { - data.typing == true - ? messagesContainer.find(".typing-indicator").show() - : messagesContainer.find(".typing-indicator").hide(); - } - // scroll to bottom - scrollToBottom(messagesContainer); + if (data.from_id == getMessengerId() && data.to_id == auth_id) { + data.typing == true + ? messagesContainer.find(".typing-indicator").show() + : messagesContainer.find(".typing-indicator").hide(); + } + // scroll to bottom + scrollToBottom(messagesContainer); }); // listen to seen event clientListenChannel.bind("client-seen", function (data) { - if (data.from_id == getMessengerId() && data.to_id == auth_id) { - if (data.seen == true) { - $(".message-time") - .find(".fa-check") - .before(' '); - $(".message-time").find(".fa-check").remove(); - } - } + if (data.from_id == getMessengerId() && data.to_id == auth_id) { + if (data.seen == true) { + $(".message-time") + .find(".fa-check") + .before(' '); + $(".message-time").find(".fa-check").remove(); + } + } }); // listen to contact item updates event clientListenChannel.bind("client-contactItem", function (data) { - if (data.to == auth_id) { - if (data.update) { - updateContactItem(data.from); - } else { - console.error("Can not update contact item!"); - } - } + if (data.to == auth_id) { + if (data.update) { + updateContactItem(data.from); + } else { + console.error("Can not update contact item!"); + } + } }); // listen on message delete event clientListenChannel.bind("client-messageDelete", function (data) { - $("body").find(`.message-card[data-id=${data.id}]`).remove(); + $("body").find(`.message-card[data-id=${data.id}]`).remove(); }); // listen on delete conversation event clientListenChannel.bind("client-deleteConversation", function (data) { - if (data.from == getMessengerId() && data.to == auth_id) { - $("body").find(`.messages`).html(""); - $(".messages").find(".message-hint").show(); - } + if (data.from == getMessengerId() && data.to == auth_id) { + $("body").find(`.messages`).html(""); + $(".messages").find(".message-hint").show(); + } }); // ------------------------------------- // presence channel [User Active Status] @@ -706,923 +740,923 @@ var activeStatusChannel = pusher.subscribe("presence-activeStatus"); // Joined activeStatusChannel.bind("pusher:member_added", function (member) { - setActiveStatus(1); - $(".messenger-list-item[data-contact=" + member.id + "]") - .find(".activeStatus") - .remove(); - $(".messenger-list-item[data-contact=" + member.id + "]") - .find(".avatar") - .before(activeStatusCircle()); + setActiveStatus(1); + $(".messenger-list-item[data-contact=" + member.id + "]") + .find(".activeStatus") + .remove(); + $(".messenger-list-item[data-contact=" + member.id + "]") + .find(".avatar") + .before(activeStatusCircle()); }); // Leaved activeStatusChannel.bind("pusher:member_removed", function (member) { - setActiveStatus(0); - $(".messenger-list-item[data-contact=" + member.id + "]") - .find(".activeStatus") - .remove(); + setActiveStatus(0); + $(".messenger-list-item[data-contact=" + member.id + "]") + .find(".activeStatus") + .remove(); }); function handleVisibilityChange() { - if (!document.hidden) { - makeSeen(true); - } + if (!document.hidden) { + makeSeen(true); + } } document.addEventListener("visibilitychange", handleVisibilityChange, false); /** - *------------------------------------------------------------- - * Trigger typing event - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Trigger typing event +*------------------------------------------------------------- +*/ function isTyping(status) { - return clientSendChannel.trigger("client-typing", { - from_id: auth_id, // Me - to_id: getMessengerId(), // Messenger - typing: status, - }); + return clientSendChannel.trigger("client-typing", { + from_id: auth_id, // Me + to_id: getMessengerId(), // Messenger + typing: status, + }); } /** - *------------------------------------------------------------- - * Trigger seen event - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Trigger seen event +*------------------------------------------------------------- +*/ function makeSeen(status) { - if (document?.hidden) { - return; - } - // remove unseen counter for the user from the contacts list - $(".messenger-list-item[data-contact=" + getMessengerId() + "]") - .find("tr>td>b") - .remove(); - // seen - $.ajax({ - url: url + "/makeSeen", - method: "POST", - data: { _token: csrfToken, id: getMessengerId() }, - dataType: "JSON", - }); - return clientSendChannel.trigger("client-seen", { - from_id: auth_id, // Me - to_id: getMessengerId(), // Messenger - seen: status, - }); + if (document?.hidden) { + return; + } + // remove unseen counter for the user from the contacts list + $(".messenger-list-item[data-contact=" + getMessengerId() + "]") + .find("tr>td>b") + .remove(); + // seen + $.ajax({ + url: url + "/makeSeen", + method: "POST", + data: { _token: csrfToken, id: getMessengerId() }, + dataType: "JSON", + }); + return clientSendChannel.trigger("client-seen", { + from_id: auth_id, // Me + to_id: getMessengerId(), // Messenger + seen: status, + }); } /** - *------------------------------------------------------------- - * Trigger contact item updates - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Trigger contact item updates +*------------------------------------------------------------- +*/ function sendContactItemUpdates(status) { - return clientSendChannel.trigger("client-contactItem", { - from: auth_id, // Me - to: getMessengerId(), // Messenger - update: status, - }); + return clientSendChannel.trigger("client-contactItem", { + from: auth_id, // Me + to: getMessengerId(), // Messenger + update: status, + }); } /** - *------------------------------------------------------------- - * Trigger message delete - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Trigger message delete +*------------------------------------------------------------- +*/ function sendMessageDeleteEvent(messageId) { - return clientSendChannel.trigger("client-messageDelete", { - id: messageId, - }); + return clientSendChannel.trigger("client-messageDelete", { + id: messageId, + }); } /** - *------------------------------------------------------------- - * Trigger delete conversation - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Trigger delete conversation +*------------------------------------------------------------- +*/ function sendDeleteConversationEvent() { - return clientSendChannel.trigger("client-deleteConversation", { - from: auth_id, - to: getMessengerId(), - }); + return clientSendChannel.trigger("client-deleteConversation", { + from: auth_id, + to: getMessengerId(), + }); } /** - *------------------------------------------------------------- - * Check internet connection using pusher states - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Check internet connection using pusher states +*------------------------------------------------------------- +*/ function checkInternet(state, selector) { - let net_errs = 0; - const messengerTitle = $(".messenger-headTitle"); - switch (state) { - case "connected": - if (net_errs < 1) { - messengerTitle.text(messengerTitleDefault); - selector.addClass("successBG-rgba"); - selector.find("span").hide(); - selector.slideDown("fast", function () { - selector.find(".ic-connected").show(); - }); - setTimeout(function () { - $(".internet-connection").slideUp("fast"); - }, 3000); - } - break; - case "connecting": - messengerTitle.text($(".ic-connecting").text()); - selector.removeClass("successBG-rgba"); - selector.find("span").hide(); - selector.slideDown("fast", function () { - selector.find(".ic-connecting").show(); - }); - net_errs = 1; - break; - // Not connected - default: - messengerTitle.text($(".ic-noInternet").text()); - selector.removeClass("successBG-rgba"); - selector.find("span").hide(); - selector.slideDown("fast", function () { - selector.find(".ic-noInternet").show(); - }); - net_errs = 1; - break; - } + let net_errs = 0; + const messengerTitle = $(".messenger-headTitle"); + switch (state) { + case "connected": + if (net_errs < 1) { + messengerTitle.text(messengerTitleDefault); + selector.addClass("successBG-rgba"); + selector.find("span").hide(); + selector.slideDown("fast", function () { + selector.find(".ic-connected").show(); + }); + setTimeout(function () { + $(".internet-connection").slideUp("fast"); + }, 3000); + } + break; + case "connecting": + messengerTitle.text($(".ic-connecting").text()); + selector.removeClass("successBG-rgba"); + selector.find("span").hide(); + selector.slideDown("fast", function () { + selector.find(".ic-connecting").show(); + }); + net_errs = 1; + break; + // Not connected + default: + messengerTitle.text($(".ic-noInternet").text()); + selector.removeClass("successBG-rgba"); + selector.find("span").hide(); + selector.slideDown("fast", function () { + selector.find(".ic-noInternet").show(); + }); + net_errs = 1; + break; + } } /** - *------------------------------------------------------------- - * Get contacts - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Get contacts +*------------------------------------------------------------- +*/ let contactsPage = 1; let contactsLoading = false; let noMoreContacts = false; function setContactsLoading(loading = false) { - if (!loading) { - $(".listOfContacts").find(".loading-contacts").remove(); - } else { - $(".listOfContacts").append( - `
${listItemLoading(4)}
` - ); - } - contactsLoading = loading; + if (!loading) { + $(".listOfContacts").find(".loading-contacts").remove(); + } else { + $(".listOfContacts").append( + `
${listItemLoading(4)}
` + ); + } + contactsLoading = loading; } function getContacts() { - if (!contactsLoading && !noMoreContacts) { - setContactsLoading(true); - $.ajax({ - url: url + "/getContacts", - method: "GET", - data: { _token: csrfToken, page: contactsPage }, - dataType: "JSON", - success: (data) => { - setContactsLoading(false); - if (contactsPage < 2) { - $(".listOfContacts").html(data.contacts); - } else { - $(".listOfContacts").append(data.contacts); - } - updateSelectedContact(); - // update data-action required with [responsive design] - cssMediaQueries(); - // Pagination lock & messages page - noMoreContacts = contactsPage >= data?.last_page; - if (!noMoreContacts) contactsPage += 1; - }, - error: (error) => { - setContactsLoading(false); - console.error(error); - }, - }); - } + if (!contactsLoading && !noMoreContacts) { + setContactsLoading(true); + $.ajax({ + url: url + "/getContacts", + method: "GET", + data: { _token: csrfToken, page: contactsPage }, + dataType: "JSON", + success: (data) => { + setContactsLoading(false); + if (contactsPage < 2) { + $(".listOfContacts").html(data.contacts); + } else { + $(".listOfContacts").append(data.contacts); + } + updateSelectedContact(); + // update data-action required with [responsive design] + cssMediaQueries(); + // Pagination lock & messages page + noMoreContacts = contactsPage >= data?.last_page; + if (!noMoreContacts) contactsPage += 1; + }, + error: (error) => { + setContactsLoading(false); + console.error(error); + }, + }); + } } /** - *------------------------------------------------------------- - * Update contact item - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Update contact item +*------------------------------------------------------------- +*/ function updateContactItem(user_id) { - if (user_id != auth_id) { - $.ajax({ - url: url + "/updateContacts", - method: "POST", - data: { - _token: csrfToken, - user_id, - }, - dataType: "JSON", - success: (data) => { - $(".listOfContacts") - .find(".messenger-list-item[data-contact=" + user_id + "]") - .remove(); - if (data.contactItem) $(".listOfContacts").prepend(data.contactItem); - if (user_id == getMessengerId()) updateSelectedContact(user_id); - // show/hide message hint (empty state message) - const totalContacts = - $(".listOfContacts").find(".messenger-list-item")?.length || 0; - if (totalContacts > 0) { - $(".listOfContacts").find(".message-hint").hide(); - } else { - $(".listOfContacts").find(".message-hint").show(); - } - // update data-action required with [responsive design] - cssMediaQueries(); - }, - error: (error) => { - console.error(error); - }, - }); - } + if (user_id != auth_id) { + $.ajax({ + url: url + "/updateContacts", + method: "POST", + data: { + _token: csrfToken, + user_id, + }, + dataType: "JSON", + success: (data) => { + $(".listOfContacts") + .find(".messenger-list-item[data-contact=" + user_id + "]") + .remove(); + if (data.contactItem) $(".listOfContacts").prepend(data.contactItem); + if (user_id == getMessengerId()) updateSelectedContact(user_id); + // show/hide message hint (empty state message) + const totalContacts = + $(".listOfContacts").find(".messenger-list-item")?.length || 0; + if (totalContacts > 0) { + $(".listOfContacts").find(".message-hint").hide(); + } else { + $(".listOfContacts").find(".message-hint").show(); + } + // update data-action required with [responsive design] + cssMediaQueries(); + }, + error: (error) => { + console.error(error); + }, + }); + } } /** - *------------------------------------------------------------- - * Star - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Star +*------------------------------------------------------------- +*/ function star(user_id) { - if (getMessengerId() != auth_id) { - $.ajax({ - url: url + "/star", - method: "POST", - data: { _token: csrfToken, user_id: user_id }, - dataType: "JSON", - success: (data) => { - data.status > 0 - ? $(".add-to-favorite").addClass("favorite") - : $(".add-to-favorite").removeClass("favorite"); - }, - error: () => { - console.error("Server error, check your response"); - }, - }); - } + if (getMessengerId() != auth_id) { + $.ajax({ + url: url + "/star", + method: "POST", + data: { _token: csrfToken, user_id: user_id }, + dataType: "JSON", + success: (data) => { + data.status > 0 + ? $(".add-to-favorite").addClass("favorite") + : $(".add-to-favorite").removeClass("favorite"); + }, + error: () => { + console.error("Server error, check your response"); + }, + }); + } } /** - *------------------------------------------------------------- - * Get favorite list - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Get favorite list +*------------------------------------------------------------- +*/ function getFavoritesList() { - $(".messenger-favorites").html(avatarLoading(4)); - $.ajax({ - url: url + "/favorites", - method: "POST", - data: { _token: csrfToken }, - dataType: "JSON", - success: (data) => { - if (data.count > 0) { - $(".favorites-section").show(); - $(".messenger-favorites").html(data.favorites); - } else { - $(".favorites-section").hide(); - } - // update data-action required with [responsive design] - cssMediaQueries(); - }, - error: () => { - console.error("Server error, check your response"); - }, - }); + $(".messenger-favorites").html(avatarLoading(4)); + $.ajax({ + url: url + "/favorites", + method: "POST", + data: { _token: csrfToken }, + dataType: "JSON", + success: (data) => { + if (data.count > 0) { + $(".favorites-section").show(); + $(".messenger-favorites").html(data.favorites); + } else { + $(".favorites-section").hide(); + } + // update data-action required with [responsive design] + cssMediaQueries(); + }, + error: () => { + console.error("Server error, check your response"); + }, + }); } /** - *------------------------------------------------------------- - * Get shared photos - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Get shared photos +*------------------------------------------------------------- +*/ function getSharedPhotos(user_id) { - $.ajax({ - url: url + "/shared", - method: "POST", - data: { _token: csrfToken, user_id: user_id }, - dataType: "JSON", - success: (data) => { - $(".shared-photos-list").html(data.shared); - }, - error: () => { - console.error("Server error, check your response"); - }, - }); + $.ajax({ + url: url + "/shared", + method: "POST", + data: { _token: csrfToken, user_id: user_id }, + dataType: "JSON", + success: (data) => { + $(".shared-photos-list").html(data.shared); + }, + error: () => { + console.error("Server error, check your response"); + }, + }); } /** - *------------------------------------------------------------- - * Search in messenger - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Search in messenger +*------------------------------------------------------------- +*/ let searchPage = 1; let noMoreDataSearch = false; let searchLoading = false; let searchTempVal = ""; function setSearchLoading(loading = false) { - if (!loading) { - $(".search-records").find(".loading-search").remove(); - } else { - $(".search-records").append( - `` - ); - } - searchLoading = loading; + if (!loading) { + $(".search-records").find(".loading-search").remove(); + } else { + $(".search-records").append( + `` + ); + } + searchLoading = loading; } function messengerSearch(input) { - if (input != searchTempVal) { - searchPage = 1; - noMoreDataSearch = false; - searchLoading = false; - } - searchTempVal = input; - if (!searchLoading && !noMoreDataSearch) { - if (searchPage < 2) { - $(".search-records").html(""); - } - setSearchLoading(true); - $.ajax({ - url: url + "/search", - method: "GET", - data: { _token: csrfToken, input: input, page: searchPage }, - dataType: "JSON", - success: (data) => { - setSearchLoading(false); - if (searchPage < 2) { - $(".search-records").html(data.records); - } else { - $(".search-records").append(data.records); - } - // update data-action required with [responsive design] - cssMediaQueries(); - // Pagination lock & messages page - noMoreDataSearch = searchPage >= data?.last_page; - if (!noMoreDataSearch) searchPage += 1; - }, - error: (error) => { - setSearchLoading(false); - console.error(error); - }, - }); - } + if (input != searchTempVal) { + searchPage = 1; + noMoreDataSearch = false; + searchLoading = false; + } + searchTempVal = input; + if (!searchLoading && !noMoreDataSearch) { + if (searchPage < 2) { + $(".search-records").html(""); + } + setSearchLoading(true); + $.ajax({ + url: url + "/search", + method: "GET", + data: { _token: csrfToken, input: input, page: searchPage }, + dataType: "JSON", + success: (data) => { + setSearchLoading(false); + if (searchPage < 2) { + $(".search-records").html(data.records); + } else { + $(".search-records").append(data.records); + } + // update data-action required with [responsive design] + cssMediaQueries(); + // Pagination lock & messages page + noMoreDataSearch = searchPage >= data?.last_page; + if (!noMoreDataSearch) searchPage += 1; + }, + error: (error) => { + setSearchLoading(false); + console.error(error); + }, + }); + } } /** - *------------------------------------------------------------- - * Delete Conversation - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Delete Conversation +*------------------------------------------------------------- +*/ function deleteConversation(id) { - $.ajax({ - url: url + "/deleteConversation", - method: "POST", - data: { _token: csrfToken, id: id }, - dataType: "JSON", - beforeSend: () => { - // hide delete modal - app_modal({ - show: false, - name: "delete", - }); - // Show waiting alert modal - app_modal({ - show: true, - name: "alert", - buttons: false, - body: loadingSVG("32px", null, "margin:auto"), - }); - }, - success: (data) => { - // delete contact from the list - $(".listOfContacts") - .find(".messenger-list-item[data-contact=" + id + "]") - .remove(); - // refresh info - IDinfo(id); - - if (!data.deleted) - return alert("Error occurred, messages can not be deleted!"); - - // Hide waiting alert modal - app_modal({ - show: false, - name: "alert", - buttons: true, - body: "", - }); - - sendDeleteConversationEvent(); - - // update contact list item - sendContactItemUpdates(true); - }, - error: () => { - console.error("Server error, check your response"); - }, - }); + $.ajax({ + url: url + "/deleteConversation", + method: "POST", + data: { _token: csrfToken, id: id }, + dataType: "JSON", + beforeSend: () => { + // hide delete modal + app_modal({ + show: false, + name: "delete", + }); + // Show waiting alert modal + app_modal({ + show: true, + name: "alert", + buttons: false, + body: loadingSVG("32px", null, "margin:auto"), + }); + }, + success: (data) => { + // delete contact from the list + $(".listOfContacts") + .find(".messenger-list-item[data-contact=" + id + "]") + .remove(); + // refresh info + IDinfo(id); + + if (!data.deleted) + return alert("Error occurred, messages can not be deleted!"); + + // Hide waiting alert modal + app_modal({ + show: false, + name: "alert", + buttons: true, + body: "", + }); + + sendDeleteConversationEvent(); + + // update contact list item + sendContactItemUpdates(true); + }, + error: () => { + console.error("Server error, check your response"); + }, + }); } /** - *------------------------------------------------------------- - * Delete Message By ID - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Delete Message By ID +*------------------------------------------------------------- +*/ function deleteMessage(id) { - $.ajax({ - url: url + "/deleteMessage", - method: "POST", - data: { _token: csrfToken, id: id }, - dataType: "JSON", - beforeSend: () => { - // hide delete modal - app_modal({ - show: false, - name: "delete", - }); - // Show waiting alert modal - app_modal({ - show: true, - name: "alert", - buttons: false, - body: loadingSVG("32px", null, "margin:auto"), - }); - }, - success: (data) => { - $(".messages").find(`.message-card[data-id=${id}]`).remove(); - if (!data.deleted) - console.error("Error occurred, message can not be deleted!"); - - sendMessageDeleteEvent(id); - - // Hide waiting alert modal - app_modal({ - show: false, - name: "alert", - buttons: true, - body: "", - }); - }, - error: () => { - console.error("Server error, check your response"); - }, - }); + $.ajax({ + url: url + "/deleteMessage", + method: "POST", + data: { _token: csrfToken, id: id }, + dataType: "JSON", + beforeSend: () => { + // hide delete modal + app_modal({ + show: false, + name: "delete", + }); + // Show waiting alert modal + app_modal({ + show: true, + name: "alert", + buttons: false, + body: loadingSVG("32px", null, "margin:auto"), + }); + }, + success: (data) => { + $(".messages").find(`.message-card[data-id=${id}]`).remove(); + if (!data.deleted) + console.error("Error occurred, message can not be deleted!"); + + sendMessageDeleteEvent(id); + + // Hide waiting alert modal + app_modal({ + show: false, + name: "alert", + buttons: true, + body: "", + }); + }, + error: () => { + console.error("Server error, check your response"); + }, + }); } /** - *------------------------------------------------------------- - * Update Settings - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Update Settings +*------------------------------------------------------------- +*/ function updateSettings() { - const formData = new FormData($("#update-settings")[0]); - if (messengerColor) { - formData.append("messengerColor", messengerColor); - } - if (dark_mode) { - formData.append("dark_mode", dark_mode); - } - $.ajax({ - url: url + "/updateSettings", - method: "POST", - data: formData, - dataType: "JSON", - processData: false, - contentType: false, - beforeSend: () => { - // close settings modal - app_modal({ - show: false, - name: "settings", - }); - // Show waiting alert modal - app_modal({ - show: true, - name: "alert", - buttons: false, - body: loadingSVG("32px", null, "margin:auto"), - }); - }, - success: (data) => { - if (data.error) { - // Show error message in alert modal - app_modal({ - show: true, - name: "alert", - buttons: true, - body: data.msg, - }); - } else { - // Hide alert modal - app_modal({ - show: false, - name: "alert", - buttons: true, - body: "", - }); - - // reload the page - location.reload(true); - } - }, - error: () => { - console.error("Server error, check your response"); - }, - }); + const formData = new FormData($("#update-settings")[0]); + if (messengerColor) { + formData.append("messengerColor", messengerColor); + } + if (dark_mode) { + formData.append("dark_mode", dark_mode); + } + $.ajax({ + url: url + "/updateSettings", + method: "POST", + data: formData, + dataType: "JSON", + processData: false, + contentType: false, + beforeSend: () => { + // close settings modal + app_modal({ + show: false, + name: "settings", + }); + // Show waiting alert modal + app_modal({ + show: true, + name: "alert", + buttons: false, + body: loadingSVG("32px", null, "margin:auto"), + }); + }, + success: (data) => { + if (data.error) { + // Show error message in alert modal + app_modal({ + show: true, + name: "alert", + buttons: true, + body: data.msg, + }); + } else { + // Hide alert modal + app_modal({ + show: false, + name: "alert", + buttons: true, + body: "", + }); + + // reload the page + location.reload(true); + } + }, + error: () => { + console.error("Server error, check your response"); + }, + }); } /** - *------------------------------------------------------------- - * Set Active status - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Set Active status +*------------------------------------------------------------- +*/ function setActiveStatus(status) { - $.ajax({ - url: url + "/setActiveStatus", - method: "POST", - data: { _token: csrfToken, status: status }, - dataType: "JSON", - success: (data) => { - // Nothing to do - }, - error: () => { - console.error("Server error, check your response"); - }, - }); + $.ajax({ + url: url + "/setActiveStatus", + method: "POST", + data: { _token: csrfToken, status: status }, + dataType: "JSON", + success: (data) => { + // Nothing to do + }, + error: () => { + console.error("Server error, check your response"); + }, + }); } /** - *------------------------------------------------------------- - * On DOM ready - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* On DOM ready +*------------------------------------------------------------- +*/ $(document).ready(function () { - // get contacts list - getContacts(); - - // get contacts list - getFavoritesList(); - - // Clear typing timeout - clearTimeout(typingTimeout); - - // NProgress configurations - NProgress.configure({ showSpinner: false, minimum: 0.7, speed: 500 }); - - // make message input autosize. - autosize($(".m-send")); - - // check if pusher has access to the channel [Internet status] - pusher.connection.bind("state_change", function (states) { - let selector = $(".internet-connection"); - checkInternet(states.current, selector); - // listening for pusher:subscription_succeeded - channel.bind("pusher:subscription_succeeded", function () { - // On connection state change [Updating] and get [info & msgs] - if (getMessengerId() != 0) { - if ( - $(".messenger-list-item") - .find("tr[data-action]") - .attr("data-action") == "1" - ) { - $(".messenger-listView").hide(); - } - IDinfo(getMessengerId()); - } - }); - }); - - // tabs on click, show/hide... - $(".messenger-listView-tabs a").on("click", function () { - var dataView = $(this).attr("data-view"); - $(".messenger-listView-tabs a").removeClass("active-tab"); - $(this).addClass("active-tab"); - $(".messenger-tab").hide(); - $(".messenger-tab[data-view=" + dataView + "]").show(); - }); - - // set item active on click - $("body").on("click", ".messenger-list-item", function () { - $(".messenger-list-item").removeClass("m-list-active"); - $(this).addClass("m-list-active"); - const userID = $(this).attr("data-contact"); - routerPush(document.title, `${url}/${userID}`); - updateSelectedContact(userID); - }); - - // show info side button - $(".messenger-infoView nav a , .show-infoSide").on("click", function () { - $(".messenger-infoView").toggle(); - }); - - // make favorites card dragable on click to slide. - hScroller(".messenger-favorites"); - - // click action for list item [user/group] - $("body").on("click", ".messenger-list-item", function () { - if ($(this).find("tr[data-action]").attr("data-action") == "1") { - $(".messenger-listView").hide(); - } - const dataId = $(this).find("p[data-id]").attr("data-id"); - setMessengerId(dataId); - // IDinfo(dataId); - }); - - // click action for favorite button - $("body").on("click", ".favorite-list-item", function () { - if ($(this).find("div").attr("data-action") == "1") { - $(".messenger-listView").hide(); - } - const uid = $(this).find("div.avatar").attr("data-id"); - setMessengerId(uid); - IDinfo(uid); - updateSelectedContact(uid); - routerPush(document.title, `${url}/${uid}`); - }); - - // list view buttons - $(".listView-x").on("click", function () { - $(".messenger-listView").hide(); - }); - $(".show-listView").on("click", function () { - routerPush(document.title, `${url}/`); - $(".messenger-listView").show(); - }); - - // click action for [add to favorite] button. - $(".add-to-favorite").on("click", function () { - star(getMessengerId()); - }); - - // calling Css Media Queries - cssMediaQueries(); - - // message form on submit. - $("#message-form").on("submit", (e) => { - e.preventDefault(); - sendMessage(); - }); - - // message input on keyup [Enter to send, Enter+Shift for new line] - $("#message-form .m-send").on("keyup", (e) => { - // if enter key pressed. - if (e.which == 13 || e.keyCode == 13) { - // if shift + enter key pressed, do nothing (new line). - // if only enter key pressed, send message. - if (!e.shiftKey) { - triggered = isTyping(false); - sendMessage(); - } - } - }); - - // On [upload attachment] input change, show a preview of the image/file. - $("body").on("change", ".upload-attachment", (e) => { - let file = e.target.files[0]; - if (!attachmentValidate(file)) return false; - let reader = new FileReader(); - let sendCard = $(".messenger-sendCard"); - reader.readAsDataURL(file); - reader.addEventListener("loadstart", (e) => { - $("#message-form").before(loadingSVG()); - }); - reader.addEventListener("load", (e) => { - $(".messenger-sendCard").find(".loadingSVG").remove(); - if (!file.type.match("image.*")) { - // if the file not image - sendCard.find(".attachment-preview").remove(); // older one - sendCard.prepend(attachmentTemplate("file", file.name)); - } else { - // if the file is an image - sendCard.find(".attachment-preview").remove(); // older one - sendCard.prepend( - attachmentTemplate("image", file.name, e.target.result) - ); - } - }); - }); - - function attachmentValidate(file) { - const fileElement = $(".upload-attachment"); - const { name: fileName, size: fileSize } = file; - const fileExtension = fileName.split(".").pop(); - if ( - !chatify.allAllowedExtensions.includes( - fileExtension.toString().toLowerCase() - ) - ) { - alert("file type not allowed"); - fileElement.val(""); - return false; - } - // Validate file size. - if (fileSize > chatify.maxUploadSize) { - alert("File is too large!"); - return false; - } - return true; - } - - // Attachment preview cancel button. - $("body").on("click", ".attachment-preview .cancel", () => { - cancelAttachment(); - }); - - // typing indicator on [input] keyDown - $("#message-form .m-send").on("keydown", () => { - if (typingNow < 1) { - isTyping(true); - typingNow = 1; - } - clearTimeout(typingTimeout); - typingTimeout = setTimeout(function () { - isTyping(false); - typingNow = 0; - }, 1000); - }); - - // Image modal - $("body").on("click", ".chat-image", function () { - let src = $(this).css("background-image").split(/"/)[1]; - $("#imageModalBox").show(); - $("#imageModalBoxSrc").attr("src", src); - }); - $(".imageModal-close").on("click", function () { - $("#imageModalBox").hide(); - }); - - // Search input on focus - $(".messenger-search").on("focus", function () { - $(".messenger-tab").hide(); - $('.messenger-tab[data-view="search"]').show(); - }); - $(".messenger-search").on("blur", function () { - setTimeout(function () { - $(".messenger-tab").hide(); - $('.messenger-tab[data-view="users"]').show(); - }, 200); - }); - // Search action on keyup - const debouncedSearch = debounce(function () { - const value = $(".messenger-search").val(); - messengerSearch(value); - }, 500); - $(".messenger-search").on("keyup", function (e) { - const value = $(this).val(); - if ($.trim(value).length > 0) { - $(".messenger-search").trigger("focus"); - debouncedSearch(); - } else { - $(".messenger-tab").hide(); - $('.messenger-listView-tabs a[data-view="users"]').trigger("click"); - } - }); - - // Delete Conversation button - $(".messenger-infoView-btns .delete-conversation").on("click", function () { - app_modal({ - name: "delete", - }); - }); - // Delete Message Button - $("body").on("click", ".message-card .actions .delete-btn", function () { - app_modal({ - name: "delete", - data: $(this).data("id"), - }); - }); - // Delete modal [on delete button click] - $(".app-modal[data-name=delete]") - .find(".app-modal-footer .delete") - .on("click", function () { - const id = $("body") - .find(".app-modal[data-name=delete]") - .find(".app-modal-card") - .attr("data-modal"); - if (id == 0) { - deleteConversation(getMessengerId()); - } else { - deleteMessage(id); - } - app_modal({ - show: false, - name: "delete", - }); - }); - // delete modal [cancel button] - $(".app-modal[data-name=delete]") - .find(".app-modal-footer .cancel") - .on("click", function () { - app_modal({ - show: false, - name: "delete", - }); - }); - - // Settings button action to show settings modal - $("body").on("click", ".settings-btn", function (e) { - e.preventDefault(); - app_modal({ - show: true, - name: "settings", - }); - }); - - // on submit settings' form - $("#update-settings").on("submit", (e) => { - e.preventDefault(); - updateSettings(); - }); - // Settings modal [cancel button] - $(".app-modal[data-name=settings]") - .find(".app-modal-footer .cancel") - .on("click", function () { - app_modal({ - show: false, - name: "settings", - }); - cancelUpdatingAvatar(); - }); - // upload avatar on change - $("body").on("change", ".upload-avatar", (e) => { - // store the original avatar - if (defaultAvatarInSettings == null) { - defaultAvatarInSettings = $(".upload-avatar-preview").css( - "background-image" - ); - } - let file = e.target.files[0]; - if (!attachmentValidate(file)) return false; - let reader = new FileReader(); - reader.readAsDataURL(file); - reader.addEventListener("loadstart", (e) => { - $(".upload-avatar-preview").append( - loadingSVG("42px", "upload-avatar-loading") - ); - }); - reader.addEventListener("load", (e) => { - $(".upload-avatar-preview").find(".loadingSVG").remove(); - if (!file.type.match("image.*")) { - // if the file is not an image - console.error("File you selected is not an image!"); - } else { - // if the file is an image - $(".upload-avatar-preview").css( - "background-image", - 'url("' + e.target.result + '")' - ); - } - }); - }); - // change messenger color button - $("body").on("click", ".update-messengerColor .color-btn", function () { - messengerColor = $(this).attr("data-color"); - $(".update-messengerColor .color-btn").removeClass("m-color-active"); - $(this).addClass("m-color-active"); - }); - // Switch to Dark/Light mode - $("body").on("click", ".dark-mode-switch", function () { - if ($(this).attr("data-mode") == "0") { - $(this).attr("data-mode", "1"); - $(this).removeClass("far"); - $(this).addClass("fas"); - dark_mode = "dark"; - } else { - $(this).attr("data-mode", "0"); - $(this).removeClass("fas"); - $(this).addClass("far"); - dark_mode = "light"; - } - }); - - //Messages pagination - actionOnScroll( - ".m-body.messages-container", - function () { - fetchMessages(getMessengerId()); - }, - true - ); - //Contacts pagination - actionOnScroll(".messenger-tab.users-tab", function () { - getContacts(); - }); - //Search pagination - actionOnScroll(".messenger-tab.search-tab", function () { - messengerSearch($(".messenger-search").val()); - }); + // get contacts list + getContacts(); + + // get contacts list + getFavoritesList(); + + // Clear typing timeout + clearTimeout(typingTimeout); + + // NProgress configurations + NProgress.configure({ showSpinner: false, minimum: 0.7, speed: 500 }); + + // make message input autosize. + autosize($(".m-send")); + + // check if pusher has access to the channel [Internet status] + pusher.connection.bind("state_change", function (states) { + let selector = $(".internet-connection"); + checkInternet(states.current, selector); + // listening for pusher:subscription_succeeded + channel.bind("pusher:subscription_succeeded", function () { + // On connection state change [Updating] and get [info & msgs] + if (getMessengerId() != 0) { + if ( + $(".messenger-list-item") + .find("tr[data-action]") + .attr("data-action") == "1" + ) { + $(".messenger-listView").hide(); + } + IDinfo(getMessengerId()); + } + }); + }); + + // tabs on click, show/hide... + $(".messenger-listView-tabs a").on("click", function () { + var dataView = $(this).attr("data-view"); + $(".messenger-listView-tabs a").removeClass("active-tab"); + $(this).addClass("active-tab"); + $(".messenger-tab").hide(); + $(".messenger-tab[data-view=" + dataView + "]").show(); + }); + + // set item active on click + $("body").on("click", ".messenger-list-item", function () { + $(".messenger-list-item").removeClass("m-list-active"); + $(this).addClass("m-list-active"); + const userID = $(this).attr("data-contact"); + routerPush(document.title, `${url}/${userID}`); + updateSelectedContact(userID); + }); + + // show info side button + $(".messenger-infoView nav a , .show-infoSide").on("click", function () { + $(".messenger-infoView").toggle(); + }); + + // make favorites card dragable on click to slide. + hScroller(".messenger-favorites"); + + // click action for list item [user/group] + $("body").on("click", ".messenger-list-item", function () { + if ($(this).find("tr[data-action]").attr("data-action") == "1") { + $(".messenger-listView").hide(); + } + const dataId = $(this).find("p[data-id]").attr("data-id"); + setMessengerId(dataId); + // IDinfo(dataId); + }); + + // click action for favorite button + $("body").on("click", ".favorite-list-item", function () { + if ($(this).find("div").attr("data-action") == "1") { + $(".messenger-listView").hide(); + } + const uid = $(this).find("div.avatar").attr("data-id"); + setMessengerId(uid); + IDinfo(uid); + updateSelectedContact(uid); + routerPush(document.title, `${url}/${uid}`); + }); + + // list view buttons + $(".listView-x").on("click", function () { + $(".messenger-listView").hide(); + }); + $(".show-listView").on("click", function () { + routerPush(document.title, `${url}/`); + $(".messenger-listView").show(); + }); + + // click action for [add to favorite] button. + $(".add-to-favorite").on("click", function () { + star(getMessengerId()); + }); + + // calling Css Media Queries + cssMediaQueries(); + + // message form on submit. + $("#message-form").on("submit", (e) => { + e.preventDefault(); + sendMessage(); + }); + + // message input on keyup [Enter to send, Enter+Shift for new line] + $("#message-form .m-send").on("keyup", (e) => { + // if enter key pressed. + if (e.which == 13 || e.keyCode == 13) { + // if shift + enter key pressed, do nothing (new line). + // if only enter key pressed, send message. + if (!e.shiftKey) { + triggered = isTyping(false); + sendMessage(); + } + } + }); + + // On [upload attachment] input change, show a preview of the image/file. + $("body").on("change", ".upload-attachment", (e) => { + let file = e.target.files[0]; + if (!attachmentValidate(file)) return false; + let reader = new FileReader(); + let sendCard = $(".messenger-sendCard"); + reader.readAsDataURL(file); + reader.addEventListener("loadstart", (e) => { + $("#message-form").before(loadingSVG()); + }); + reader.addEventListener("load", (e) => { + $(".messenger-sendCard").find(".loadingSVG").remove(); + if (!file.type.match("image.*")) { + // if the file not image + sendCard.find(".attachment-preview").remove(); // older one + sendCard.prepend(attachmentTemplate("file", file.name)); + } else { + // if the file is an image + sendCard.find(".attachment-preview").remove(); // older one + sendCard.prepend( + attachmentTemplate("image", file.name, e.target.result) + ); + } + }); + }); + + function attachmentValidate(file) { + const fileElement = $(".upload-attachment"); + const { name: fileName, size: fileSize } = file; + const fileExtension = fileName.split(".").pop(); + if ( + !chatify.allAllowedExtensions.includes( + fileExtension.toString().toLowerCase() + ) + ) { + alert("file type not allowed"); + fileElement.val(""); + return false; + } + // Validate file size. + if (fileSize > chatify.maxUploadSize) { + alert("File is too large!"); + return false; + } + return true; + } + + // Attachment preview cancel button. + $("body").on("click", ".attachment-preview .cancel", () => { + cancelAttachment(); + }); + + // typing indicator on [input] keyDown + $("#message-form .m-send").on("keydown", () => { + if (typingNow < 1) { + isTyping(true); + typingNow = 1; + } + clearTimeout(typingTimeout); + typingTimeout = setTimeout(function () { + isTyping(false); + typingNow = 0; + }, 1000); + }); + + // Image modal + $("body").on("click", ".chat-image", function () { + let src = $(this).css("background-image").split(/"/)[1]; + $("#imageModalBox").show(); + $("#imageModalBoxSrc").attr("src", src); + }); + $(".imageModal-close").on("click", function () { + $("#imageModalBox").hide(); + }); + + // Search input on focus + $(".messenger-search").on("focus", function () { + $(".messenger-tab").hide(); + $('.messenger-tab[data-view="search"]').show(); + }); + $(".messenger-search").on("blur", function () { + setTimeout(function () { + $(".messenger-tab").hide(); + $('.messenger-tab[data-view="users"]').show(); + }, 200); + }); + // Search action on keyup + const debouncedSearch = debounce(function () { + const value = $(".messenger-search").val(); + messengerSearch(value); + }, 500); + $(".messenger-search").on("keyup", function (e) { + const value = $(this).val(); + if ($.trim(value).length > 0) { + $(".messenger-search").trigger("focus"); + debouncedSearch(); + } else { + $(".messenger-tab").hide(); + $('.messenger-listView-tabs a[data-view="users"]').trigger("click"); + } + }); + + // Delete Conversation button + $(".messenger-infoView-btns .delete-conversation").on("click", function () { + app_modal({ + name: "delete", + }); + }); + // Delete Message Button + $("body").on("click", ".message-card .actions .delete-btn", function () { + app_modal({ + name: "delete", + data: $(this).data("id"), + }); + }); + // Delete modal [on delete button click] + $(".app-modal[data-name=delete]") + .find(".app-modal-footer .delete") + .on("click", function () { + const id = $("body") + .find(".app-modal[data-name=delete]") + .find(".app-modal-card") + .attr("data-modal"); + if (id == 0) { + deleteConversation(getMessengerId()); + } else { + deleteMessage(id); + } + app_modal({ + show: false, + name: "delete", + }); + }); + // delete modal [cancel button] + $(".app-modal[data-name=delete]") + .find(".app-modal-footer .cancel") + .on("click", function () { + app_modal({ + show: false, + name: "delete", + }); + }); + + // Settings button action to show settings modal + $("body").on("click", ".settings-btn", function (e) { + e.preventDefault(); + app_modal({ + show: true, + name: "settings", + }); + }); + + // on submit settings' form + $("#update-settings").on("submit", (e) => { + e.preventDefault(); + updateSettings(); + }); + // Settings modal [cancel button] + $(".app-modal[data-name=settings]") + .find(".app-modal-footer .cancel") + .on("click", function () { + app_modal({ + show: false, + name: "settings", + }); + cancelUpdatingAvatar(); + }); + // upload avatar on change + $("body").on("change", ".upload-avatar", (e) => { + // store the original avatar + if (defaultAvatarInSettings == null) { + defaultAvatarInSettings = $(".upload-avatar-preview").css( + "background-image" + ); + } + let file = e.target.files[0]; + if (!attachmentValidate(file)) return false; + let reader = new FileReader(); + reader.readAsDataURL(file); + reader.addEventListener("loadstart", (e) => { + $(".upload-avatar-preview").append( + loadingSVG("42px", "upload-avatar-loading") + ); + }); + reader.addEventListener("load", (e) => { + $(".upload-avatar-preview").find(".loadingSVG").remove(); + if (!file.type.match("image.*")) { + // if the file is not an image + console.error("File you selected is not an image!"); + } else { + // if the file is an image + $(".upload-avatar-preview").css( + "background-image", + 'url("' + e.target.result + '")' + ); + } + }); + }); + // change messenger color button + $("body").on("click", ".update-messengerColor .color-btn", function () { + messengerColor = $(this).attr("data-color"); + $(".update-messengerColor .color-btn").removeClass("m-color-active"); + $(this).addClass("m-color-active"); + }); + // Switch to Dark/Light mode + $("body").on("click", ".dark-mode-switch", function () { + if ($(this).attr("data-mode") == "0") { + $(this).attr("data-mode", "1"); + $(this).removeClass("far"); + $(this).addClass("fas"); + dark_mode = "dark"; + } else { + $(this).attr("data-mode", "0"); + $(this).removeClass("fas"); + $(this).addClass("far"); + dark_mode = "light"; + } + }); + + //Messages pagination + actionOnScroll( + ".m-body.messages-container", + function () { + fetchMessages(getMessengerId()); + }, + true + ); + //Contacts pagination + actionOnScroll(".messenger-tab.users-tab", function () { + getContacts(); + }); + //Search pagination + actionOnScroll(".messenger-tab.search-tab", function () { + messengerSearch($(".messenger-search").val()); + }); }); /** - *------------------------------------------------------------- - * Observer on DOM changes - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Observer on DOM changes +*------------------------------------------------------------- +*/ let previousMessengerId = getMessengerId(); const observer = new MutationObserver(function (mutations) { - if (getMessengerId() !== previousMessengerId) { - previousMessengerId = getMessengerId(); - initClientChannel(); - } + if (getMessengerId() !== previousMessengerId) { + previousMessengerId = getMessengerId(); + initClientChannel(); + } }); const config = { subtree: true, childList: true }; @@ -1633,84 +1667,86 @@ observer.observe(document, config); // observer.disconnect(); /** - *------------------------------------------------------------- - * Resize messaging area when resize the viewport. - * on mobile devices when the keyboard is shown, the viewport - * height is changed, so we need to resize the messaging area - * to fit the new height. - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Resize messaging area when resize the viewport. +* on mobile devices when the keyboard is shown, the viewport +* height is changed, so we need to resize the messaging area +* to fit the new height. +*------------------------------------------------------------- +*/ var resizeTimeout; window.visualViewport.addEventListener("resize", (e) => { - clearTimeout(resizeTimeout); - resizeTimeout = setTimeout(function () { - const h = e.target.height; - if (h) { - $(".messenger-messagingView").css({ height: h + "px" }); - } - }, 100); + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(function () { + const h = e.target.height; + if (h) { + $(".messenger-messagingView").css({ height: h + "px" }); + } + }, 100); }); /** - *------------------------------------------------------------- - * Emoji Picker - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Emoji Picker +*------------------------------------------------------------- +*/ const emojiButton = document.querySelector(".emoji-button"); const emojiPicker = new EmojiButton({ - theme: messengerTheme, - autoHide: false, - position: "top-start", + theme: messengerTheme, + autoHide: false, + position: "top-start", }); emojiButton.addEventListener("click", (e) => { - e.preventDefault(); - emojiPicker.togglePicker(emojiButton); + e.preventDefault(); + emojiPicker.togglePicker(emojiButton); }); emojiPicker.on("emoji", (emoji) => { - const el = messageInput[0]; - const startPos = el.selectionStart; - const endPos = el.selectionEnd; - const value = messageInput.val(); - const newValue = - value.substring(0, startPos) + - emoji + - value.substring(endPos, value.length); - messageInput.val(newValue); - el.selectionStart = el.selectionEnd = startPos + emoji.length; - el.focus(); + const el = messageInput[0]; + const startPos = el.selectionStart; + const endPos = el.selectionEnd; + const value = messageInput.val(); + const newValue = + value.substring(0, startPos) + + emoji + + value.substring(endPos, value.length); + messageInput.val(newValue); + el.selectionStart = el.selectionEnd = startPos + emoji.length; + el.focus(); }); /** - *------------------------------------------------------------- - * Notification sounds - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Notification sounds +*------------------------------------------------------------- +*/ function playNotificationSound(soundName, condition = false) { - if ((document.hidden || condition) && chatify.sounds.enabled) { - const sound = new Audio( - `/${chatify.sounds.public_path}/${chatify.sounds[soundName]}` - ); - sound.play(); - } + if ((document.hidden || condition) && chatify.sounds.enabled) { + const sound = new Audio( + `/${chatify.sounds.public_path}/${chatify.sounds[soundName]}` + ); + sound.play(); + } } /** - *------------------------------------------------------------- - * Update and format dates to time ago. - *------------------------------------------------------------- - */ +*------------------------------------------------------------- +* Update and format dates to time ago. +*------------------------------------------------------------- +*/ function updateElementsDateToTimeAgo() { - $(".message-time").each(function () { - const time = $(this).attr("data-time"); - $(this).find(".time").text(dateStringToTimeAgo(time)); - }); - $(".contact-item-time").each(function () { - const time = $(this).attr("data-time"); - $(this).text(dateStringToTimeAgo(time)); - }); + $(".message-time").each(function () { + const time = $(this).attr("data-time"); + $(this).find(".time").text(dateStringToTimeAgo(time)); + }); + $(".contact-item-time").each(function () { + const time = $(this).attr("data-time"); + $(this).text(dateStringToTimeAgo(time)); + }); } setInterval(() => { - updateElementsDateToTimeAgo(); + updateElementsDateToTimeAgo(); }, 60000); + + diff --git a/src/assets/js/voiceMessage.js b/src/assets/js/voiceMessage.js new file mode 100644 index 00000000..3ccdaa2e --- /dev/null +++ b/src/assets/js/voiceMessage.js @@ -0,0 +1,372 @@ +/* +**************************************************************************** +* Recording Voice Message +**************************************************************************** +*/ +// Global variables +let microphoneButton, form, recordingUI, trashButton, recordingProgressBar, recordingTime, stopButton, sendRecordButton; +let isRecording = false; +let isPaused = false; +let mediaRecorder; +let audioChunks = []; +let stream; +let recordingInterval; +let recordingSeconds = 0; +const MAX_RECORDING_TIME = 60; // 1 minute + +function initializeDOMElements() { + microphoneButton = document.getElementById('microphone-button'); + form = document.getElementById('message-form'); + recordingUI = document.getElementById('recording-ui'); + trashButton = document.getElementById('trash-button'); + recordingProgressBar = document.getElementById('recording-progress-bar'); + recordingTime = document.getElementById('recording-time'); + stopButton = document.getElementById('stop-button'); + sendRecordButton = document.getElementById('send-record-button'); + const elements = [microphoneButton, form, recordingUI, trashButton, recordingProgressBar, recordingTime, stopButton, sendRecordButton]; + const missingElements = elements.filter(el => !el); + + if (missingElements.length > 0) { + console.error('Some DOM elements are missing:', missingElements); + return false; + } + + return true; +} +/** + *------------------------------------------------------------- + * Recording Event + *------------------------------------------------------------- + */ + +function setupEventListeners() { + microphoneButton.addEventListener('click', toggleRecording); + stopButton.addEventListener('click', togglePauseResume); + sendRecordButton.addEventListener('click', sendRecording); + trashButton.addEventListener('click', cancelRecording); +} + +/** + *------------------------------------------------------------- + * Toggle Record Button + *------------------------------------------------------------- + */ + +function toggleRecording() { + if (isRecording) { + stopRecording(); + } else { + requestMicrophonePermission(); + } +} +/** + *------------------------------------------------------------- + * Request Microphone Permission + *------------------------------------------------------------- + */ + + +function requestMicrophonePermission() { + navigator.mediaDevices.getUserMedia({ audio: true }) + .then(audioStream => { + stream = audioStream; + startRecording(); + }) + .catch(error => { + console.error('Error accessing microphone:', error); + alert('Microphone permission is required to record audio.'); + }); +} + +/** + *------------------------------------------------------------- + * Start Recording + *------------------------------------------------------------- + */ + + +function startRecording() { + if (!initializeDOMElements()) { + console.error('Cannot start recording due to missing DOM elements'); + return; + } + + form.style.display ='none'; + recordingUI.style.display = 'flex'; + + if (!isRecording) { + recordingSeconds = 0; + audioChunks = []; + } + updateRecordingTime(); + updateProgressBar(); + + recordingInterval = setInterval(() => { + if (!isPaused) { + recordingSeconds++; + updateRecordingTime(); + updateProgressBar(); + if (recordingSeconds >= MAX_RECORDING_TIME) { + stopRecording(); + } + } + }, 1000); + + microphoneButton.classList.add('recording'); + stopButton.innerHTML = ''; + + const options = { mimeType: 'audio/mp4' }; + mediaRecorder = new MediaRecorder(stream, options); + + mediaRecorder.start(); + + mediaRecorder.addEventListener("dataavailable", event => { + audioChunks.push(event.data); + + }); + + isRecording = true; + isPaused = false; +} + +/** + *------------------------------------------------------------- + * Stop Recording + *------------------------------------------------------------- + */ + + +function stopRecording() { + if (!isRecording) return; + + clearInterval(recordingInterval); + + if (mediaRecorder && mediaRecorder.state !== 'inactive') { + mediaRecorder.stop(); + } + + isRecording = false; + isPaused = false; + microphoneButton.classList.remove('recording'); + stopButton.innerHTML = ''; + if (stream) { + stream.getTracks().forEach(track => track.stop()); + } +} + +/** + *------------------------------------------------------------- + * Toggle between pause and resume recording + *------------------------------------------------------------- + */ + + +function togglePauseResume() { + if (!isRecording) return; + + if (isPaused) { + // Resume recording + mediaRecorder.resume(); + stopButton.innerHTML = ''; + isPaused = false; + } else { + // Pause recording + mediaRecorder.pause(); + stopButton.innerHTML = ''; + isPaused = true; + } +} +/** + *------------------------------------------------------------- + * Send Recording + *------------------------------------------------------------- + */ + + +function sendRecording() { + stopRecording(); + setTimeout(() => { + + if (audioChunks.length > 0) { + const audioBlob = new Blob(audioChunks, { type: 'audio/mp4' }); + + if (audioBlob.size > 0) { + const duration = formatTime(recordingSeconds); + sendMessage(true, audioBlob, duration); + } else { + console.warn('Blob size is zero.'); + } + } else { + console.warn('No audio data to send.'); + } + + resetRecordingState(); + }, 500); // delay +} + +/** + *------------------------------------------------------------- + * Canceling Recording + *------------------------------------------------------------- + */ + +function cancelRecording() { + stopRecording(); + resetRecordingState(); +} + + +/** + *------------------------------------------------------------- + * Reset Recording State + *------------------------------------------------------------- + */ + +function resetRecordingState() { + audioChunks = []; + recordingSeconds = 0; + updateRecordingTime(); + updateProgressBar(); + form.style.display ='flex'; + recordingUI.style.display = 'none'; + isRecording = false; + isPaused = false; + stopButton.innerHTML = ''; +} + +/** + *------------------------------------------------------------- + * update Recording Time + *------------------------------------------------------------- + */ + +function updateRecordingTime() { + const minutes = Math.floor(recordingSeconds / 60).toString().padStart(2, '0'); + const seconds = (recordingSeconds % 60).toString().padStart(2, '0'); + recordingTime.textContent = `${minutes}:${seconds}`; +} + +/** + *------------------------------------------------------------- + * update Progress Bar + *------------------------------------------------------------- + */ + +function updateProgressBar() { + const progress = (recordingSeconds / MAX_RECORDING_TIME) * 100; + recordingProgressBar.style.width = `${progress}%`; +} + +/** + *------------------------------------------------------------- + * Format Time + *------------------------------------------------------------- + */ + + +function formatTime(time) { + const minutes = Math.floor(time / 60); + const seconds = Math.floor(time % 60); + return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; +} + + +document.addEventListener('DOMContentLoaded', function() { + if (initializeDOMElements()) { + setupEventListeners(); + } else { + console.error('Failed to initialize audio recording feature due to missing DOM elements'); + } +}); +/* +*------------------------------------------------------------- +* Function to initialize the audio player after a message is injected +*------------------------------------------------------------- +*/ +function initializeAudioPlayer(selector, playerId) { + if ($(`#waveform-${playerId}`).hasClass('initialized')) { + return; + } + + const container = $(selector); + if (container.length === 0) { + console.error(`Container element not found: ${selector}`); + return; + } + + let audioElement = document.querySelector(`#waveform-${playerId}`); + const playButton = document.querySelector(`button[data-player-id="${playerId}"]`); + initilaizeWavesurfer(audioElement,audioElement.getAttribute('data-audio-url'),playButton,`${playerId}`) + // Mark the player as initialized + $(`#waveform-${playerId}`).addClass('initialized'); +} +/** + *------------------------------------------------------------- + * initilaize wavesurfer + *------------------------------------------------------------- + */ + function initilaizeWavesurfer(container,URL,playButton,playerId){ + let wavesurfer = WaveSurfer.create({ + container: container, + waveColor: 'rgb(218, 210, 210)', + progressColor: 'grey', + cursorColor: 'transparent', // No cursor + barWidth: 2.5, + barRadius: 3, + height: 80, + responsive: true, + barGap:3, + hideScrollbar: true + }); + + wavesurfer.load(URL); + // Closure to ensure each playButton is associated with the correct wavesurfer instance + playButton.addEventListener('click', function () { + + wavesurfer.playPause(); // Correctly refers to the associated wavesurfer instance + const svg = playButton.querySelector('svg'); + + if (svg) { + // Check the current icon and toggle between play and pause + if (svg.classList.contains('fa-play')) { + svg.classList.remove('fa-play'); + svg.classList.add('fa-pause'); + svg.setAttribute('data-icon', 'pause'); + } else { + svg.classList.remove('fa-pause'); + svg.classList.add('fa-play'); + svg.setAttribute('data-icon', 'play'); + } + } + }); + + wavesurfer.on('ready', function () { + var durationElement = document.querySelector("#player-" + playerId + " .duration"); + if (durationElement) { + durationElement.textContent = formatTime(wavesurfer.getDuration()); + } + }); + +} + +/** + *------------------------------------------------------------- + * Reder Wavesurfers for Fetching Voice Messages + *------------------------------------------------------------- + */ + + + +function rederWavesurfers(){ +var waveformElements = document.getElementsByClassName("waveform"); + +for (let i = 0; i < waveformElements.length; i++) { +let waveformElement = waveformElements[i]; // Use let here for block scoping +let playerId = waveformElement.getAttribute('data-audio-id'); +let playButton = document.querySelector('.btn-toggle-play[data-player-id="' + playerId + '"]'); +if (typeof WaveSurfer !== 'undefined' && waveformElement) { +initilaizeWavesurfer(waveformElement,waveformElement.getAttribute('data-audio-url'),playButton,playerId); +} +} +} + diff --git a/src/config/chatify.php b/src/config/chatify.php index 359c93b7..e28c5293 100644 --- a/src/config/chatify.php +++ b/src/config/chatify.php @@ -87,6 +87,7 @@ 'download_route_name' => 'attachments.download', 'allowed_images' => (array) ['png','jpg','jpeg','gif'], 'allowed_files' => (array) ['zip','rar','txt'], + 'allowed_voice_messages' => (array) ['mpeg','mpga','mp3','wav','webm','mp4'], 'max_upload_size' => env('CHATIFY_MAX_FILE_SIZE', 150), // MB ], diff --git a/src/views/layouts/footerLinks.blade.php b/src/views/layouts/footerLinks.blade.php index 45540236..5b62409a 100644 --- a/src/views/layouts/footerLinks.blade.php +++ b/src/views/layouts/footerLinks.blade.php @@ -7,11 +7,13 @@ sounds: {!! json_encode(config('chatify.sounds')) !!}, allowedImages: {!! json_encode(config('chatify.attachments.allowed_images')) !!}, allowedFiles: {!! json_encode(config('chatify.attachments.allowed_files')) !!}, + allowedVoiceMessages: {!! json_encode(config('chatify.attachments.allowed_voice_messages')) !!}, maxUploadSize: {{ Chatify::getMaxUploadSize() }}, pusher: {!! json_encode(config('chatify.pusher')) !!}, pusherAuthEndpoint: '{{route("pusher.auth")}}' }; - window.chatify.allAllowedExtensions = chatify.allowedImages.concat(chatify.allowedFiles); + window.chatify.allAllowedExtensions = chatify.allowedImages.concat(chatify.allowedFiles).concat(chatify.allowedVoiceMessages); + diff --git a/src/views/layouts/headLinks.blade.php b/src/views/layouts/headLinks.blade.php index 630c934f..9a1cc431 100644 --- a/src/views/layouts/headLinks.blade.php +++ b/src/views/layouts/headLinks.blade.php @@ -15,6 +15,7 @@ + {{-- styles --}} diff --git a/src/views/layouts/messageCard.blade.php b/src/views/layouts/messageCard.blade.php index 6c1719de..b7b57cb4 100644 --- a/src/views/layouts/messageCard.blade.php +++ b/src/views/layouts/messageCard.blade.php @@ -14,7 +14,7 @@ @endif {{-- Card --}}
- @if (@$attachment->type != 'image' || $message) + @if (@$attachment->type == 'file' || $message)
{!! ($message == null && $attachment != null && @$attachment->type != 'file') ? $attachment->title : nl2br($message) !!} {!! $timeAndSeen !!} @@ -27,13 +27,32 @@ @endif @if(@$attachment->type == 'image')
-
+
{{ $attachment->title }}
{!! $timeAndSeen !!}
- @endif + @endif + @if ($attachment->type == 'audio') +
+ messenger_color ?? 'blue'; ?> +
+ + +
+ 0:00 +
+
+ {!! $timeAndSeen !!} +
+ +@endif
+
+ diff --git a/src/views/layouts/sendForm.blade.php b/src/views/layouts/sendForm.blade.php index c435734f..612f8b2d 100644 --- a/src/views/layouts/sendForm.blade.php +++ b/src/views/layouts/sendForm.blade.php @@ -1,9 +1,26 @@
-
+ @csrf - + +
+ + + +
\ No newline at end of file From e2ff89a06576e299e2f04c29569d7cf476d87adb Mon Sep 17 00:00:00 2001 From: sarahelsagheir <53413980+sarahelsagheir@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:43:01 +0300 Subject: [PATCH 2/2] update wave color & support firefox on ubuntu --- src/assets/css/style.css | 5 +- src/assets/js/code.js | 197 ++++++++++++------------ src/assets/js/voiceMessage.js | 50 +++--- src/views/layouts/messageCard.blade.php | 4 +- 4 files changed, 138 insertions(+), 118 deletions(-) diff --git a/src/assets/css/style.css b/src/assets/css/style.css index 133e3112..cd70151a 100644 --- a/src/assets/css/style.css +++ b/src/assets/css/style.css @@ -1253,7 +1253,7 @@ div.loadingPlaceholder-date { .waveform { width: 80%; overflow: hidden; - +margin-left: 12px; } @@ -1285,6 +1285,7 @@ div.loadingPlaceholder-date { display: flex; align-items: center; width: 100%; + height: 100%; position: relative; z-index: 2; } @@ -1313,7 +1314,7 @@ div.loadingPlaceholder-date { .fa-trash , .fa-paper-plane { color: var(--primary-color) !important; } -.fa-stop, .fa-play, .fa-pause{ +.fa-stop, .fa-play , .fa-pause{ color: white !important; font-size: 15px !important; } diff --git a/src/assets/js/code.js b/src/assets/js/code.js index be3d3c8b..adf4bed1 100644 --- a/src/assets/js/code.js +++ b/src/assets/js/code.js @@ -79,11 +79,10 @@ function updateSelectedContact(user_id) { ".messenger-list-item[data-contact=" + (user_id) + "]" ) .addClass("m-list-active"); - if (user_id != 0) { IDinfo(user_id); } - } +} /** *------------------------------------------------------------- * Global Templates @@ -442,10 +441,15 @@ function IDinfo(id) { } } +/** +*------------------------------------------------------------- +* Send message function +*------------------------------------------------------------- +*/ // Function to handle sending audio messages function sendAudioMessage(audioBlob, tempID, receiverId, csrfToken) { const formData = new FormData(); - const fileName = 'audio.mp4'; + const fileName = 'audio.webm'; formData.append('audio', audioBlob, fileName); formData.append("temporaryMsgId", tempID); @@ -466,96 +470,88 @@ function sendAudioMessage(audioBlob, tempID, receiverId, csrfToken) { */ function sendMessage(isVoiceMessage = false, audioBlob = null, duration = null) { - temporaryMsgId += 1; - const tempID = `temp_${temporaryMsgId}`; - const receiverId = getMessengerId(); - let formData; - - if (isVoiceMessage && audioBlob) { - formData = sendAudioMessage(audioBlob, tempID, receiverId, csrfToken); - } else { - // Existing code for handling text or file messages - let hasFile = !!$(".upload-attachment").val(); - const inputValue = $.trim(messageInput.val()); - - if (inputValue.length === 0 && !hasFile) { - return false; - } - - formData = new FormData($("#message-form")[0]); - formData.append("id", receiverId); - formData.append("temporaryMsgId", tempID); - formData.append("_token", csrfToken); - - if (hasFile) { - messagesContainer - .find(".messages") - .append( - sendTempMessageCard( - inputValue + "\n" + loadingSVG("28px"), - tempID - ) - ); - } else { - messagesContainer - .find(".messages") - .append(sendTempMessageCard(inputValue, tempID)); - } - - $("#message-form").trigger("reset"); - cancelAttachment(); - messageInput.focus(); - } - - scrollToBottom(messagesContainer); - - // Send the message via AJAX - $.ajax({ - url: $("#message-form").attr("action"), - method: "POST", - data: formData, - dataType: "JSON", - processData: false, - contentType: false, - beforeSend: () => { - $(".messages").find(".message-hint").hide(); - }, - success: (data) => { - if (data.error > 0) { - errorMessageCard(tempID); - console.error(data.error_msg); - } else { - updateContactItem(receiverId); - - const tempMsgCardElement = messagesContainer.find( - `.message-card[data-id=${data.tempID}]` - ); - - let $message = data.message.replace(/()\d{1,2}:\d{2}(<\/span>)/, `$1${duration}$2`); - tempMsgCardElement.before($message); - tempMsgCardElement.remove(); - - const actualMessageIdMatch = data.message.match(/data-id="([a-z0-9\-]+)"/); - const actualMessageId = actualMessageIdMatch ? actualMessageIdMatch[1] : null; + console.log("sendMessage function called"); + temporaryMsgId += 1; + const tempID = `temp_${temporaryMsgId}`; + const receiverId = getMessengerId(); + let formData; + + if (isVoiceMessage && audioBlob) { + formData = sendAudioMessage(audioBlob, tempID, receiverId, csrfToken); + } else { + let hasFile = !!$(".upload-attachment").val(); + const inputValue = $.trim(messageInput.val()); + + if (inputValue.length === 0 && !hasFile) { + return false; + } + + formData = new FormData($("#message-form")[0]); + formData.append("id", receiverId); + formData.append("temporaryMsgId", tempID); + formData.append("_token", csrfToken); + + // Optimistically append temporary message + let tempMessageHtml = hasFile + ? sendTempMessageCard(inputValue + "\n" + loadingSVG("28px"), tempID) + : sendTempMessageCard(inputValue, tempID); + + console.log("Appending temporary message:", tempMessageHtml); + messagesContainer.find(".messages").append(tempMessageHtml); + + // Reset form + $("#message-form").trigger("reset"); + cancelAttachment(); + } - if (actualMessageId) { - setTimeout(() => { - initializeAudioPlayer(`#player-${actualMessageId}`, actualMessageId); - }, 100); - } + scrollToBottom(messagesContainer); + + $.ajax({ + url: $("#message-form").attr("action"), + method: "POST", + data: formData, + dataType: "JSON", + processData: false, + contentType: false, + beforeSend: () => { + $(".messages").find(".message-hint").hide(); + }, + success: (data) => { + if (data.error > 0) { + errorMessageCard(tempID); + } else { + const tempMsgCardElement = messagesContainer.find(`.message-card[data-id=${tempID}]`); + + if (tempMsgCardElement.length === 0) { + console.error("Temporary message card not found:", tempID); + } else { + console.log("Replacing temporary message with:", data.message); + tempMsgCardElement.replaceWith(data.message); + + const actualMessageIdMatch = data.message.match(/data-id="([a-z0-9\-]+)"/); + const actualMessageId = actualMessageIdMatch ? actualMessageIdMatch[1] : null; + + if (actualMessageId) { + initializeAudioPlayer(`#player-${actualMessageId}`, actualMessageId); + } + + updateContactItem(receiverId); + sendContactItemUpdates(true); + } + } + + scrollToBottom(messagesContainer); + }, + error: (jqXHR, textStatus, errorThrown) => { + console.error("AJAX error:", textStatus, errorThrown); + errorMessageCard(tempID); + }, + }); + + return false; +} - scrollToBottom(messagesContainer); - sendContactItemUpdates(true); - } - }, - error: () => { - errorMessageCard(tempID); - console.error("Failed sending the message! Please, check your server response."); - }, - }); - return false; -} /** *------------------------------------------------------------- * Fetch messages from database @@ -597,19 +593,20 @@ function fetchMessages(id, newFetch = false) { setMessagesLoading(false); if (messagesPage == 1) { messagesElement.html(data.messages); - //update Voice Message & intialize wavesurfer - rederWavesurfers(); + renderWaveSurfers(); scrollToBottom(messagesContainer); } else { const lastMsg = messagesElement.find( messagesElement.find(".message-card")[0] ); + const curOffset = lastMsg.offset().top - messagesContainer.scrollTop(); messagesElement.prepend(data.messages); messagesContainer.scrollTop(lastMsg.offset().top - curOffset); } + // trigger seen event makeSeen(true); // Pagination lock & messages page @@ -674,8 +671,18 @@ initClientChannel(); channel.bind("messaging", function (data) { if (data.from_id == getMessengerId() && data.to_id == auth_id) { $(".messages").find(".message-hint").remove(); + if (data.message.includes('data-audio-url')) { + messagesContainer.find(".messages").append(data.message); - scrollToBottom(messagesContainer); + const actualMessageIdMatch = data.message.match(/data-id="([a-z0-9\-]+)"/); + const actualMessageId = actualMessageIdMatch ? actualMessageIdMatch[1] : null; + + if (actualMessageId) { + console.log("Initializing audio player for:", actualMessageId); + initializeAudioPlayer(`#player-${actualMessageId}`, actualMessageId); + } + }; +scrollToBottom(messagesContainer); makeSeen(true); // remove unseen counter for the user from the contacts list $(".messenger-list-item[data-contact=" + getMessengerId() + "]") @@ -1358,7 +1365,7 @@ $(document).ready(function () { } const dataId = $(this).find("p[data-id]").attr("data-id"); setMessengerId(dataId); - // IDinfo(dataId); + // IDinfo(dataId); }); // click action for favorite button @@ -1748,5 +1755,3 @@ function updateElementsDateToTimeAgo() { setInterval(() => { updateElementsDateToTimeAgo(); }, 60000); - - diff --git a/src/assets/js/voiceMessage.js b/src/assets/js/voiceMessage.js index 3ccdaa2e..8905938b 100644 --- a/src/assets/js/voiceMessage.js +++ b/src/assets/js/voiceMessage.js @@ -113,9 +113,9 @@ function startRecording() { }, 1000); microphoneButton.classList.add('recording'); - stopButton.innerHTML = ''; + stopButton.innerHTML = ''; - const options = { mimeType: 'audio/mp4' }; + const options = { mimeType: 'audio/webm' }; mediaRecorder = new MediaRecorder(stream, options); mediaRecorder.start(); @@ -167,7 +167,7 @@ function togglePauseResume() { if (isPaused) { // Resume recording mediaRecorder.resume(); - stopButton.innerHTML = ''; + stopButton.innerHTML = ''; isPaused = false; } else { // Pause recording @@ -188,7 +188,7 @@ function sendRecording() { setTimeout(() => { if (audioChunks.length > 0) { - const audioBlob = new Blob(audioChunks, { type: 'audio/mp4' }); + const audioBlob = new Blob(audioChunks, { type: 'audio/webm' }); if (audioBlob.size > 0) { const duration = formatTime(recordingSeconds); @@ -313,10 +313,14 @@ function initializeAudioPlayer(selector, playerId) { cursorColor: 'transparent', // No cursor barWidth: 2.5, barRadius: 3, - height: 80, + height: 28, responsive: true, barGap:3, - hideScrollbar: true + // fillParent:true, + hideScrollbar: true, + drawingContextAttributes: {desynchronized: true} , + barMinHeight:5 + }); wavesurfer.load(URL); @@ -357,16 +361,26 @@ function initializeAudioPlayer(selector, playerId) { -function rederWavesurfers(){ -var waveformElements = document.getElementsByClassName("waveform"); - -for (let i = 0; i < waveformElements.length; i++) { -let waveformElement = waveformElements[i]; // Use let here for block scoping -let playerId = waveformElement.getAttribute('data-audio-id'); -let playButton = document.querySelector('.btn-toggle-play[data-player-id="' + playerId + '"]'); -if (typeof WaveSurfer !== 'undefined' && waveformElement) { -initilaizeWavesurfer(waveformElement,waveformElement.getAttribute('data-audio-url'),playButton,playerId); -} -} -} + function renderWaveSurfers() { + // Cache waveform elements to avoid querying the DOM repeatedly + const waveformElements = document.querySelectorAll(".waveform"); + + for (let i = 0; i < waveformElements.length; i++) { + const waveformElement = waveformElements[i]; + const playerId = waveformElement.getAttribute('data-audio-id'); + + // Avoid repeated DOM querying by caching the result + const playButton = document.querySelector(`.btn-toggle-play[data-player-id="${playerId}"]`); + + // Ensure that WaveSurfer is only initialized if not already initialized + if (typeof WaveSurfer !== 'undefined' && waveformElement && !waveformElement.dataset.initialized) { + // Mark this element as initialized to avoid reinitialization + waveformElement.dataset.initialized = 'true'; + + // Initialize WaveSurfer for this waveform + initilaizeWavesurfer(waveformElement, waveformElement.getAttribute('data-audio-url'), playButton, playerId); + } + } + } + diff --git a/src/views/layouts/messageCard.blade.php b/src/views/layouts/messageCard.blade.php index b7b57cb4..24405905 100644 --- a/src/views/layouts/messageCard.blade.php +++ b/src/views/layouts/messageCard.blade.php @@ -40,8 +40,8 @@
messenger_color ?? 'blue'; ?>
-