feat: Rewrite keyboardEventListener mixin to a composable (#9831)

This commit is contained in:
Sivin Varghese
2024-08-05 18:59:47 +05:30
committed by GitHub
parent b4b308336f
commit e0b67bb552
9 changed files with 474 additions and 120 deletions

View File

@@ -1,14 +1,15 @@
<script>
import { ref } from 'vue';
import { mapGetters } from 'vuex';
import { useUISettings } from 'dashboard/composables/useUISettings';
import { useAlert } from 'dashboard/composables';
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
import VirtualList from 'vue-virtual-scroll-list';
import ChatListHeader from './ChatListHeader.vue';
import ConversationAdvancedFilter from './widgets/conversation/ConversationAdvancedFilter.vue';
import ChatTypeTabs from './widgets/ChatTypeTabs.vue';
import ConversationItem from './ConversationItem.vue';
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
import conversationMixin from '../mixins/conversations';
import wootConstants from 'dashboard/constants/globals';
import advancedFilterTypes from './widgets/conversation/advancedFilterItems';
@@ -41,7 +42,7 @@ export default {
IntersectionObserver,
VirtualList,
},
mixins: [conversationMixin, keyboardEventListenerMixins, filterMixin],
mixins: [conversationMixin, filterMixin],
provide() {
return {
// Actions to be performed on virtual list item and context menu.
@@ -89,8 +90,63 @@ export default {
setup() {
const { uiSettings } = useUISettings();
const conversationListRef = ref(null);
const getKeyboardListenerParams = () => {
const allConversations = conversationListRef.value.querySelectorAll(
'div.conversations-list div.conversation'
);
const activeConversation = conversationListRef.value.querySelector(
'div.conversations-list div.conversation.active'
);
const activeConversationIndex = [...allConversations].indexOf(
activeConversation
);
const lastConversationIndex = allConversations.length - 1;
return {
allConversations,
activeConversation,
activeConversationIndex,
lastConversationIndex,
};
};
const handlePreviousConversation = () => {
const { allConversations, activeConversationIndex } =
getKeyboardListenerParams();
if (activeConversationIndex === -1) {
allConversations[0].click();
}
if (activeConversationIndex >= 1) {
allConversations[activeConversationIndex - 1].click();
}
};
const handleNextConversation = () => {
const {
allConversations,
activeConversationIndex,
lastConversationIndex,
} = getKeyboardListenerParams();
if (activeConversationIndex === -1) {
allConversations[lastConversationIndex].click();
} else if (activeConversationIndex < lastConversationIndex) {
allConversations[activeConversationIndex + 1].click();
}
};
const keyboardEvents = {
'Alt+KeyJ': {
action: () => handlePreviousConversation(),
allowOnFocusedInput: true,
},
'Alt+KeyK': {
action: () => handleNextConversation(),
allowOnFocusedInput: true,
},
};
useKeyboardEvents(keyboardEvents, conversationListRef);
return {
uiSettings,
conversationListRef,
};
},
data() {
@@ -113,7 +169,7 @@ export default {
isContextMenuOpen: false,
appliedFilter: [],
infiniteLoaderOptions: {
root: this.$refs.conversationList,
root: this.$refs.conversationListRef,
rootMargin: '100px 0px 100px 0px',
},
@@ -490,58 +546,6 @@ export default {
}))
);
},
getKeyboardListenerParams() {
const allConversations = this.$refs.conversationList.querySelectorAll(
'div.conversations-list div.conversation'
);
const activeConversation = this.$refs.conversationList.querySelector(
'div.conversations-list div.conversation.active'
);
const activeConversationIndex = [...allConversations].indexOf(
activeConversation
);
const lastConversationIndex = allConversations.length - 1;
return {
allConversations,
activeConversation,
activeConversationIndex,
lastConversationIndex,
};
},
handlePreviousConversation() {
const { allConversations, activeConversationIndex } =
this.getKeyboardListenerParams();
if (activeConversationIndex === -1) {
allConversations[0].click();
}
if (activeConversationIndex >= 1) {
allConversations[activeConversationIndex - 1].click();
}
},
handleNextConversation() {
const {
allConversations,
activeConversationIndex,
lastConversationIndex,
} = this.getKeyboardListenerParams();
if (activeConversationIndex === -1) {
allConversations[lastConversationIndex].click();
} else if (activeConversationIndex < lastConversationIndex) {
allConversations[activeConversationIndex + 1].click();
}
},
getKeyboardEvents() {
return {
'Alt+KeyJ': {
action: () => this.handlePreviousConversation(),
allowOnFocusedInput: true,
},
'Alt+KeyK': {
action: () => this.handleNextConversation(),
allowOnFocusedInput: true,
},
};
},
resetAndFetchData() {
this.appliedFilter = [];
this.resetBulkActions();
@@ -927,7 +931,7 @@ export default {
@assignTeam="onAssignTeamsForBulk"
/>
<div
ref="conversationList"
ref="conversationListRef"
class="flex-1 conversations-list"
:class="{ 'overflow-hidden': isContextMenuOpen }"
>

View File

@@ -1,11 +1,13 @@
<script>
import { ref } from 'vue';
import { mapGetters } from 'vuex';
import { getSidebarItems } from './config/default-sidebar';
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
import { useRoute, useRouter } from 'dashboard/composables/route';
import PrimarySidebar from './sidebarComponents/Primary.vue';
import SecondarySidebar from './sidebarComponents/Secondary.vue';
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
import router, { routesWithPermissions } from '../../routes';
import { routesWithPermissions } from '../../routes';
import { hasPermissions } from '../../helper/permissionsHelper';
export default {
@@ -13,7 +15,6 @@ export default {
PrimarySidebar,
SecondarySidebar,
},
mixins: [keyboardEventListenerMixins],
props: {
showSecondarySidebar: {
type: Boolean,
@@ -24,6 +25,52 @@ export default {
default: '',
},
},
setup(props, { emit }) {
const sidebarRef = ref(null);
const route = useRoute();
const router = useRouter();
const toggleKeyShortcutModal = () => {
emit('openKeyShortcutModal');
};
const closeKeyShortcutModal = () => {
emit('closeKeyShortcutModal');
};
const isCurrentRouteSameAsNavigation = routeName => {
return route.name === routeName;
};
const navigateToRoute = routeName => {
if (!isCurrentRouteSameAsNavigation(routeName)) {
router.push({ name: routeName });
}
};
const keyboardEvents = {
'$mod+Slash': {
action: toggleKeyShortcutModal,
},
'$mod+Escape': {
action: closeKeyShortcutModal,
},
'Alt+KeyC': {
action: () => navigateToRoute('home'),
},
'Alt+KeyV': {
action: () => navigateToRoute('contacts_dashboard'),
},
'Alt+KeyR': {
action: () => navigateToRoute('account_overview_reports'),
},
'Alt+KeyS': {
action: () => navigateToRoute('agent_list'),
},
};
useKeyboardEvents(keyboardEvents, sidebarRef);
return {
toggleKeyShortcutModal,
sidebarRef,
};
},
data() {
return {
showOptionsMenu: false,
@@ -131,38 +178,6 @@ export default {
this.$store.dispatch('customViews/get', this.activeCustomView);
}
},
toggleKeyShortcutModal() {
this.$emit('openKeyShortcutModal');
},
closeKeyShortcutModal() {
this.$emit('closeKeyShortcutModal');
},
getKeyboardEvents() {
return {
'$mod+Slash': this.toggleKeyShortcutModal,
'$mod+Escape': this.closeKeyShortcutModal,
'Alt+KeyC': {
action: () => this.navigateToRoute('home'),
},
'Alt+KeyV': {
action: () => this.navigateToRoute('contacts_dashboard'),
},
'Alt+KeyR': {
action: () => this.navigateToRoute('account_overview_reports'),
},
'Alt+KeyS': {
action: () => this.navigateToRoute('agent_list'),
},
};
},
navigateToRoute(routeName) {
if (!this.isCurrentRouteSameAsNavigation(routeName)) {
router.push({ name: routeName });
}
},
isCurrentRouteSameAsNavigation(routeName) {
return this.$route.name === routeName;
},
toggleSupportChatWindow() {
window.$chatwoot.toggle();
},
@@ -180,7 +195,7 @@ export default {
</script>
<template>
<aside class="flex h-full">
<aside ref="sidebarRef" class="flex h-full">
<PrimarySidebar
:logo-source="globalConfig.logoThumbnail"
:installation-name="globalConfig.installationName"

View File

@@ -1,18 +1,14 @@
<script>
import { ref } from 'vue';
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
import { REPLY_EDITOR_MODES, CHAR_LENGTH_WARNING } from './constants';
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
export default {
name: 'ReplyTopPanel',
mixins: [keyboardEventListenerMixins],
props: {
mode: {
type: String,
default: REPLY_EDITOR_MODES.REPLY,
},
setReplyMode: {
type: Function,
default: () => {},
},
isMessageLengthReachingThreshold: {
type: Boolean,
default: () => false,
@@ -26,6 +22,36 @@ export default {
default: false,
},
},
setup(props, { emit }) {
const replyTopRef = ref(null);
const setReplyMode = mode => {
emit('setReplyMode', mode);
};
const handleReplyClick = () => {
setReplyMode(REPLY_EDITOR_MODES.REPLY);
};
const handleNoteClick = () => {
setReplyMode(REPLY_EDITOR_MODES.NOTE);
};
const keyboardEvents = {
'Alt+KeyP': {
action: () => handleNoteClick(),
allowOnFocusedInput: true,
},
'Alt+KeyL': {
action: () => handleReplyClick(),
allowOnFocusedInput: true,
},
};
useKeyboardEvents(keyboardEvents, replyTopRef);
return {
handleReplyClick,
handleNoteClick,
replyTopRef,
};
},
computed: {
replyButtonClass() {
return {
@@ -46,31 +72,14 @@ export default {
: `${this.charactersRemaining} ${CHAR_LENGTH_WARNING.UNDER_50}`;
},
},
methods: {
getKeyboardEvents() {
return {
'Alt+KeyP': {
action: () => this.handleNoteClick(),
allowOnFocusedInput: true,
},
'Alt+KeyL': {
action: () => this.handleReplyClick(),
allowOnFocusedInput: true,
},
};
},
handleReplyClick() {
this.setReplyMode(REPLY_EDITOR_MODES.REPLY);
},
handleNoteClick() {
this.setReplyMode(REPLY_EDITOR_MODES.NOTE);
},
},
};
</script>
<template>
<div class="flex justify-between bg-black-50 dark:bg-slate-800">
<div
ref="replyTopRef"
class="flex justify-between bg-black-50 dark:bg-slate-800"
>
<div class="button-group">
<woot-button
variant="clear"

View File

@@ -1091,10 +1091,10 @@ export default {
/>
<ReplyTopPanel
:mode="replyType"
:set-reply-mode="setReplyMode"
:is-message-length-reaching-threshold="isMessageLengthReachingThreshold"
:characters-remaining="charactersRemaining"
:popout-reply-box="popoutReplyBox"
@setReplyMode="setReplyMode"
@click="$emit('click')"
/>
<ArticleSearchPopover