Skip to content

Commit

Permalink
Chat Mode
Browse files Browse the repository at this point in the history
  • Loading branch information
freescout-help-desk committed Oct 14, 2023
1 parent 99bab47 commit 5efc10e
Show file tree
Hide file tree
Showing 13 changed files with 594 additions and 30 deletions.
30 changes: 30 additions & 0 deletions app/Conversation.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ class Conversation extends Model
*/
const DEFAULT_LIST_SIZE = 50;

/**
* Default size of the chats list.
*/
const CHATS_LIST_SIZE = 50;

/**
* Cache of the conversations starred by user.
*
Expand Down Expand Up @@ -2145,6 +2150,7 @@ public static function refreshConversations($conversation, $thread)
{
\App\Events\RealtimeConvNewThread::dispatchSelf($thread);
\App\Events\RealtimeMailboxNewThread::dispatchSelf($conversation->mailbox_id);
\App\Events\RealtimeChat::dispatchSelf($conversation->mailbox_id);
}

public static function getConvTableSorting($request = null)
Expand Down Expand Up @@ -2378,4 +2384,28 @@ public static function updatePreview($conversation_id)
$thread->conversation->save();
}
}

public function isInChatMode()
{
return $this->isChat() && \Helper::isChatMode() && \Route::is('conversations.view');
}

public static function getChats($mailbox_id, $offset = 0, $limit = self::CHATS_LIST_SIZE+1)
{
$chats = Conversation::where('type', self::TYPE_CHAT)
->where('mailbox_id', $mailbox_id)
->where('state', self::STATE_PUBLISHED)
->whereIn('status', [self::STATUS_ACTIVE, self::STATUS_PENDING])
->orderBy('last_reply_at', 'desc')
->offset($offset)
->limit($limit)
->get();

// Preload customers.
if (count($chats)) {
self::loadCustomers($chats);
}

return $chats;
}
}
102 changes: 102 additions & 0 deletions app/Events/RealtimeChat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php
/**
* Fefresh chats list when new thread created in mailbox.
*/
namespace App\Events;

use App\Conversation;
use App\Mailbox;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Queue\SerializesModels;

class RealtimeChat implements ShouldBroadcastNow
{
use SerializesModels;

/**
* The notification data.
*
* @var array
*/
public $data = [];

/**
* Create a new event instance.
*
* @return void
*/
public function __construct($data)
{
$this->data = $data;
}

/**
* Get the channels the event should broadcast on.
*
* @return Channel|array
*/
public function broadcastOn()
{
return new \Illuminate\Broadcasting\Channel($this->channelName());
}

/**
* Get the data to broadcast.
*
* @return array
*/
public function broadcastWith()
{
return $this->data;
}

/**
* Get the broadcast channel name for the event.
*
* @return string
*/
protected function channelName()
{
if (!empty($this->data['mailbox_id'])) {
return 'chat.'.$this->data['mailbox_id'];
} else {
return 'chat.0';
}
}

/**
* Helper funciton.
*/
public static function dispatchSelf($mailbox_id)
{
if (!\Helper::isChatModeAvailable()) {
return;
}
$notification_data = [
'mailbox_id' => $mailbox_id
];
event(new \App\Events\RealtimeChat($notification_data));
}

public static function processPayload($payload)
{
$user = auth()->user();
$mailbox = Mailbox::rememberForever()->find($payload->mailbox_id);

// Check if user can listen to this event.
if (!$user || !$mailbox || !$user->can('viewCached', $mailbox)) {
return [];
}

// Chats are retrieved in the template.
$template_data = [
'mailbox' => $mailbox,
];

$payload->chats_html = \View::make('mailboxes/partials/chat_list')->with($template_data)->render();

return $payload;
}
}
46 changes: 46 additions & 0 deletions app/Http/Controllers/ConversationsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2207,6 +2207,25 @@ public function ajax(Request $request)

break;

case 'chats_load_more':
$mailbox = Mailbox::find($request->mailbox_id);

if (!$mailbox) {
$response['msg'] = __('Mailbox not found');
} elseif (!$mailbox->userHasAccess($user->id)) {
$response['msg'] = __('Action not authorized');
}

if (!$response['msg']) {
$response['html'] = \View::make('mailboxes/partials/chat_list')->with([
'mailbox' => $mailbox,
'offset' => $request->offset,
])->render();
$response['status'] = 'success';
}

break;

default:
$response['msg'] = 'Unknown action';
break;
Expand Down Expand Up @@ -3056,4 +3075,31 @@ public function processPhoneCustomer($request)
'customer_email' => $customer_email,
];
}

