Skip to content

Commit

Permalink
feat: make toolbar and dropdown buttons dynamic so they can be rearra…
Browse files Browse the repository at this point in the history
…nged more easily
  • Loading branch information
Lars- committed Dec 4, 2024
1 parent 1105880 commit 93391f8
Show file tree
Hide file tree
Showing 2 changed files with 244 additions and 32 deletions.
173 changes: 173 additions & 0 deletions app/Helpers/ConversationActions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<?php

namespace App\Helpers;

use App\Conversation;
use Eventy;
use Request;

class ConversationActions {
// Location constants
const LOCATION_TOOLBAR = 'toolbar';
const LOCATION_DROPDOWN = 'dropdown';
const LOCATION_BOTH = 'both';

/**
* Get all available conversation actions
*/
public static function getActions( $conversation, $user, $mailbox ) {
$actions = [
// Default toolbar actions - keeping them in toolbar for backwards compatibility
'reply' => [
'icon' => 'glyphicon-share-alt',
'location' => self::LOCATION_TOOLBAR, // Forcing toolbar location for compatibility
'label' => __( 'Reply' ),
'permission' => function ( $conversation ) {
return ( ! $conversation->isPhone() || ( $conversation->customer && $conversation->customer->getMainEmail() ) )
&& Eventy::filter( 'conversation.reply_button.enabled', true, $conversation );
},
'class' => 'conv-reply',
'fixed_location' => true,
],
'note' => [
'icon' => 'glyphicon-edit',
'location' => self::LOCATION_TOOLBAR, // Forcing toolbar location for compatibility
'label' => __( 'Note' ),
'permission' => function ( $conversation ) {
return Eventy::filter( 'conversation.note_button.enabled', true, $conversation );
},
'class' => 'conv-add-note',
'fixed_location' => true,
],
'delete' => [
'icon' => 'glyphicon-trash',
'location' => self::LOCATION_BOTH, // Keeping both locations for compatibility
'label' => $conversation->state != Conversation::STATE_DELETED ? __( 'Delete' ) : __( 'Delete Forever' ),
'permission' => function ( $conversation, $user ) {
return $user->can( 'delete', $conversation );
},
'class' => $conversation->state != Conversation::STATE_DELETED ? 'conv-delete' : 'conv-delete-forever',
'mobile_only' => true,
'fixed_location' => true,
],

// Default dropdown actions - keeping them in dropdown for backwards compatibility
'follow' => [
'icon' => 'glyphicon-bell',
'location' => self::LOCATION_DROPDOWN,
'label' => __( 'Follow' ),
'permission' => function () {
return true;
},
'class' => 'conv-follow',
'has_opposite' => true,
'opposite' => [
'label' => __( 'Unfollow' ),
'class' => 'conv-follow',
],
'fixed_location' => true,
],
'forward' => [
'icon' => 'glyphicon-arrow-right',
'location' => self::LOCATION_DROPDOWN,
'label' => __( 'Forward' ),
'permission' => function () {
return true;
},
'class' => 'conv-forward',
'fixed_location' => true,
],
'merge' => [
'icon' => 'glyphicon-indent-left',
'location' => self::LOCATION_DROPDOWN,
'label' => __( 'Merge' ),
'permission' => function ( $conversation ) {
return ! $conversation->isChat();
},
'class' => '',
'url' => function ( $conversation ) {
return route( 'conversations.ajax_html', array_merge( [ 'action' => 'merge_conv' ], Request::all(), [ 'conversation_id' => $conversation->id ] ) );
},
'attrs' => [
'data-trigger' => 'modal',
'data-modal-title' => __( 'Merge Conversations' ),
'data-modal-no-footer' => 'true',
'data-modal-on-show' => 'initMergeConv',
],
'fixed_location' => true,
],
'move' => [
'icon' => 'glyphicon-log-out',
'location' => self::LOCATION_DROPDOWN,
'label' => __( 'Move' ),
'permission' => function ( $conversation, $user ) {
return $user->can( 'move', Conversation::class );
},
'class' => '',
'url' => function ( $conversation ) {
return route( 'conversations.ajax_html', array_merge( [ 'action' => 'move_conv' ], Request::all(), [ 'conversation_id' => $conversation->id ] ) );
},
'attrs' => [
'data-trigger' => 'modal',
'data-modal-title' => __( 'Move Conversation' ),
'data-modal-no-footer' => 'true',
'data-modal-on-show' => 'initMoveConv',
],
'fixed_location' => true,
],
'print' => [
'icon' => 'glyphicon-print',
'location' => self::LOCATION_DROPDOWN,
'label' => __( 'Print' ),
'permission' => function () {
return true;
},
'url' => function () {
return Request::getRequestUri() . '&amp;print=1';
},
'attrs' => [
'target' => '_blank',
],
'fixed_location' => true,
'class' => '',
],
];

// Allow overriding default actions while preserving backwards compatibility
$actions = Eventy::filter( 'conversation.actions', $actions, $conversation, $user, $mailbox );

// Filter actions based on permissions
foreach ( $actions as $key => $action ) {
if ( ! $action['permission']( $conversation, $user ) ) {
unset( $actions[ $key ] );
}
}

return $actions;
}

/**
* Get actions for a specific location
*/
public static function getActionsByLocation( $actions, $location ) {
return array_filter( $actions, function ( $action ) use ( $location ) {
// If action has fixed_location, respect its original location
if ( ! empty( $action['fixed_location'] ) ) {
return $action['location'] === $location || $action['location'] === self::LOCATION_BOTH;
}

// For new/custom actions, use the specified location
return $action['location'] === $location || $action['location'] === self::LOCATION_BOTH;
} );
}

/**
* Override an action's location
* This should only be used for custom actions, not default ones
*/
public static function setActionLocation( $action_key, $location ) {
// This method can be used to change location of custom actions
// Default actions will maintain their original location for compatibility
return Eventy::filter( 'conversation.action.location', $location, $action_key );
}
}
103 changes: 71 additions & 32 deletions resources/views/conversations/view.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,45 +27,84 @@
<div id="conv-toolbar">

