messages.blade.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. @extends('app.my-account.admin.users.single')
  2. @section('details')
  3. <div>
  4. <script src="https://static.opentok.com/v2/js/opentok.min.js"></script>
  5. <div class="pb-5">
  6. <div class="custom-card w-100 p-0 patients-content">
  7. <div class="mcp-theme-1 im-body" id="messages">
  8. <div class="card overflow-hidden border-0 h-100">
  9. <div class="msg-header p-3 d-flex align-items-center shadow-sm">
  10. <strong class="">
  11. <i class="fas fa-comments mr-2"></i>
  12. Messages
  13. </strong>
  14. </div>
  15. <div class="card-body p-0 h-100">
  16. <div class="im-container h-100">
  17. <div class="im-rhs p-2">
  18. @if (!$messages || !count($messages))
  19. <div class="py-4 text-center text-secondary">
  20. <h3 class="mb-0"><i class="far fa-comment-dots"></i></h3>
  21. No messages yet!
  22. </div>
  23. @endif
  24. <div class="im-messages">
  25. <div class="mh-100 overflow-auto opacity-0 mb-2" id="im-scroller">
  26. <?php $messageDates = []; ?>
  27. @foreach ($messages as $k => $message)
  28. @if (!in_array(friendly_date($message->created_at), $messageDates))
  29. <?php array_push($messageDates, friendly_date($message->created_at)); ?>
  30. <div class="text-center my-4">
  31. <span style="background:#c2efff"
  32. class="py-1 px-3 text-secondary rounded text-sm">{{ friendly_date_est($message->created_at) }}</span>
  33. </div>
  34. @endif
  35. <div class="d-flex align-items-start">
  36. @if ($message->from_user_id !== $performer->user->id)
  37. <div class="d-none">
  38. @if (!$message->is_removed)
  39. @if ($k == 0 || $messages[$k - 1]->from_user_id !== $messages[$k]->from_user_id)
  40. <!-- remove unecessary timestamps -->
  41. <div class="circle-icon">
  42. {{ mb_substr($message->fromUser->name_first, 0, 1) }}
  43. </div>
  44. @else
  45. <div class="circle-icon op">
  46. {{ mb_substr($message->fromUser->name_first, 0, 1) }}
  47. </div>
  48. @endif
  49. @endif
  50. </div>
  51. @endif
  52. <div class="w-100">
  53. <div class="im-message mt-0 pe-5 {{ $message->from_user_id === $performer->user->id ? 'sent' : 'received' }}"
  54. data-uid="{{ $message->uid }}"
  55. data-mark-as-read="{{ !$message->is_read ? 1 : 0 }}">
  56. <div class="im-message-sender align-items-center">
  57. @if (!$message->is_removed)
  58. @if ($k == 0 || $messages[$k - 1]->from_user_id !== $messages[$k]->from_user_id)
  59. <!-- remove unecessary timestamps -->
  60. @if ($message->from_user_id !== $performer->user->id)
  61. <small
  62. class="me-2 text-secondary">{{ $message->fromUser->displayName() }},</small>
  63. @endif
  64. <small
  65. class="header-item text-secondary text-sm">{{ friendly_time_by_time_zone($message->created_at) }}</small>
  66. @endif
  67. @if ($performer->user->id == $message->created_by_user_id && $message->content_text)
  68. <div class="header-item">
  69. <div moe large relative>
  70. @if ($k == 0 || $messages[$k - 1]->from_user_id !== $messages[$k]->from_user_id)
  71. <a href="#" start show><i
  72. class="fa fa-edit on-hover-opaque text-pry text-sm hidden edit"
  73. style="top:18px;"></i></a>
  74. @else
  75. <a href="#" start show><i
  76. class="fa fa-edit on-hover-opaque text-pry text-sm hidden edit"></i></a>
  77. @endif
  78. <form url="/api/message/editContent"
  79. class="text-left" right>
  80. <input type="hidden" name="uid"
  81. value="{{ $message->uid }}">
  82. <div class="mb-2">
  83. <label class="mb-1 text-sm">Edit
  84. Message</label>
  85. <textarea name="contentText" rows="3" class="form-control form-control-sm">{{ $message->content_text }}</textarea>
  86. </div>
  87. <div class="mt-3">
  88. <button submit
  89. class="btn btn-sm btn-primary mr-2">Submit</button>
  90. <button cancel
  91. class="btn btn-default btn-sm border">Cancel</button>
  92. </div>
  93. </form>
  94. </div>
  95. </div>
  96. @endif
  97. @if ($message->from_user_id === $performer->user->id)
  98. <span class="header-item hidden delete">
  99. <a href="#" native class="remove-message"
  100. data-message-uid="{{ $message->uid }}"><i
  101. class="fa fa-trash-alt text-danger text-sm"></i></a>
  102. </span>
  103. @endif
  104. @endif
  105. </div>
  106. @if ($message->is_removed)
  107. <div class="im-message-content text-secondary font-italic">
  108. This message was removed.</div>
  109. @else
  110. @if ($message->content_text)
  111. <?php
  112. $message->content_text = preg_replace_callback(
  113. '~(?:http|ftp)s?://(?:www\.)?([a-z0-9.-]+\.[a-z]{2,3}(?:/\S*)?)~i',
  114. function ($match) {
  115. return '<a native target="_blank" href="' . $match[0] . '">' . $match[0] . '</a>';
  116. },
  117. $message->content_text,
  118. );
  119. ?>
  120. <div class="im-message-content">{!! $message->content_text !!}
  121. </div>
  122. @endif
  123. @endif
  124. @if (count($message->attachments) && !$message->is_removed)
  125. <div class="attachments-container mt-1 d-flex align-items-center flex-wrap {{ $message->from_user_id === $performer->user->id ? 'justify-content-end' : 'justify-content-start' }}"
  126. data-message-uid="{{ $message->uid }}"
  127. data-attachments-loaded="0">
  128. <span class="my-1 text-primary c-pointer text-sm">
  129. <i class="fa fa-paperclip"></i>
  130. {{ count($message->attachments) }}
  131. attachment{{ count($message->attachments) === 1 ? '' : 's' }}
  132. </span>
  133. </div>
  134. @endif
  135. </div>
  136. </div>
  137. </div>
  138. @endforeach
  139. </div>
  140. </div>
  141. @if ($toUser)
  142. <div class="im-input">
  143. <div class="d-flex align-items-end">
  144. <div class="msg-input">
  145. <textarea placeholder="Enter your message here..."></textarea>
  146. <button class="btn btn-sm btn-input" id="im-btn-send"><i
  147. class="fa fa-paper-plane"></i></button>
  148. </div>
  149. <div class="fabs ms-2">
  150. <button class="btn btn-sm btn-primary ms-2" id="im-btn-select-file"><i
  151. class="fa fa-paperclip"></i></button>
  152. </div>
  153. </div>
  154. <div class="d-flex align-items-end flex-wrap" id="selected-files">
  155. </div>
  156. </div>
  157. @endif
  158. </div>
  159. </div>
  160. </div>
  161. </div>
  162. </div>
  163. </div>
  164. </div>
  165. <script>
  166. $(document).ready(function() {
  167. setTimeout(function() {
  168. jQuery('.mark-as-read').click();
  169. }, 1000);
  170. let inProgress = false;
  171. function showSelectedFiles() {
  172. $('#selected-files').empty();
  173. $('.im-file-upload').each(function() {
  174. if (this.files && this.files.length) {
  175. for (let i = 0; i < this.files.length; i++) {
  176. $('#selected-files').append($('<div class="selected-file" data-id="' + this.id +
  177. '" title="Click to remove">').text(this.files[i].name));
  178. }
  179. }
  180. });
  181. var hght = $('#selected-files')[0].scrollHeight;
  182. $('.im-messages').css('margin-bottom', (hght + 100) + 'px');
  183. $('.im-messages').scrollTop($('.im-messages')[0].scrollHeight);
  184. }
  185. function hasError(_data) {
  186. let msg = 'Unknown error!';
  187. if (_data) {
  188. if (_data.success) return false;
  189. else if (_data.message) msg = _data.message;
  190. }
  191. toastr.error(msg);
  192. return true;
  193. }
  194. function showInnerMask(){
  195. $('.msg-input textarea').css('background-color', '#fff !important');
  196. $('.msg-input textarea').prop('disabled', true);
  197. $('.msg-input button').prop('disabled', true);
  198. $('.msg-input i').removeClass('fa-paper-plane');
  199. $('.msg-input i').addClass('fa-circle-notch fa-spin ms-0');
  200. }
  201. function hideInnerMask(){
  202. $('.msg-input textarea').prop('disabled', false);
  203. $('.msg-input button').prop('disabled', false);
  204. $('.msg-input i').addClass('fa-paper-plane');
  205. $('.msg-input i').removeClass('fa-circle-notch fa-spin ms-0');
  206. }
  207. function doSend(_elem) {
  208. if (inProgress) return false;
  209. inProgress = true;
  210. showInnerMask();
  211. let text = $.trim(_elem.value);
  212. let formData = new FormData();
  213. formData.set('fromUserUid', '{{ $performer->user->uid }}');
  214. formData.set('toUserUid', '{{ isset($toUser) ? $toUser->uid : null }}');
  215. formData.set('contentText', text);
  216. let hasFiles = false;
  217. $('.im-file-upload').each(function() {
  218. if (this.files && this.files.length) {
  219. for (let i = 0; i < this.files.length; i++) {
  220. formData.append('attachments', this.files[i]);
  221. hasFiles = true;
  222. }
  223. }
  224. });
  225. if (!hasFiles && !text) { // either attachment or text or both should be there
  226. inProgress = false;
  227. hideInnerMask();
  228. return false;
  229. }
  230. jQuery.ajax('/api/message/create', {
  231. dataType: 'json',
  232. data: formData,
  233. processData: false,
  234. contentType: false,
  235. type: 'POST',
  236. }).done(function(_data) {
  237. if (!hasError(_data)) {
  238. fastReload();
  239. } else {
  240. $('.im-input textarea').val('');
  241. inProgress = false;
  242. hideInnerMask();
  243. }
  244. });
  245. return false;
  246. }
  247. function init() {
  248. @if (isset($user))
  249. $('.im-input textarea').on('keydown', function(_e) {
  250. if (_e.which === 13 && !_e.shiftKey) {
  251. return doSend(this);
  252. }
  253. });
  254. $('#im-btn-send').click(function() {
  255. return doSend($('.im-input textarea')[0]);
  256. });
  257. $('#plusBtn').click(function() {
  258. $('.hide').toggle();
  259. $('.show').toggle();
  260. });
  261. $('#im-btn-select-file').click(function() {
  262. let fiID = Math.floor(Math.random() * 10000);
  263. let fileInput = $('<input type="file" class="d-none im-file-upload" accept="image/*, .pdf, .doc, .docx, application/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document" id="fu-' +
  264. fiID + '">');
  265. $('.im-input').append(fileInput)
  266. fileInput.click();
  267. });
  268. $(document).on('change', '.im-file-upload', function() {
  269. showSelectedFiles();
  270. });
  271. $(document)
  272. .off('click', '.selected-file')
  273. .on('click', '.selected-file', function() {
  274. $('#' + $(this).attr('data-id')).remove();
  275. showSelectedFiles();
  276. });
  277. $(document)
  278. .off('click.load-attachments', '.attachments-container[data-attachments-loaded="0"]>span')
  279. .on('click.load-attachments', '.attachments-container[data-attachments-loaded="0"]>span',
  280. function() {
  281. let container = $(this).closest('.attachments-container');
  282. if (inProgress) return false;
  283. inProgress = true;
  284. $.get('/admin/users/view/{{$user->uid}}/messages/' + container.attr(
  285. 'data-message-uid') + '/attachments', (_data) => {
  286. container.html(_data).attr('data-attachments-loaded', 1);
  287. }).then(function() {
  288. inProgress = false;
  289. });
  290. });
  291. $(document)
  292. .off('click', '.mark-as-read')
  293. .on('click', '.mark-as-read', function() {
  294. $.post('/api/message/markRead', {
  295. uid: $(this).attr('data-message-uid')
  296. }, () => {
  297. $(this).replaceWith(
  298. '<i class="fa fa-check text-secondary on-hover-opaque text-sm"></i>'
  299. );
  300. let unreadBadge = $('.unread-badge[data-user-id="' +
  301. {{ $user->id }} + '"]').first();
  302. if (unreadBadge.length) {
  303. let newCount = (+unreadBadge.text()) - 1;
  304. if (newCount) {
  305. unreadBadge.text(newCount);
  306. } else {
  307. unreadBadge.remove();
  308. }
  309. }
  310. }, 'json');
  311. return false;
  312. });
  313. $(document)
  314. .off('click', '.remove-message')
  315. .on('click', '.remove-message', function() {
  316. $.post('/api/message/markAsRemoved', {
  317. uid: $(this).attr('data-message-uid')
  318. }, () => {
  319. $(this).closest('.im-message').html(
  320. '<div class="im-message-content text-secondary font-italic">This message was removed.</div>'
  321. );
  322. }, 'json');
  323. return false;
  324. });
  325. var imScroller = document.getElementById("im-scroller");
  326. imScroller.scrollTop = imScroller.scrollHeight;
  327. $(imScroller).removeClass('opacity-0');
  328. @endif
  329. }
  330. init();
  331. });
  332. </script>
  333. </div>
  334. @endsection