messages.blade.php 22 KB

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