<div class="conv-actions">
@php
$actions = \App\Helpers\ConversationActions::getActions($conversation, Auth::user(), $mailbox);
$toolbar_actions = \App\Helpers\ConversationActions::getActionsByLocation($actions, \App\Helpers\ConversationActions::LOCATION_TOOLBAR);
$dropdown_actions = \App\Helpers\ConversationActions::getActionsByLocation($actions, \App\Helpers\ConversationActions::LOCATION_DROPDOWN);
@endphp

{{-- There should be no spaces between buttons --}}
@if ((!$conversation->isPhone() || ($customer && $customer->getMainEmail())) && Eventy::filter('conversation.reply_button.enabled', true, $conversation) )
<span class="conv-reply conv-action glyphicon glyphicon-share-alt" data-toggle="tooltip" data-placement="bottom" title="{{ __("Reply") }}" aria-label="{{ __("Reply") }}" role="button"></span>
@endif
@if (Eventy::filter('conversation.note_button.enabled', true, $conversation))
<span class="conv-add-note conv-action glyphicon glyphicon-edit" data-toggle="tooltip" data-placement="bottom" title="{{ __("Note") }}" aria-label="{{ __("Note") }}" role="button"></span>
@endif
@if (Auth::user()->can('delete', $conversation))
@if ($conversation->state != App\Conversation::STATE_DELETED)
<span class="hidden-xs conv-action glyphicon glyphicon-trash conv-delete" data-toggle="tooltip" data-placement="bottom" title="{{ __("Delete") }}" aria-label="{{ __("Delete") }}" role="button"></span>
@foreach ($toolbar_actions as $action_key => $action)
@if (!empty($action['url']))
{{-- Action with URL (like move, merge) --}}
<a href="{{ $action['url']($conversation) }}"
class="{{ $action['class'] }} conv-action"
role="button"
@if (!empty($action['mobile_only']))class="hidden-xs"@endif
@if (!empty($action['attrs']))
@foreach ($action['attrs'] as $attr_key => $attr_value)
{{ $attr_key }}="{{ $attr_value }}"
@endforeach
@endif
data-toggle="tooltip"
data-placement="bottom"
title="{{ $action['label'] }}"
aria-label="{{ $action['label'] }}">
<i class="glyphicon {{ $action['icon'] }}"></i>
</a>
@else
<span class="hidden-xs conv-action glyphicon glyphicon-trash conv-delete-forever" data-toggle="tooltip" data-placement="bottom" title="{{ __("Delete Forever") }}" aria-label="{{ __("Delete Forever") }}" role="button"></span>
{{-- Simple button action --}}
<span class="@if (!empty($action['mobile_only']))hidden-xs @endif {{ $action['class'] }} conv-action glyphicon {{ $action['icon'] }}"
data-toggle="tooltip"
data-placement="bottom"
title="{{ $action['label'] }}"
aria-label="{{ $action['label'] }}"
role="button"
@if (!empty($action['attrs']))
@foreach ($action['attrs'] as $attr_key => $attr_value)
{{ $attr_key }}="{{ $attr_value }}"
@endforeach
@endif
></span>
@endif
@endif
@endforeach

