chore: Inbox view enhancements
This commit is contained in:
@@ -129,7 +129,7 @@ export default {
|
||||
name,
|
||||
} = this.$route;
|
||||
if (this.isInboxView) {
|
||||
return frontendURL(`accounts/${accountId}/inbox`);
|
||||
return frontendURL(`accounts/${accountId}/inbox-view`);
|
||||
}
|
||||
return conversationListPageURL({
|
||||
accountId,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"TITLE": "Inbox",
|
||||
"DISPLAY_DROPDOWN": "Display",
|
||||
"LOADING": "Fetching notifications",
|
||||
"EOF": "All notifications loaded 🎉",
|
||||
"404": "There are no active notifications in this group.",
|
||||
"NO_NOTIFICATIONS": "No notifications",
|
||||
"NOTE": "Notifications from all subscribed inboxes",
|
||||
|
||||
@@ -50,6 +50,7 @@ import AddLabelModal from 'dashboard/routes/dashboard/settings/labels/AddLabel.v
|
||||
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel.vue';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import resizeListenerMixin from 'shared/mixins/resizeListenerMixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -61,7 +62,7 @@ export default {
|
||||
AddLabelModal,
|
||||
NotificationPanel,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
mixins: [uiSettingsMixin, resizeListenerMixin],
|
||||
data() {
|
||||
return {
|
||||
showAccountModal: false,
|
||||
@@ -109,33 +110,20 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
this.handleResize();
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
bus.$on(BUS_EVENTS.TOGGLE_SIDEMENU, this.toggleSidebar);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
bus.$off(BUS_EVENTS.TOGGLE_SIDEMENU, this.toggleSidebar);
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleResize() {
|
||||
const { SMALL_SCREEN_BREAKPOINT, LAYOUT_TYPES } = wootConstants;
|
||||
let throttled = false;
|
||||
const delay = 150;
|
||||
|
||||
if (throttled) {
|
||||
return;
|
||||
if (window.innerWidth <= SMALL_SCREEN_BREAKPOINT) {
|
||||
this.displayLayoutType = LAYOUT_TYPES.EXPANDED;
|
||||
} else {
|
||||
this.displayLayoutType = LAYOUT_TYPES.CONDENSED;
|
||||
}
|
||||
throttled = true;
|
||||
|
||||
setTimeout(() => {
|
||||
throttled = false;
|
||||
if (window.innerWidth <= SMALL_SCREEN_BREAKPOINT) {
|
||||
this.displayLayoutType = LAYOUT_TYPES.EXPANDED;
|
||||
} else {
|
||||
this.displayLayoutType = LAYOUT_TYPES.CONDENSED;
|
||||
}
|
||||
}, delay);
|
||||
},
|
||||
toggleSidebar() {
|
||||
this.updateUISettings({
|
||||
|
||||
@@ -15,12 +15,12 @@ export default {
|
||||
},
|
||||
},
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/inbox-view/:conversation_id'),
|
||||
path: frontendURL('accounts/:accountId/inbox-view/:notification_id'),
|
||||
name: 'inbox_view_conversation',
|
||||
roles: ['administrator', 'agent'],
|
||||
component: InboxView,
|
||||
props: route => {
|
||||
return { inboxId: 0, conversationId: route.params.conversation_id };
|
||||
return { inboxId: 0, notificationId: route.params.notification_id };
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
class="flex flex-col h-full w-full ltr:border-r border-slate-50 dark:border-slate-800/50"
|
||||
:class="isOnExpandedLayout ? '' : 'min-w-[360px] max-w-[360px]'"
|
||||
>
|
||||
<inbox-list-header @filter="onFilterChange" />
|
||||
<inbox-list-header
|
||||
:is-context-menu-open="isContextMenuOpen"
|
||||
@filter="onFilterChange"
|
||||
@redirect="redirectToInbox"
|
||||
/>
|
||||
<div
|
||||
ref="notificationList"
|
||||
class="flex flex-col w-full h-[calc(100%-56px)] overflow-x-hidden overflow-y-auto"
|
||||
@@ -15,6 +19,8 @@
|
||||
@mark-notification-as-read="markNotificationAsRead"
|
||||
@mark-notification-as-unread="markNotificationAsUnRead"
|
||||
@delete-notification="deleteNotification"
|
||||
@context-menu-open="isContextMenuOpen = true"
|
||||
@context-menu-close="isContextMenuOpen = false"
|
||||
/>
|
||||
<div v-if="uiFlags.isFetching" class="text-center">
|
||||
<span class="spinner mt-4 mb-4" />
|
||||
@@ -25,12 +31,6 @@
|
||||
>
|
||||
{{ $t('INBOX.LIST.NO_NOTIFICATIONS') }}
|
||||
</p>
|
||||
<p
|
||||
v-if="showEndOfListMessage"
|
||||
class="text-center text-slate-400 dark:text-slate-400 p-4"
|
||||
>
|
||||
{{ $t('INBOX.LIST.EOF') }}
|
||||
</p>
|
||||
<intersection-observer
|
||||
v-if="!showEndOfList && !uiFlags.isFetching"
|
||||
:options="infiniteLoaderOptions"
|
||||
@@ -57,7 +57,7 @@ export default {
|
||||
},
|
||||
mixins: [alertMixin, uiSettingsMixin],
|
||||
props: {
|
||||
conversationId: {
|
||||
notificationId: {
|
||||
type: [String, Number],
|
||||
default: 0,
|
||||
},
|
||||
@@ -76,6 +76,7 @@ export default {
|
||||
status: '',
|
||||
type: '',
|
||||
sortOrder: wootConstants.INBOX_SORT_BY.NEWEST,
|
||||
isContextMenuOpen: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -102,9 +103,6 @@ export default {
|
||||
showEmptyState() {
|
||||
return !this.uiFlags.isFetching && !this.notifications.length;
|
||||
},
|
||||
showEndOfListMessage() {
|
||||
return this.showEndOfList && this.notifications.length;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setSavedFilter();
|
||||
@@ -119,17 +117,15 @@ export default {
|
||||
this.$store.dispatch('notifications/index', filter);
|
||||
},
|
||||
redirectToInbox() {
|
||||
if (!this.conversationId) return;
|
||||
if (!this.notificationId) return;
|
||||
if (this.$route.name === 'inbox_view') return;
|
||||
this.$router.push({ name: 'inbox_view' });
|
||||
},
|
||||
loadMoreNotifications() {
|
||||
if (this.uiFlags.isAllNotificationsLoaded) return;
|
||||
this.$store.dispatch('notifications/index', {
|
||||
...this.inboxFilters,
|
||||
page: this.page + 1,
|
||||
status: this.status,
|
||||
type: this.type,
|
||||
sortOrder: this.sortOrder,
|
||||
});
|
||||
this.page += 1;
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<section class="flex w-full h-full bg-white dark:bg-slate-900">
|
||||
<inbox-list
|
||||
v-show="showConversationList"
|
||||
:conversation-id="conversationId"
|
||||
:notification-id="notificationId"
|
||||
:is-on-expanded-layout="isOnExpandedLayout"
|
||||
/>
|
||||
<div
|
||||
@@ -51,6 +51,7 @@ import InboxList from './InboxList.vue';
|
||||
import InboxItemHeader from './components/InboxItemHeader.vue';
|
||||
import ConversationBox from 'dashboard/components/widgets/conversation/ConversationBox.vue';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import resizeListenerMixin from 'shared/mixins/resizeListenerMixin';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||
@@ -62,17 +63,22 @@ export default {
|
||||
InboxItemHeader,
|
||||
ConversationBox,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
mixins: [uiSettingsMixin, resizeListenerMixin],
|
||||
props: {
|
||||
inboxId: {
|
||||
type: [String, Number],
|
||||
default: 0,
|
||||
},
|
||||
conversationId: {
|
||||
notificationId: {
|
||||
type: [String, Number],
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isOnExpandedLayout: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
currentAccountId: 'getCurrentAccountId',
|
||||
@@ -82,9 +88,10 @@ export default {
|
||||
uiFlags: 'notifications/getUIFlags',
|
||||
}),
|
||||
activeNotification() {
|
||||
return this.notifications.find(
|
||||
n => n.primary_actor.id === Number(this.conversationId)
|
||||
);
|
||||
return this.notifications.find(n => n.id === Number(this.notificationId));
|
||||
},
|
||||
conversationId() {
|
||||
return this.activeNotification?.primary_actor.id;
|
||||
},
|
||||
isInboxViewEnabled() {
|
||||
return this.$store.getters['accounts/isFeatureEnabledGlobally'](
|
||||
@@ -93,14 +100,14 @@ export default {
|
||||
);
|
||||
},
|
||||
showConversationList() {
|
||||
return this.isOnExpandedLayout ? !this.conversationId : true;
|
||||
return this.isOnExpandedLayout ? !this.notificationId : true;
|
||||
},
|
||||
isFetchingInitialData() {
|
||||
return this.uiFlags.isFetching && !this.notifications.length;
|
||||
},
|
||||
showInboxMessageView() {
|
||||
return (
|
||||
Boolean(this.conversationId) &&
|
||||
Boolean(this.notificationId) &&
|
||||
Boolean(this.currentChat.id) &&
|
||||
!this.isFetchingInitialData
|
||||
);
|
||||
@@ -109,20 +116,11 @@ export default {
|
||||
return this.notifications?.length ?? 0;
|
||||
},
|
||||
activeNotificationIndex() {
|
||||
const conversationId = Number(this.conversationId);
|
||||
const notificationIndex = this.notifications.findIndex(
|
||||
n => n.primary_actor.id === conversationId
|
||||
n => n.id === Number(this.notificationId)
|
||||
);
|
||||
return notificationIndex >= 0 ? notificationIndex + 1 : 0;
|
||||
},
|
||||
isOnExpandedLayout() {
|
||||
const {
|
||||
LAYOUT_TYPES: { CONDENSED },
|
||||
} = wootConstants;
|
||||
const { conversation_display_type: conversationDisplayType = CONDENSED } =
|
||||
this.uiSettings;
|
||||
return conversationDisplayType !== CONDENSED;
|
||||
},
|
||||
isContactPanelOpen() {
|
||||
if (this.currentChat.id) {
|
||||
const { is_contact_sidebar_open: isContactSidebarOpen } =
|
||||
@@ -149,10 +147,19 @@ export default {
|
||||
});
|
||||
}
|
||||
this.$store.dispatch('agents/get');
|
||||
this.handleResize();
|
||||
},
|
||||
methods: {
|
||||
handleResize() {
|
||||
const { SMALL_SCREEN_BREAKPOINT } = wootConstants;
|
||||
if (window.innerWidth <= SMALL_SCREEN_BREAKPOINT) {
|
||||
this.isOnExpandedLayout = true;
|
||||
} else {
|
||||
this.isOnExpandedLayout = false;
|
||||
}
|
||||
},
|
||||
async fetchConversationById() {
|
||||
if (!this.conversationId) return;
|
||||
if (!this.notificationId || !this.conversationId) return;
|
||||
const chat = this.findConversation();
|
||||
if (!chat) {
|
||||
await this.$store.dispatch('getConversation', this.conversationId);
|
||||
@@ -169,8 +176,9 @@ export default {
|
||||
});
|
||||
},
|
||||
findConversation() {
|
||||
const conversationId = Number(this.conversationId);
|
||||
return this.allConversation.find(c => c.id === conversationId);
|
||||
return this.allConversation.find(
|
||||
c => c.id === Number(this.conversationId)
|
||||
);
|
||||
},
|
||||
navigateToConversation(activeIndex, direction) {
|
||||
const indexOffset = direction === 'next' ? 0 : -2;
|
||||
@@ -180,7 +188,7 @@ export default {
|
||||
id,
|
||||
primary_actor_id: primaryActorId,
|
||||
primary_actor_type: primaryActorType,
|
||||
primary_actor: { id: conversationId, meta: { unreadCount } = {} },
|
||||
primary_actor: { meta: { unreadCount } = {} },
|
||||
notification_type: notificationType,
|
||||
} = targetNotification;
|
||||
|
||||
@@ -197,7 +205,7 @@ export default {
|
||||
|
||||
this.$router.push({
|
||||
name: 'inbox_view_conversation',
|
||||
params: { conversation_id: conversationId },
|
||||
params: { notification_id: id },
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -166,11 +166,11 @@ export default {
|
||||
id,
|
||||
primary_actor_id: primaryActorId,
|
||||
primary_actor_type: primaryActorType,
|
||||
primary_actor: { id: conversationId, inbox_id: inboxId },
|
||||
primary_actor: { inbox_id: inboxId },
|
||||
notification_type: notificationType,
|
||||
} = notification;
|
||||
|
||||
if (this.$route.params.conversation_id !== conversationId) {
|
||||
if (this.$route.params.notification_id !== id) {
|
||||
this.$track(INBOX_EVENTS.OPEN_CONVERSATION_VIA_INBOX, {
|
||||
notificationType,
|
||||
});
|
||||
@@ -184,13 +184,14 @@ export default {
|
||||
|
||||
this.$router.push({
|
||||
name: 'inbox_view_conversation',
|
||||
params: { inboxId, conversation_id: conversationId },
|
||||
params: { inboxId, notification_id: id },
|
||||
});
|
||||
}
|
||||
},
|
||||
closeContextMenu() {
|
||||
this.isContextMenuOpen = false;
|
||||
this.contextMenuPosition = { x: null, y: null };
|
||||
this.$emit('context-menu-close');
|
||||
},
|
||||
openContextMenu(e) {
|
||||
this.closeContextMenu();
|
||||
@@ -200,6 +201,7 @@ export default {
|
||||
y: e.pageY || e.clientY,
|
||||
};
|
||||
this.isContextMenuOpen = true;
|
||||
this.$emit('context-menu-open');
|
||||
},
|
||||
handleAction(key) {
|
||||
switch (key) {
|
||||
|
||||
@@ -69,12 +69,29 @@ export default {
|
||||
InboxDisplayMenu,
|
||||
},
|
||||
mixins: [clickaway, alertMixin],
|
||||
props: {
|
||||
isContextMenuOpen: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showInboxDisplayMenu: false,
|
||||
showInboxOptionMenu: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
isContextMenuOpen: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.showInboxDisplayMenu = false;
|
||||
this.showInboxOptionMenu = false;
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
markAllRead() {
|
||||
this.$track(INBOX_EVENTS.MARK_ALL_NOTIFICATIONS_AS_READ);
|
||||
@@ -100,21 +117,19 @@ export default {
|
||||
},
|
||||
onInboxOptionMenuClick(key) {
|
||||
this.showInboxOptionMenu = false;
|
||||
if (key === 'mark_all_read') {
|
||||
this.markAllRead();
|
||||
}
|
||||
if (key === 'delete_all') {
|
||||
this.deleteAll();
|
||||
}
|
||||
if (key === 'delete_all_read') {
|
||||
this.deleteAllRead();
|
||||
}
|
||||
const actions = {
|
||||
mark_all_read: () => this.markAllRead(),
|
||||
delete_all: () => this.deleteAll(),
|
||||
delete_all_read: () => this.deleteAllRead(),
|
||||
};
|
||||
const action = actions[key];
|
||||
if (action) action();
|
||||
this.$emit('redirect');
|
||||
},
|
||||
onFilterChange(option) {
|
||||
this.$emit('filter', option);
|
||||
this.showInboxDisplayMenu = false;
|
||||
if (this.$route.name === 'inbox_view') return;
|
||||
this.$router.push({ name: 'inbox_view' });
|
||||
this.$emit('redirect');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
export default {
|
||||
mounted() {
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.onResizeHandler);
|
||||
},
|
||||
methods: {
|
||||
// Resize event for layout changes
|
||||
onResizeHandler() {
|
||||
let throttled = false;
|
||||
const delay = 150;
|
||||
|
||||
if (throttled) {
|
||||
return;
|
||||
}
|
||||
throttled = true;
|
||||
|
||||
setTimeout(() => {
|
||||
throttled = false;
|
||||
this.handleResize();
|
||||
}, delay);
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user