/**
* View conversation.
*/
public function chats(Request $request, $mailbox_id)
{
$user = auth()->user();

$mailbox = Mailbox::findOrFailWithSettings($mailbox_id, $user->id);
$this->authorize('viewCached', $mailbox);

// Redirect to the first available chat.
$chats = Conversation::getChats($mailbox_id, 0, 1);

if (count($chats)) {
if (!\Helper::isChatMode()) {
\Helper::setChatMode(true);
}

return redirect()->away($chats[0]->url());
}

return view('conversations/chats', [
'is_in_chat_mode' => true,
'mailbox' => $mailbox,
]);
}
}
5 changes: 5 additions & 0 deletions app/Http/Middleware/CustomHandle.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ class CustomHandle
*/
public function handle($request, Closure $next)
{
// Enable/disable chat mode
if ($request->exists('chat_mode')) {
\Helper::setChatMode((int)$request->chat_mode);
}

// Hook.
\Eventy::action('middleware.web.custom_handle', $request);

Expand Down
20 changes: 20 additions & 0 deletions app/Misc/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Carbon\Carbon;
use App\Option;
use App\User;
use App\CustomerChannel;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\Console\Output\BufferedOutput;

Expand Down Expand Up @@ -2030,4 +2031,23 @@ public static function cspNonceAttr()

return ' nonce="'.\Helper::cspNonce().'"';
}

public static function isChatModeAvailable()
{
return count(CustomerChannel::getChannels());
}

public static function isChatMode()
{
return (int)\Session::get('chat_mode', 0);
}

public static function setChatMode($is_on)
{
if ((int)$is_on) {
\Session::put('chat_mode', 1);
} else {
\Session::forget('chat_mode');
}
}
}
79 changes: 79 additions & 0 deletions public/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -4253,6 +4253,7 @@ a.disabled:focus {
display: inline-block;
overflow: hidden;
word-wrap: normal;
text-overflow: ellipsis;
}
.mailbox-name .glyphicon {
top: 2px;
Expand Down Expand Up @@ -4356,11 +4357,22 @@ a.disabled:focus {
border-radius: 3px;
background-color: #97a4b0;
}
.fs-tag-green {
background-color: #52ad67;
}
.conv-subject .fs-tag {
margin-right: 6px;
position: relative;
top: 1px;
}
.fs-tag-btn {
margin-top: 8px;
margin-right: 8px;
line-height: 14px;
font-size: 15px;
padding: 4px 6px 4px 6px;
vertical-align: inherit;
}
.glyphicon-spin {
-webkit-animation: spin 1000ms infinite linear;
animation: spin 1000ms infinite linear;
Expand All @@ -4386,6 +4398,69 @@ a.disabled:focus {
}
}

.chats {
height: calc(100vh - 125px);
overflow-y: auto;
overflow-x: hidden;
margin-right: -11px;
}
.chats.sidebar-menu > li:nth-child(n+2) > a {
padding-left: 22px;
}
.chat-mode .note-statusbar {
display: none;
}
.chat-mode .conv-note-block .note-statusbar {
display: block;
}
.chat-mode .conv-note-block .note-placeholder {
/*color: #fff;*/
display: none !important;
}
.conv-top-chat {
line-height: 21px;
}
.chats.sidebar-menu > li > a {
height: auto;
border-radius: 0;
}
.chats.sidebar-menu > li {
border-radius: 0;
}
.chat-preview {
color: #93a1af;
font-size: 13.4px;
display: inline-block;
padding-top: 1px;
max-width: 217px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: normal;
}
.chats li {
border-bottom: 1px solid #e3e8eb;
}
.chats li.active {
border-bottom: 0;
}
.chats li.new {
background-color: #f4f8fd;
}
.chat-tags .fs-tag-green {
opacity: 0.9;
}
.chats-load-more {
text-align: center;
height: 45px !important;
line-height: 35px !important;
}
.chats-load-more i {
font-size: 18px !important;
position: static !important;
top: 0 !important;
}

@media (max-width: 350px) {
.conv-next-prev {
display: none !important;
Expand All @@ -4399,6 +4474,10 @@ a.disabled:focus {
.descr-block {
margin: 20px 0;
}
/* Chat placeholder is not needed on mobile as mobile devices don't have ENTER button */
.chat-mode .note-placeholder {
display: none !important;
}
}
/**
* Only main content is visible
Expand Down
Loading

0 comments on commit 5efc10e

Please sign in to comment.