@action('conversation.action_buttons', $conversation, $mailbox)

<div class="dropdown conv-action" data-toggle="tooltip" title="{{ __("More Actions") }}">
<span class="conv-action glyphicon glyphicon-option-horizontal dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" aria-label="{{ __("More Actions") }}"></span>
{{-- More Actions Dropdown --}}
<div class="dropdown conv-action" data-toggle="tooltip" title="{{ __('More Actions') }}">
<span class="conv-action glyphicon glyphicon-option-horizontal dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
aria-label="{{ __('More Actions') }}"></span>
<ul class="dropdown-menu dropdown-with-icons">
@action('conversation.prepend_action_buttons', $conversation, $mailbox)
<li>
<a href="#" class="conv-follow @if ($is_following) hidden @endif" data-follow-action="follow" role="button"><i class="glyphicon glyphicon-bell"></i> {{ __("Follow") }}</a>
<a href="#" class="conv-follow @if (!$is_following) hidden @endif" data-follow-action="unfollow" role="button"><i class="glyphicon glyphicon-bell"></i> {{ __("Unfollow") }}</a>
</li>
<li><a href="#" class="conv-forward" role="button"><i class="glyphicon glyphicon-arrow-right"></i> {{ __("Forward") }}</a></li>
@if (!$conversation->isChat())
<li><a href="{{ route('conversations.ajax_html', array_merge(['action' =>
'merge_conv'], \Request::all(), ['conversation_id' => $conversation->id]) ) }}" data-trigger="modal" data-modal-title="{{ __("Merge Conversations") }}" data-modal-no-footer="true" data-modal-on-show="initMergeConv" role="button"><i class="glyphicon glyphicon-indent-left"></i> {{ __("Merge") }}</a></li>
@endif
@if (Auth::user()->can('move', App\Conversation::class))
<li><a href="{{ route('conversations.ajax_html', array_merge(['action' =>
'move_conv'], \Request::all(), ['conversation_id' => $conversation->id]) ) }}" data-trigger="modal" data-modal-title="{{ __("Move Conversation") }}" data-modal-no-footer="true" data-modal-on-show="initMoveConv" role="button"><i class="glyphicon glyphicon-log-out"></i> {{ __("Move") }}</a></li>
@endif
@if ($conversation->state != App\Conversation::STATE_DELETED)
<li class="hidden-lg hidden-md hidden-sm"><a href="#" class="conv-delete" role="button"><i class="glyphicon glyphicon-trash"></i> {{ __("Delete") }}</a></li>
@else
<li class="hidden-lg hidden-md hidden-sm"><a href="#" class="conv-delete-forever" role="button"><i class="glyphicon glyphicon-trash"></i> {{ __("Delete Forever") }}</a></li>
@endif
<li><a href="{{ \Request::getRequestUri() }}&amp;print=1" target="_blank" role="button"><i class="glyphicon glyphicon-print"></i> {{ __("Print") }}</a></li>
@foreach ($dropdown_actions as $action_key => $action)
<li>
@if (!empty($action['has_opposite']))
<a href="#" class="{{ $action['class'] }} @if ($is_following) hidden @endif" data-follow-action="follow" role="button">
<i class="glyphicon {{ $action['icon'] }}"></i> {{ $action['label'] }}
</a>
<a href="#" class="{{ $action['opposite']['class'] }} @if (!$is_following) hidden @endif" data-follow-action="unfollow" role="button">
<i class="glyphicon {{ $action['icon'] }}"></i> {{ $action['opposite']['label'] }}
</a>
@else
<a href="{{ !empty($action['url']) ? $action['url']($conversation) : '#' }}"
class="{{ $action['class'] }}"
role="button"
@if (!empty($action['attrs']))
@foreach ($action['attrs'] as $attr_key => $attr_value)
{{ $attr_key }}="{{ $attr_value }}"
@endforeach
@endif
>
<i class="glyphicon {{ $action['icon'] }}"></i> {{ $action['label'] }}
</a>
@endif
</li>
@endforeach
@action('conversation.append_action_buttons', $conversation, $mailbox)
</ul>
</div>
Expand Down

0 comments on commit 93391f8

Please sign in to comment.