feat: Rewrite uiSettings mixin to a composable (#9819)

This commit is contained in:
Sivin Varghese
2024-07-23 21:27:22 +05:30
committed by GitHub
parent 79aa5a5d7f
commit fb99ba7b40
31 changed files with 579 additions and 385 deletions

View File

@@ -116,6 +116,7 @@
<script>
import { mapGetters } from 'vuex';
import { useUISettings } from 'dashboard/composables/useUISettings';
import { useAlert } from 'dashboard/composables';
import VirtualList from 'vue-virtual-scroll-list';
@@ -132,7 +133,6 @@ import AddCustomViews from 'dashboard/routes/dashboard/customviews/AddCustomView
import DeleteCustomViews from 'dashboard/routes/dashboard/customviews/DeleteCustomViews.vue';
import ConversationBulkActions from './widgets/conversation/conversationBulkActions/Index.vue';
import filterMixin from 'shared/mixins/filterMixin';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
import countries from 'shared/constants/countries';
import { generateValuesForEditCustomViews } from 'dashboard/helper/customViewsHelper';
@@ -157,12 +157,7 @@ export default {
IntersectionObserver,
VirtualList,
},
mixins: [
conversationMixin,
keyboardEventListenerMixins,
filterMixin,
uiSettingsMixin,
],
mixins: [conversationMixin, keyboardEventListenerMixins, filterMixin],
provide() {
return {
// Actions to be performed on virtual list item and context menu.
@@ -207,6 +202,13 @@ export default {
type: Boolean,
},
},
setup() {
const { uiSettings } = useUISettings();
return {
uiSettings,
};
},
data() {
return {
activeAssigneeTab: wootConstants.ASSIGNEE_TYPE.ME,

View File

@@ -36,12 +36,12 @@
<script>
import { mapGetters } from 'vuex';
import { useAdmin } from 'dashboard/composables/useAdmin';
import { useUISettings } from 'dashboard/composables/useUISettings';
import AICTAModal from './AICTAModal.vue';
import AIAssistanceModal from './AIAssistanceModal.vue';
import aiMixin from 'dashboard/mixins/aiMixin';
import { CMD_AI_ASSIST } from 'dashboard/routes/dashboard/commands/commandBarBusEvents';
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import AIAssistanceCTAButton from './AIAssistanceCTAButton.vue';
export default {
@@ -50,10 +50,14 @@ export default {
AICTAModal,
AIAssistanceCTAButton,
},
mixins: [aiMixin, keyboardEventListenerMixins, uiSettingsMixin],
mixins: [aiMixin, keyboardEventListenerMixins],
setup() {
const { uiSettings, updateUISettings } = useUISettings();
const { isAdmin } = useAdmin();
return {
uiSettings,
updateUISettings,
isAdmin,
};
},

View File

@@ -1,14 +1,14 @@
<template>
<div class="px-0 min-w-0 flex-1">
<div class="flex-1 min-w-0 px-0">
<woot-modal-header
:header-title="$t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.TITLE')"
:header-content="$t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.DESC')"
/>
<form
class="flex flex-wrap flex-col modal-content"
class="flex flex-col flex-wrap modal-content"
@submit.prevent="finishOpenAI"
>
<div class="mt-2 w-full">
<div class="w-full mt-2">
<woot-input
v-model="value"
type="text"
@@ -19,7 +19,7 @@
@blur="$v.value.$touch"
/>
</div>
<div class="flex flex-row justify-between gap-2 py-2 px-0 w-full">
<div class="flex flex-row justify-between w-full gap-2 px-0 py-2">
<woot-button variant="link" @click.prevent="openOpenAIDoc">
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.BUTTONS.NEED_HELP') }}
</woot-button>
@@ -39,13 +39,20 @@
<script>
import { required } from 'vuelidate/lib/validators';
import { mapGetters } from 'vuex';
import aiMixin from 'dashboard/mixins/aiMixin';
import { useAlert } from 'dashboard/composables';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { useUISettings } from 'dashboard/composables/useUISettings';
import aiMixin from 'dashboard/mixins/aiMixin';
import { OPEN_AI_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
export default {
mixins: [aiMixin, uiSettingsMixin],
mixins: [aiMixin],
setup() {
const { updateUISettings } = useUISettings();
return {
updateUISettings,
};
},
data() {
return {
value: '',

View File

@@ -80,8 +80,7 @@ import {
hasPressedCommandAndEnter,
} from 'shared/helpers/KeyboardHelpers';
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
import { useUISettings } from 'dashboard/composables/useUISettings';
import {
replaceVariablesInMessage,
createTypingIndicator,
@@ -119,7 +118,7 @@ const createState = (
export default {
name: 'WootMessageEditor',
components: { TagAgents, CannedResponse, VariableList },
mixins: [keyboardEventListenerMixins, uiSettingsMixin],
mixins: [keyboardEventListenerMixins],
props: {
value: { type: String, default: '' },
editorId: { type: String, default: '' },
@@ -139,6 +138,19 @@ export default {
channelType: { type: String, default: '' },
showImageResizeToolbar: { type: Boolean, default: false }, // A kill switch to show or hide the image toolbar
},
setup() {
const {
uiSettings,
isEditorHotKeyEnabled,
fetchSignatureFlagFromUISettings,
} = useUISettings();
return {
uiSettings,
isEditorHotKeyEnabled,
fetchSignatureFlagFromUISettings,
};
},
data() {
return {
typingIndicator: createTypingIndicator(
@@ -278,7 +290,7 @@ export default {
// this is considered the source of truth, we watch this property
// on change, we toggle the signature in the editor
if (this.allowSignature && !this.isPrivate && this.channelType) {
return this.fetchSignatureFlagFromUiSettings(this.channelType);
return this.fetchSignatureFlagFromUISettings(this.channelType);
}
return false;
@@ -521,10 +533,10 @@ export default {
}
},
isEnterToSendEnabled() {
return isEditorHotKeyEnabled(this.uiSettings, 'enter');
return this.isEditorHotKeyEnabled('enter');
},
isCmdPlusEnterToSendEnabled() {
return isEditorHotKeyEnabled(this.uiSettings, 'cmd_enter');
return this.isEditorHotKeyEnabled('cmd_enter');
},
getKeyboardEvents() {
return {

View File

@@ -25,8 +25,8 @@ import {
} from '@chatwoot/prosemirror-schema';
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
import { useAlert } from 'dashboard/composables';
import { useUISettings } from 'dashboard/composables/useUISettings';
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
const MAXIMUM_FILE_UPLOAD_SIZE = 4; // in MB
const createState = (
@@ -51,13 +51,21 @@ const createState = (
};
export default {
mixins: [keyboardEventListenerMixins, uiSettingsMixin],
mixins: [keyboardEventListenerMixins],
props: {
value: { type: String, default: '' },
editorId: { type: String, default: '' },
placeholder: { type: String, default: '' },
enabledMenuOptions: { type: Array, default: () => [] },
},
setup() {
const { uiSettings, updateUISettings } = useUISettings();
return {
uiSettings,
updateUISettings,
};
},
data() {
return {
editorView: null,

View File

@@ -134,10 +134,10 @@
</template>
<script>
import { useUISettings } from 'dashboard/composables/useUISettings';
import FileUpload from 'vue-upload-component';
import * as ActiveStorage from 'activestorage';
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import inboxMixin from 'shared/mixins/inboxMixin';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
import {
@@ -153,7 +153,7 @@ import { mapGetters } from 'vuex';
export default {
name: 'ReplyBottomPanel',
components: { FileUpload, VideoCallButton, AIAssistanceButton },
mixins: [keyboardEventListenerMixins, uiSettingsMixin, inboxMixin],
mixins: [keyboardEventListenerMixins, inboxMixin],
props: {
mode: {
type: String,
@@ -248,6 +248,15 @@ export default {
required: true,
},
},
setup() {
const { setSignatureFlagForInbox, fetchSignatureFlagFromUISettings } =
useUISettings();
return {
setSignatureFlagForInbox,
fetchSignatureFlagFromUISettings,
};
},
computed: {
...mapGetters({
accountId: 'getCurrentAccountId',
@@ -320,7 +329,7 @@ export default {
},
sendWithSignature() {
// channelType is sourced from inboxMixin
return this.fetchSignatureFlagFromUiSettings(this.channelType);
return this.fetchSignatureFlagFromUISettings(this.channelType);
},
signatureToggleTooltip() {
return this.sendWithSignature

View File

@@ -12,10 +12,10 @@
<div
v-if="showActionsDropdown"
v-on-clickaway="closeDropdown"
class="dropdown-pane dropdown-pane--open mt-1 right-0 basic-filter"
class="right-0 mt-1 dropdown-pane dropdown-pane--open basic-filter"
>
<div class="items-center flex justify-between last:mt-4">
<span class="text-slate-800 dark:text-slate-100 text-xs font-medium">{{
<div class="flex items-center justify-between last:mt-4">
<span class="text-xs font-medium text-slate-800 dark:text-slate-100">{{
$t('CHAT_LIST.CHAT_SORT.STATUS')
}}</span>
<filter-item
@@ -26,8 +26,8 @@
@onChangeFilter="onChangeFilter"
/>
</div>
<div class="items-center flex justify-between last:mt-4">
<span class="text-slate-800 dark:text-slate-100 text-xs font-medium">{{
<div class="flex items-center justify-between last:mt-4">
<span class="text-xs font-medium text-slate-800 dark:text-slate-100">{{
$t('CHAT_LIST.CHAT_SORT.ORDER_BY')
}}</span>
<filter-item
@@ -46,13 +46,19 @@
import wootConstants from 'dashboard/constants/globals';
import { mapGetters } from 'vuex';
import FilterItem from './FilterItem.vue';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { useUISettings } from 'dashboard/composables/useUISettings';
export default {
components: {
FilterItem,
},
mixins: [uiSettingsMixin],
setup() {
const { updateUISettings } = useUISettings();
return {
updateUISettings,
};
},
data() {
return {
showActionsDropdown: false,

View File

@@ -154,6 +154,7 @@
<script>
import { mapGetters } from 'vuex';
import { useAlert } from 'dashboard/composables';
import { useUISettings } from 'dashboard/composables/useUISettings';
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
import CannedResponse from './CannedResponse.vue';
@@ -180,10 +181,8 @@ import {
import WhatsappTemplates from './WhatsappTemplates/Modal.vue';
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { trimContent, debounce } from '@chatwoot/utils';
import wootConstants from 'dashboard/constants/globals';
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
import rtlMixin from 'shared/mixins/rtlMixin';
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
@@ -218,7 +217,6 @@ export default {
},
mixins: [
inboxMixin,
uiSettingsMixin,
messageFormatterMixin,
rtlMixin,
fileUploadMixin,
@@ -230,6 +228,21 @@ export default {
default: false,
},
},
setup() {
const {
uiSettings,
updateUISettings,
isEditorHotKeyEnabled,
fetchSignatureFlagFromUISettings,
} = useUISettings();
return {
uiSettings,
updateUISettings,
isEditorHotKeyEnabled,
fetchSignatureFlagFromUISettings,
};
},
data() {
return {
message: '',
@@ -405,7 +418,7 @@ export default {
if (this.isPrivate) {
sendMessageText = this.$t('CONVERSATION.REPLYBOX.CREATE');
}
const keyLabel = isEditorHotKeyEnabled(this.uiSettings, 'cmd_enter')
const keyLabel = this.isEditorHotKeyEnabled('cmd_enter')
? '(⌘ + ↵)'
: '(↵)';
return `${sendMessageText} ${keyLabel}`;
@@ -478,7 +491,7 @@ export default {
return !!this.signatureToApply;
},
sendWithSignature() {
return this.fetchSignatureFlagFromUiSettings(this.channelType);
return this.fetchSignatureFlagFromUISettings(this.channelType);
},
editorMessageKey() {
const { editor_message_key: isEnabled } = this.uiSettings;
@@ -747,7 +760,7 @@ export default {
!this.showCannedMenu &&
!this.showVariablesMenu &&
this.isFocused &&
isEditorHotKeyEnabled(this.uiSettings, selectedKey)
this.isEditorHotKeyEnabled(selectedKey)
);
},
onPaste(e) {

View File

@@ -0,0 +1,138 @@
import { ref } from 'vue';
import {
useUISettings,
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
} from 'dashboard/composables/useUISettings';
// Mocking the store composables
const mockDispatch = vi.fn();
const getUISettingsMock = ref({
is_ct_labels_open: true,
conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
editor_message_key: 'enter',
});
vi.mock('dashboard/composables/store', () => ({
useStoreGetters: () => ({
getUISettings: getUISettingsMock,
}),
useStore: () => ({
dispatch: mockDispatch,
}),
}));
describe('useUISettings', () => {
beforeEach(() => {
mockDispatch.mockClear();
});
it('returns uiSettings', () => {
const { uiSettings } = useUISettings();
expect(uiSettings.value).toEqual({
is_ct_labels_open: true,
conversation_sidebar_items_order:
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
editor_message_key: 'enter',
});
});
it('updates UI settings correctly', () => {
const { updateUISettings } = useUISettings();
updateUISettings({ enter_to_send_enabled: true });
expect(mockDispatch).toHaveBeenCalledWith('updateUISettings', {
uiSettings: {
enter_to_send_enabled: true,
is_ct_labels_open: true,
conversation_sidebar_items_order:
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
editor_message_key: 'enter',
},
});
});
it('toggles sidebar UI state correctly', () => {
const { toggleSidebarUIState } = useUISettings();
toggleSidebarUIState('is_ct_labels_open');
expect(mockDispatch).toHaveBeenCalledWith('updateUISettings', {
uiSettings: {
is_ct_labels_open: false,
conversation_sidebar_items_order:
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
editor_message_key: 'enter',
},
});
});
it('returns correct conversation sidebar items order', () => {
const { conversationSidebarItemsOrder } = useUISettings();
expect(conversationSidebarItemsOrder.value).toEqual(
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER
);
});
it('returns correct contact sidebar items order', () => {
const { contactSidebarItemsOrder } = useUISettings();
expect(contactSidebarItemsOrder.value).toEqual(
DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER
);
});
it('returns correct value for isContactSidebarItemOpen', () => {
const { isContactSidebarItemOpen } = useUISettings();
expect(isContactSidebarItemOpen('is_ct_labels_open')).toBe(true);
expect(isContactSidebarItemOpen('non_existent_key')).toBe(false);
});
it('sets signature flag for inbox correctly', () => {
const { setSignatureFlagForInbox } = useUISettings();
setSignatureFlagForInbox('email', true);
expect(mockDispatch).toHaveBeenCalledWith('updateUISettings', {
uiSettings: {
is_ct_labels_open: true,
conversation_sidebar_items_order:
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
email_signature_enabled: true,
editor_message_key: 'enter',
},
});
});
it('fetches signature flag from UI settings correctly', () => {
const { fetchSignatureFlagFromUISettings } = useUISettings();
expect(fetchSignatureFlagFromUISettings('email')).toBe(undefined);
});
it('returns correct value for isEditorHotKeyEnabled when editor_message_key is configured', () => {
getUISettingsMock.value.enter_to_send_enabled = false;
const { isEditorHotKeyEnabled } = useUISettings();
expect(isEditorHotKeyEnabled('enter')).toBe(true);
expect(isEditorHotKeyEnabled('cmd_enter')).toBe(false);
});
it('returns correct value for isEditorHotKeyEnabled when editor_message_key is not configured', () => {
getUISettingsMock.value.editor_message_key = undefined;
const { isEditorHotKeyEnabled } = useUISettings();
expect(isEditorHotKeyEnabled('enter')).toBe(false);
expect(isEditorHotKeyEnabled('cmd_enter')).toBe(true);
});
it('handles non-existent keys', () => {
const {
isContactSidebarItemOpen,
fetchSignatureFlagFromUISettings,
isEditorHotKeyEnabled,
} = useUISettings();
expect(isContactSidebarItemOpen('non_existent_key')).toBe(false);
expect(fetchSignatureFlagFromUISettings('non_existent_key')).toBe(
undefined
);
expect(isEditorHotKeyEnabled('non_existent_key')).toBe(false);
});
});

View File

@@ -0,0 +1,149 @@
import { computed } from 'vue';
import { useStore, useStoreGetters } from 'dashboard/composables/store';
export const DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER = Object.freeze([
{ name: 'conversation_actions' },
{ name: 'macros' },
{ name: 'conversation_info' },
{ name: 'contact_attributes' },
{ name: 'previous_conversation' },
{ name: 'conversation_participants' },
]);
export const DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER = Object.freeze([
{ name: 'contact_attributes' },
{ name: 'contact_labels' },
{ name: 'previous_conversation' },
]);
/**
* Slugifies the channel name.
* Replaces spaces, hyphens, and double colons with underscores.
* @param {string} name - The channel name to slugify.
* @returns {string} The slugified channel name.
*/
const slugifyChannel = name =>
name?.toLowerCase().replace(' ', '_').replace('-', '_').replace('::', '_');
/**
* Computes the order of items in the conversation sidebar, using defaults if not present.
* @param {Object} uiSettings - Reactive UI settings object.
* @returns {Array} Ordered list of sidebar items.
*/
const useConversationSidebarItemsOrder = uiSettings => {
return computed(() => {
const { conversation_sidebar_items_order: itemsOrder } = uiSettings.value;
// If the sidebar order is not set, use the default order.
if (!itemsOrder) {
return DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER;
}
// Create a copy of itemsOrder to avoid mutating the original store object.
const itemsOrderCopy = [...itemsOrder];
// If the sidebar order doesn't have the new elements, then add them to the list.
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER.forEach(item => {
if (!itemsOrderCopy.find(i => i.name === item.name)) {
itemsOrderCopy.push(item);
}
});
return itemsOrderCopy;
});
};
/**
* Computes the order of items in the contact sidebar,using defaults if not present.
* @param {Object} uiSettings - Reactive UI settings object.
* @returns {Array} Ordered list of sidebar items.
*/
const useContactSidebarItemsOrder = uiSettings => {
return computed(() => {
const { contact_sidebar_items_order: itemsOrder } = uiSettings.value;
return itemsOrder || DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER;
});
};
/**
* Toggles the open state of a sidebar item.
* @param {string} key - The key of the sidebar item to toggle.
* @param {Object} uiSettings - Reactive UI settings object.
* @param {Function} updateUISettings - Function to update UI settings.
*/
const toggleSidebarUIState = (key, uiSettings, updateUISettings) => {
updateUISettings({ [key]: !uiSettings.value[key] });
};
/**
* Sets the signature flag for a specific channel type in the inbox settings.
* @param {string} channelType - The type of the channel.
* @param {boolean} value - The value to set for the signature enabled flag.
* @param {Function} updateUISettings - Function to update UI settings.
*/
const setSignatureFlagForInbox = (channelType, value, updateUISettings) => {
if (!channelType) return;
const slugifiedChannel = slugifyChannel(channelType);
updateUISettings({ [`${slugifiedChannel}_signature_enabled`]: value });
};
/**
* Fetches the signature flag for a specific channel type from UI settings.
* @param {string} channelType - The type of the channel.
* @param {Object} uiSettings - Reactive UI settings object.
* @returns {boolean} The value of the signature enabled flag.
*/
const fetchSignatureFlagFromUISettings = (channelType, uiSettings) => {
if (!channelType) return false;
const slugifiedChannel = slugifyChannel(channelType);
return uiSettings.value[`${slugifiedChannel}_signature_enabled`];
};
/**
* Checks if a specific editor hotkey is enabled.
* @param {string} key - The key to check.
* @param {Object} uiSettings - Reactive UI settings object.
* @returns {boolean} True if the hotkey is enabled, otherwise false.
*/
const isEditorHotKeyEnabled = (key, uiSettings) => {
const {
editor_message_key: editorMessageKey,
enter_to_send_enabled: enterToSendEnabled,
} = uiSettings.value || {};
if (!editorMessageKey) {
return key === (enterToSendEnabled ? 'enter' : 'cmd_enter');
}
return editorMessageKey === key;
};
/**
* Main composable function for managing UI settings.
* @returns {Object} An object containing reactive properties and methods for UI settings management.
*/
export function useUISettings() {
const getters = useStoreGetters();
const store = useStore();
const uiSettings = computed(() => getters.getUISettings.value);
const updateUISettings = (settings = {}) => {
store.dispatch('updateUISettings', {
uiSettings: {
...uiSettings.value,
...settings,
},
});
};
return {
uiSettings,
updateUISettings,
conversationSidebarItemsOrder: useConversationSidebarItemsOrder(uiSettings),
contactSidebarItemsOrder: useContactSidebarItemsOrder(uiSettings),
isContactSidebarItemOpen: key => !!uiSettings.value[key],
toggleSidebarUIState: key =>
toggleSidebarUIState(key, uiSettings, updateUISettings),
setSignatureFlagForInbox: (channelType, value) =>
setSignatureFlagForInbox(channelType, value, updateUISettings),
fetchSignatureFlagFromUISettings: channelType =>
fetchSignatureFlagFromUISettings(channelType, uiSettings),
isEditorHotKeyEnabled: key => isEditorHotKeyEnabled(key, uiSettings),
};
}

View File

@@ -1,169 +0,0 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import uiSettingsMixin, {
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
isEditorHotKeyEnabled,
} from '../uiSettings';
import Vuex from 'vuex';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('uiSettingsMixin', () => {
let getters;
let actions;
let store;
beforeEach(() => {
actions = { updateUISettings: vi.fn(), toggleSidebarUIState: vi.fn() };
getters = {
getUISettings: () => ({
enter_to_send_enabled: false,
is_ct_labels_open: true,
conversation_sidebar_items_order:
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
}),
};
store = new Vuex.Store({ actions, getters });
});
it('returns uiSettings', () => {
const Component = {
render() {},
title: 'TestComponent',
mixins: [uiSettingsMixin],
};
const wrapper = shallowMount(Component, { store, localVue });
expect(wrapper.vm.uiSettings).toEqual({
enter_to_send_enabled: false,
is_ct_labels_open: true,
conversation_sidebar_items_order:
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
});
});
describe('#updateUISettings', () => {
it('dispatches store actions correctly', () => {
const Component = {
render() {},
title: 'TestComponent',
mixins: [uiSettingsMixin],
};
const wrapper = shallowMount(Component, { store, localVue });
wrapper.vm.updateUISettings({ enter_to_send_enabled: true });
expect(actions.updateUISettings).toHaveBeenCalledWith(
expect.anything(),
{
uiSettings: {
enter_to_send_enabled: true,
is_ct_labels_open: true,
conversation_sidebar_items_order:
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
},
},
undefined
);
});
});
describe('#toggleSidebarUIState', () => {
it('dispatches store actions correctly', () => {
const Component = {
render() {},
title: 'TestComponent',
mixins: [uiSettingsMixin],
};
const wrapper = shallowMount(Component, { store, localVue });
wrapper.vm.toggleSidebarUIState('is_ct_labels_open');
expect(actions.updateUISettings).toHaveBeenCalledWith(
expect.anything(),
{
uiSettings: {
enter_to_send_enabled: false,
is_ct_labels_open: false,
conversation_sidebar_items_order:
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
},
},
undefined
);
});
});
describe('#isContactSidebarItemOpen', () => {
it('returns correct values', () => {
const Component = {
render() {},
title: 'TestComponent',
mixins: [uiSettingsMixin],
};
const wrapper = shallowMount(Component, { store, localVue });
expect(wrapper.vm.isContactSidebarItemOpen('is_ct_labels_open')).toEqual(
true
);
expect(
wrapper.vm.isContactSidebarItemOpen('is_ct_prev_conv_open')
).toEqual(false);
});
});
describe('#conversationSidebarItemsOrder', () => {
it('returns correct values', () => {
const Component = {
render() {},
title: 'TestComponent',
mixins: [uiSettingsMixin],
};
const wrapper = shallowMount(Component, { store, localVue });
expect(wrapper.vm.conversationSidebarItemsOrder).toEqual([
{ name: 'conversation_actions' },
{ name: 'macros' },
{ name: 'conversation_info' },
{ name: 'contact_attributes' },
{ name: 'previous_conversation' },
{ name: 'conversation_participants' },
]);
});
});
describe('#contactSidebarItemsOrder', () => {
it('returns correct values', () => {
const Component = {
render() {},
title: 'TestComponent',
mixins: [uiSettingsMixin],
};
const wrapper = shallowMount(Component, { store, localVue });
expect(wrapper.vm.contactSidebarItemsOrder).toEqual([
{ name: 'contact_attributes' },
{ name: 'contact_labels' },
{ name: 'previous_conversation' },
]);
});
});
});
describe('isEditorHotKeyEnabled', () => {
it('returns true if hot key is not configured and enter to send flag is true', () => {
expect(
isEditorHotKeyEnabled({ enter_to_send_enabled: true }, 'enter')
).toEqual(true);
expect(
isEditorHotKeyEnabled({ enter_to_send_enabled: true }, 'cmd_enter')
).toEqual(false);
expect(isEditorHotKeyEnabled({}, 'cmd_enter')).toEqual(true);
expect(isEditorHotKeyEnabled({}, 'enter')).toEqual(false);
});
it('returns correct value if hot key is configured', () => {
expect(
isEditorHotKeyEnabled({ editor_message_key: 'enter' }, 'enter')
).toEqual(true);
expect(
isEditorHotKeyEnabled({ editor_message_key: 'cmd_enter' }, 'enter')
).toEqual(false);
});
});

View File

@@ -1,87 +0,0 @@
import { mapGetters } from 'vuex';
export const DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER = [
{ name: 'conversation_actions' },
{ name: 'macros' },
{ name: 'conversation_info' },
{ name: 'contact_attributes' },
{ name: 'previous_conversation' },
{ name: 'conversation_participants' },
];
export const DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER = [
{ name: 'contact_attributes' },
{ name: 'contact_labels' },
{ name: 'previous_conversation' },
];
const slugifyChannel = name =>
name?.toLowerCase().replace(' ', '_').replace('-', '_').replace('::', '_');
export const isEditorHotKeyEnabled = (uiSettings, key) => {
const {
editor_message_key: editorMessageKey,
enter_to_send_enabled: enterToSendEnabled,
} = uiSettings || {};
if (!editorMessageKey) {
if (enterToSendEnabled) {
return key === 'enter';
}
return key === 'cmd_enter';
}
return editorMessageKey === key;
};
export default {
computed: {
...mapGetters({ uiSettings: 'getUISettings' }),
conversationSidebarItemsOrder() {
const { conversation_sidebar_items_order: itemsOrder } = this.uiSettings;
// If the sidebar order is not set, use the default order.
if (!itemsOrder) {
return DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER;
}
// If the sidebar order doesn't have the new elements, then add them to the list.
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER.forEach(item => {
if (!itemsOrder.find(i => i.name === item.name)) {
itemsOrder.push(item);
}
});
return itemsOrder;
},
contactSidebarItemsOrder() {
const { contact_sidebar_items_order: itemsOrder } = this.uiSettings;
return itemsOrder || DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER;
},
},
methods: {
updateUISettings(uiSettings = {}) {
this.$store.dispatch('updateUISettings', {
uiSettings: {
...this.uiSettings,
...uiSettings,
},
});
},
isContactSidebarItemOpen(key) {
const { [key]: isOpen } = this.uiSettings;
return !!isOpen;
},
toggleSidebarUIState(key) {
this.updateUISettings({ [key]: !this.isContactSidebarItemOpen(key) });
},
setSignatureFlagForInbox(channelType, value) {
if (!channelType) return;
channelType = slugifyChannel(channelType);
this.updateUISettings({
[`${channelType}_signature_enabled`]: value,
});
},
fetchSignatureFlagFromUiSettings(channelType) {
if (!channelType) return false;
channelType = slugifyChannel(channelType);
return this.uiSettings[`${channelType}_signature_enabled`];
},
},
};

View File

@@ -1,6 +1,6 @@
<template>
<div
class="app-wrapper h-full flex-grow-0 min-h-0 w-full max-w-full ml-auto mr-auto flex flex-wrap dark:text-slate-300"
class="flex flex-wrap flex-grow-0 w-full h-full max-w-full min-h-0 ml-auto mr-auto app-wrapper dark:text-slate-300"
>
<sidebar
:route="currentRoute"
@@ -11,7 +11,7 @@
@close-key-shortcut-modal="closeKeyShortcutModal"
@show-add-label-popup="showAddLabelPopup"
/>
<section class="flex h-full min-h-0 overflow-hidden flex-1 px-0">
<section class="flex flex-1 h-full min-h-0 px-0 overflow-hidden">
<router-view />
<command-bar />
<account-selector
@@ -47,7 +47,7 @@ import AddAccountModal from 'dashboard/components/layout/sidebarComponents/AddAc
import AccountSelector from 'dashboard/components/layout/sidebarComponents/AccountSelector.vue';
import AddLabelModal from 'dashboard/routes/dashboard/settings/labels/AddLabel.vue';
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel.vue';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { useUISettings } from 'dashboard/composables/useUISettings';
import wootConstants from 'dashboard/constants/globals';
const CommandBar = () => import('./commands/commandbar.vue');
@@ -61,7 +61,14 @@ export default {
AddLabelModal,
NotificationPanel,
},
mixins: [uiSettingsMixin],
setup() {
const { uiSettings, updateUISettings } = useUISettings();
return {
uiSettings,
updateUISettings,
};
},
data() {
return {
showAccountModal: false,

View File

@@ -86,7 +86,7 @@ import ContactInfo from 'dashboard/routes/dashboard/conversation/contact/Contact
import ContactLabel from 'dashboard/routes/dashboard/contacts/components/ContactLabels.vue';
import CustomAttributes from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributes.vue';
import draggable from 'vuedraggable';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { useUISettings } from 'dashboard/composables/useUISettings';
export default {
components: {
@@ -97,7 +97,6 @@ export default {
CustomAttributes,
draggable,
},
mixins: [uiSettingsMixin],
props: {
contact: {
type: Object,
@@ -116,6 +115,21 @@ export default {
default: true,
},
},
setup() {
const {
updateUISettings,
isContactSidebarItemOpen,
contactSidebarItemsOrder,
toggleSidebarUIState,
} = useUISettings();
return {
updateUISettings,
isContactSidebarItemOpen,
contactSidebarItemsOrder,
toggleSidebarUIState,
};
},
data() {
return {
dragEnabled: true,

View File

@@ -133,6 +133,7 @@
<script>
import { mapGetters } from 'vuex';
import { useUISettings } from 'dashboard/composables/useUISettings';
import AccordionItem from 'dashboard/components/Accordion/AccordionItem.vue';
import ContactConversations from './ContactConversations.vue';
import ConversationAction from './ConversationAction.vue';
@@ -142,7 +143,6 @@ import ContactInfo from './contact/ContactInfo.vue';
import ConversationInfo from './ConversationInfo.vue';
import CustomAttributes from './customAttributes/CustomAttributes.vue';
import draggable from 'vuedraggable';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import MacrosList from './Macros/List.vue';
export default {
components: {
@@ -156,7 +156,6 @@ export default {
draggable,
MacrosList,
},
mixins: [uiSettingsMixin],
props: {
conversationId: {
type: [Number, String],
@@ -171,6 +170,21 @@ export default {
default: () => {},
},
},
setup() {
const {
updateUISettings,
isContactSidebarItemOpen,
conversationSidebarItemsOrder,
toggleSidebarUIState,
} = useUISettings();
return {
updateUISettings,
isContactSidebarItemOpen,
conversationSidebarItemsOrder,
toggleSidebarUIState,
};
},
data() {
return {
dragEnabled: true,

View File

@@ -37,12 +37,12 @@
<script>
import { mapGetters } from 'vuex';
import { useAlert } from 'dashboard/composables';
import { useUISettings } from 'dashboard/composables/useUISettings';
import { getUnixTime } from 'date-fns';
import ChatList from '../../../components/ChatList.vue';
import ConversationBox from '../../../components/widgets/conversation/ConversationBox.vue';
import PopOverSearch from './search/PopOverSearch.vue';
import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import wootConstants from 'dashboard/constants/globals';
import { BUS_EVENTS } from 'shared/constants/busEvents';
import { CMD_SNOOZE_CONVERSATION } from 'dashboard/routes/dashboard/commands/commandBarBusEvents';
@@ -55,7 +55,6 @@ export default {
PopOverSearch,
CustomSnoozeModal,
},
mixins: [uiSettingsMixin],
props: {
inboxId: {
type: [String, Number],
@@ -82,6 +81,14 @@ export default {
default: 0,
},
},
setup() {
const { uiSettings, updateUISettings } = useUISettings();
return {
uiSettings,
updateUISettings,
};
},
data() {
return {
showSearchModal: false,

View File

@@ -240,6 +240,7 @@
<script>
import { mapGetters } from 'vuex';
import { useAlert } from 'dashboard/composables';
import { useUISettings } from 'dashboard/composables/useUISettings';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
import ReplyEmailHead from 'dashboard/components/widgets/conversation/ReplyEmailHead.vue';
@@ -260,7 +261,6 @@ import {
appendSignature,
removeSignature,
} from 'dashboard/helper/editorHelper';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
export default {
components: {
@@ -274,7 +274,7 @@ export default {
AttachmentPreview,
MessageSignatureMissingAlert,
},
mixins: [uiSettingsMixin, inboxMixin, fileUploadMixin],
mixins: [inboxMixin, fileUploadMixin],
props: {
contact: {
type: Object,
@@ -289,6 +289,15 @@ export default {
default: '',
},
},
setup() {
const { fetchSignatureFlagFromUISettings, setSignatureFlagForInbox } =
useUISettings();
return {
fetchSignatureFlagFromUISettings,
setSignatureFlagForInbox,
};
},
data() {
return {
name: '',
@@ -323,7 +332,7 @@ export default {
messageSignature: 'getMessageSignature',
}),
sendWithSignature() {
return this.fetchSignatureFlagFromUiSettings(this.channelType);
return this.fetchSignatureFlagFromUISettings(this.channelType);
},
signatureToApply() {
return this.messageSignature;

View File

@@ -41,17 +41,17 @@
</template>
<script>
import CustomAttribute from 'dashboard/components/CustomAttribute.vue';
import { useAlert } from 'dashboard/composables';
import attributeMixin from 'dashboard/mixins/attributeMixin';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { useUISettings } from 'dashboard/composables/useUISettings';
import { copyTextToClipboard } from 'shared/helpers/clipboard';
import CustomAttribute from 'dashboard/components/CustomAttribute.vue';
import attributeMixin from 'dashboard/mixins/attributeMixin';
export default {
components: {
CustomAttribute,
},
mixins: [attributeMixin, uiSettingsMixin],
mixins: [attributeMixin],
props: {
attributeType: {
type: String,
@@ -71,6 +71,14 @@ export default {
default: '',
},
},
setup() {
const { uiSettings, updateUISettings } = useUISettings();
return {
uiSettings,
updateUISettings,
};
},
data() {
return {
showAllAttributes: false,

View File

@@ -1,5 +1,5 @@
<template>
<div class="app-wrapper flex h-full flex-grow-0 min-h-0 w-full">
<div class="flex flex-grow-0 w-full h-full min-h-0 app-wrapper">
<sidebar
:route="currentRoute"
@toggle-account-modal="toggleAccountModal"
@@ -20,7 +20,7 @@
/>
<section
v-if="isHelpCenterEnabled"
class="flex h-full min-h-0 overflow-hidden flex-1 px-0 bg-white dark:bg-slate-900"
class="flex flex-1 h-full min-h-0 px-0 overflow-hidden bg-white dark:bg-slate-900"
>
<router-view @reload-locale="fetchPortalAndItsCategories" />
<command-bar />
@@ -68,7 +68,7 @@ import HelpCenterSidebar from '../components/Sidebar/Sidebar.vue';
import WootKeyShortcutModal from 'dashboard/components/widgets/modal/WootKeyShortcutModal.vue';
import AccountSelector from 'dashboard/components/layout/sidebarComponents/AccountSelector.vue';
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel.vue';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { useUISettings } from 'dashboard/composables/useUISettings';
import portalMixin from '../mixins/portalMixin';
import AddCategory from '../pages/categories/AddCategory.vue';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
@@ -87,7 +87,15 @@ export default {
UpgradePage,
WootKeyShortcutModal,
},
mixins: [portalMixin, uiSettingsMixin],
mixins: [portalMixin],
setup() {
const { uiSettings, updateUISettings } = useUISettings();
return {
uiSettings,
updateUISettings,
};
},
data() {
return {
isOnDesktop: true,

View File

@@ -191,9 +191,9 @@
<script>
import { useAlert } from 'dashboard/composables';
import { useUISettings } from 'dashboard/composables/useUISettings';
import thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
import LocaleItemTable from './PortalListItemTable.vue';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { PORTALS_EVENTS } from '../../../../helper/AnalyticsHelper/events';
export default {
@@ -201,7 +201,6 @@ export default {
thumbnail,
LocaleItemTable,
},
mixins: [uiSettingsMixin],
props: {
portal: {
type: Object,
@@ -213,6 +212,13 @@ export default {
values: ['archived', 'draft', 'published'],
},
},
setup() {
const { updateUISettings } = useUISettings();
return {
updateUISettings,
};
},
data() {
return {
showDeleteConfirmationPopup: false,

View File

@@ -1,17 +1,23 @@
<template>
<div
class="text-slate-600 dark:text-slate-200 flex items-center justify-center w-full"
class="flex items-center justify-center w-full text-slate-600 dark:text-slate-200"
>
Loading...
</div>
</template>
<script>
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { mapGetters } from 'vuex';
import { useUISettings } from 'dashboard/composables/useUISettings';
export default {
mixins: [uiSettingsMixin],
setup() {
const { uiSettings } = useUISettings();
return {
uiSettings,
};
},
computed: {
...mapGetters({ portals: 'portals/allPortals' }),
},

View File

@@ -47,13 +47,13 @@
<script>
import { mapGetters } from 'vuex';
import { useAlert } from 'dashboard/composables';
import { useUISettings } from 'dashboard/composables/useUISettings';
import wootConstants from 'dashboard/constants/globals';
import InboxCard from './components/InboxCard.vue';
import InboxListHeader from './components/InboxListHeader.vue';
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
import IntersectionObserver from 'dashboard/components/IntersectionObserver.vue';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
export default {
components: {
@@ -61,7 +61,13 @@ export default {
InboxListHeader,
IntersectionObserver,
},
mixins: [uiSettingsMixin],
setup() {
const { uiSettings } = useUISettings();
return {
uiSettings,
};
},
data() {
return {
infiniteLoaderOptions: {

View File

@@ -5,7 +5,7 @@
:empty-state-message="$t('INBOX.LIST.NO_MESSAGES_AVAILABLE')"
/>
</div>
<div v-else class="flex flex-col h-full w-full">
<div v-else class="flex flex-col w-full h-full">
<inbox-item-header
class="flex-1"
:total-length="totalNotificationCount"
@@ -18,7 +18,7 @@
v-if="isConversationLoading"
class="flex items-center h-[calc(100%-56px)] justify-center bg-slate-25 dark:bg-slate-800"
>
<span class="spinner my-4" />
<span class="my-4 spinner" />
</div>
<conversation-box
v-else
@@ -38,7 +38,7 @@ import { mapGetters } from 'vuex';
import InboxItemHeader from './components/InboxItemHeader.vue';
import ConversationBox from 'dashboard/components/widgets/conversation/ConversationBox.vue';
import InboxEmptyState from './InboxEmptyState.vue';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { useUISettings } from 'dashboard/composables/useUISettings';
import { BUS_EVENTS } from 'shared/constants/busEvents';
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
@@ -48,7 +48,14 @@ export default {
InboxEmptyState,
ConversationBox,
},
mixins: [uiSettingsMixin],
setup() {
const { uiSettings, updateUISettings } = useUISettings();
return {
uiSettings,
updateUISettings,
};
},
data() {
return {
isConversationLoading: false,

View File

@@ -2,7 +2,7 @@
<div
class="flex flex-col bg-white z-50 dark:bg-slate-900 w-[170px] border shadow-md border-slate-100 dark:border-slate-700/50 rounded-xl divide-y divide-slate-100 dark:divide-slate-700/50"
>
<div class="flex items-center justify-between h-11 p-3 rounded-t-lg">
<div class="flex items-center justify-between p-3 rounded-t-lg h-11">
<div class="flex gap-1.5">
<fluent-icon
icon="arrow-sort"
@@ -10,7 +10,7 @@
size="16"
class="text-slate-700 dark:text-slate-100"
/>
<span class="font-medium text-xs text-slate-800 dark:text-slate-100">
<span class="text-xs font-medium text-slate-800 dark:text-slate-100">
{{ $t('INBOX.DISPLAY_MENU.SORT') }}
</span>
</div>
@@ -64,7 +64,7 @@
</div>
<div>
<span
class="font-medium text-xs py-4 px-3 text-slate-400 dark:text-slate-400"
class="px-3 py-4 text-xs font-medium text-slate-400 dark:text-slate-400"
>
{{ $t('INBOX.DISPLAY_MENU.DISPLAY') }}
</span>
@@ -98,10 +98,17 @@
<script>
import wootConstants from 'dashboard/constants/globals';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { useUISettings } from 'dashboard/composables/useUISettings';
export default {
mixins: [uiSettingsMixin],
setup() {
const { uiSettings, updateUISettings } = useUISettings();
return {
uiSettings,
updateUISettings,
};
},
data() {
return {
showSortMenu: false,

View File

@@ -87,7 +87,7 @@
</div>
<div
class="p-4 border-slate-25 dark:border-slate-700 text-black-900 dark:text-slate-300 flex flex-row"
class="flex flex-row p-4 border-slate-25 dark:border-slate-700 text-black-900 dark:text-slate-300"
>
<div
class="flex-grow-0 flex-shrink-0 flex-[25%] min-w-0 py-4 pr-6 pl-0"
@@ -103,7 +103,7 @@
<woot-code :script="getAccountId" />
</div>
</div>
<div class="text-sm text-center p-4">
<div class="p-4 text-sm text-center">
<div>{{ `v${globalConfig.appVersion}` }}</div>
<div v-if="hasAnUpdateAvailable && globalConfig.displayManifest">
{{
@@ -132,15 +132,22 @@
import { required, minValue, maxValue } from 'vuelidate/lib/validators';
import { mapGetters } from 'vuex';
import { useAlert } from 'dashboard/composables';
import { useUISettings } from 'dashboard/composables/useUISettings';
import configMixin from 'shared/mixins/configMixin';
import accountMixin from '../../../../mixins/account';
import { FEATURE_FLAGS } from '../../../../featureFlags';
import semver from 'semver';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
export default {
mixins: [accountMixin, configMixin, uiSettingsMixin],
mixins: [accountMixin, configMixin],
setup() {
const { updateUISettings } = useUISettings();
return {
updateUISettings,
};
},
data() {
return {
id: '',

View File

@@ -32,7 +32,7 @@
import { mapGetters } from 'vuex';
import { useAlert } from 'dashboard/composables';
import configMixin from 'shared/mixins/configMixin';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { useUISettings } from 'dashboard/composables/useUISettings';
import AudioAlertTone from './AudioAlertTone.vue';
import AudioAlertEvent from './AudioAlertEvent.vue';
import AudioAlertCondition from './AudioAlertCondition.vue';
@@ -43,7 +43,15 @@ export default {
AudioAlertTone,
AudioAlertCondition,
},
mixins: [configMixin, uiSettingsMixin],
mixins: [configMixin],
setup() {
const { uiSettings, updateUISettings } = useUISettings();
return {
uiSettings,
updateUISettings,
};
},
data() {
return {
audioAlert: '',
@@ -56,7 +64,6 @@ export default {
computed: {
...mapGetters({
accountId: 'getCurrentAccountId',
uiSettings: 'getUISettings',
}),
},
watch: {

View File

@@ -47,7 +47,7 @@
:description="hotKey.description"
:light-image="hotKey.lightImage"
:dark-image="hotKey.darkImage"
:active="isEditorHotKeyEnabled(uiSettings, hotKey.key)"
:active="isEditorHotKeyEnabled(hotKey.key)"
@click="toggleHotKey(hotKey.key)"
/>
</button>
@@ -81,15 +81,12 @@
</div>
</template>
<script>
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
import uiSettingsMixin, {
isEditorHotKeyEnabled,
} from 'dashboard/mixins/uiSettings';
import { useAlert } from 'dashboard/composables';
import { mapGetters } from 'vuex';
import { useAlert } from 'dashboard/composables';
import { useUISettings } from 'dashboard/composables/useUISettings';
import { clearCookiesOnLogout } from 'dashboard/store/utils/api.js';
import { copyTextToClipboard } from 'shared/helpers/clipboard';
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
import UserProfilePicture from './UserProfilePicture.vue';
import UserBasicDetails from './UserBasicDetails.vue';
import MessageSignature from './MessageSignature.vue';
@@ -112,7 +109,17 @@ export default {
AudioNotifications,
AccessToken,
},
mixins: [globalConfigMixin, uiSettingsMixin],
mixins: [globalConfigMixin],
setup() {
const { uiSettings, updateUISettings, isEditorHotKeyEnabled } =
useUISettings();
return {
uiSettings,
updateUISettings,
isEditorHotKeyEnabled,
};
},
data() {
return {
avatarFile: '',
@@ -168,7 +175,6 @@ export default {
this.displayName = this.currentUser.display_name;
this.messageSignature = this.currentUser.message_signature;
},
isEditorHotKeyEnabled,
async dispatchUpdate(payload, successMessage, errorMessage) {
let alertMessage = '';
try {

View File

@@ -134,7 +134,6 @@
import { mapGetters } from 'vuex';
import { useAlert } from 'dashboard/composables';
import configMixin from 'shared/mixins/configMixin';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import TableHeaderCell from 'dashboard/components/widgets/TableHeaderCell.vue';
import CheckBox from 'v3/components/Form/CheckBox.vue';
import {
@@ -152,7 +151,7 @@ export default {
FormSwitch,
CheckBox,
},
mixins: [configMixin, uiSettingsMixin],
mixins: [configMixin],
data() {
return {
selectedEmailFlags: [],
@@ -167,7 +166,6 @@ export default {
accountId: 'getCurrentAccountId',
emailFlags: 'userNotificationSettings/getSelectedEmailFlags',
pushFlags: 'userNotificationSettings/getSelectedPushFlags',
uiSettings: 'getUISettings',
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
}),
hasPushAPISupport() {
@@ -242,31 +240,6 @@ export default {
useAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_ERROR'));
}
},
handleAudioInput(e) {
this.enableAudioAlerts = e.target.value;
this.updateUISettings({
enable_audio_alerts: this.enableAudioAlerts,
});
useAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_SUCCESS'));
},
handleAudioAlertConditions(e) {
let condition = e.target.value;
if (condition === 'tab_is_inactive') {
this.updateUISettings({
always_play_audio_alert: !e.target.checked,
});
} else if (condition === 'conversations_are_read') {
this.updateUISettings({
alert_if_unread_assigned_conversation_exist: e.target.checked,
});
}
useAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_SUCCESS'));
},
handleAudioToneChange(value) {
this.updateUISettings({ notification_tone: value });
useAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_SUCCESS'));
},
handleInput(type, id) {
if (type === 'email') {
this.handleEmailInput(id);

View File

@@ -42,7 +42,7 @@ export default {
type: Number,
default: 2,
},
// add this as a prop, so that we won't have to include uiSettingsMixin
// add this as a prop, so that we won't have to add useUISettings
sendWithSignature: {
type: Boolean,
default: false,

View File

@@ -1,8 +1,15 @@
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
import { useUISettings } from 'dashboard/composables/useUISettings';
export default {
mixins: [uiSettingsMixin],
setup() {
const { uiSettings, updateUISettings } = useUISettings();
return {
uiSettings,
updateUISettings,
};
},
computed: {
isRTLView() {
const { rtl_view: isRTLView } = this.uiSettings;

View File

@@ -1,16 +1,19 @@
import { shallowMount } from '@vue/test-utils';
import rtlMixin from 'shared/mixins/rtlMixin';
import { useUISettings } from 'dashboard/composables/useUISettings';
vi.mock('dashboard/composables/useUISettings');
describe('rtlMixin', () => {
const createComponent = rtl_view => {
useUISettings.mockReturnValue({
uiSettings: { rtl_view },
updateUISettings: vi.fn(),
});
return shallowMount({
render() {},
mixins: [rtlMixin],
computed: {
uiSettings() {
return { rtl_view };
},
},
});
};