From 160a6fc6cfe5205a05be1e8da84233246e895c94 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Sun, 10 Jan 2021 19:25:33 +0530 Subject: [PATCH] feat: Save UI state in the database (#1635) feat: Save UI state in the database --- app/controllers/api/v1/profiles_controller.rb | 3 ++- app/javascript/dashboard/api/auth.js | 6 +++++ .../conversation/ConversationView.vue | 27 ++++++++++--------- .../dashboard/store/modules/auth.js | 27 ++++++++++++++++++- .../store/modules/specs/auth/actions.spec.js | 26 ++++++++++++++++++ .../store/modules/specs/auth/getters.spec.js | 8 ++++++ .../modules/specs/auth/mutations.spec.js | 21 +++++++++++++++ .../dashboard/store/mutation-types.js | 1 + app/models/user.rb | 1 + app/views/api/v1/models/_user.json.jbuilder | 1 + ...20210109211805_add_ui_settings_to_users.rb | 9 +++++++ db/schema.rb | 3 ++- .../api/v1/profiles_controller_spec.rb | 11 ++++++++ 13 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 app/javascript/dashboard/store/modules/specs/auth/mutations.spec.js create mode 100644 db/migrate/20210109211805_add_ui_settings_to_users.rb diff --git a/app/controllers/api/v1/profiles_controller.rb b/app/controllers/api/v1/profiles_controller.rb index bda3e8c90..c59edfce0 100644 --- a/app/controllers/api/v1/profiles_controller.rb +++ b/app/controllers/api/v1/profiles_controller.rb @@ -23,7 +23,8 @@ class Api::V1::ProfilesController < Api::BaseController :password, :password_confirmation, :avatar, - :availability + :availability, + ui_settings: {} ) end end diff --git a/app/javascript/dashboard/api/auth.js b/app/javascript/dashboard/api/auth.js index 46379ab1e..5de3e8d9b 100644 --- a/app/javascript/dashboard/api/auth.js +++ b/app/javascript/dashboard/api/auth.js @@ -140,6 +140,12 @@ export default { return axios.put(endPoints('profileUpdate').url, formData); }, + updateUISettings({ uiSettings }) { + return axios.put(endPoints('profileUpdate').url, { + profile: { ui_settings: uiSettings }, + }); + }, + updateAvailability({ availability }) { return axios.put(endPoints('profileUpdate').url, { profile: { availability }, diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue b/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue index ec7d8b10e..31dd481a5 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue @@ -63,25 +63,23 @@ export default { data() { return { - panelToggleState: true, showSearchModal: false, }; }, computed: { ...mapGetters({ + uiSettings: 'getUISettings', chatList: 'getAllConversations', currentChat: 'getSelectedChat', }), - isContactPanelOpen: { - get() { - if (this.currentChat.id) { - return this.panelToggleState; - } - return false; - }, - set(val) { - this.panelToggleState = val; - }, + isContactPanelOpen() { + if (this.currentChat.id) { + const { + is_contact_sidebar_open: isContactSidebarOpen, + } = this.uiSettings; + return isContactSidebarOpen; + } + return false; }, }, @@ -132,7 +130,12 @@ export default { } }, onToggleContactPanel() { - this.isContactPanelOpen = !this.isContactPanelOpen; + this.$store.dispatch('updateUISettings', { + uiSettings: { + ...this.uiSettings, + is_contact_sidebar_open: !this.isContactPanelOpen, + }, + }); }, onSearch() { this.showSearchModal = true; diff --git a/app/javascript/dashboard/store/modules/auth.js b/app/javascript/dashboard/store/modules/auth.js index 27103a0c5..b27633582 100644 --- a/app/javascript/dashboard/store/modules/auth.js +++ b/app/javascript/dashboard/store/modules/auth.js @@ -34,6 +34,10 @@ export const getters = { return _state.currentUser.id; }, + getUISettings(_state) { + return _state.currentUser.ui_settings || {}; + }, + getCurrentUserAvailabilityStatus(_state) { return _state.currentUser.availability_status; }, @@ -95,6 +99,7 @@ export const actions = { logout({ commit }) { commit(types.default.CLEAR_USER); }, + updateProfile: async ({ commit }, params) => { try { const response = await authAPI.profileUpdate(params); @@ -105,6 +110,17 @@ export const actions = { } }, + updateUISettings: async ({ commit }, params) => { + try { + commit(types.default.SET_CURRENT_USER_UI_SETTINGS, params); + const response = await authAPI.updateUISettings(params); + setUser(response.data, getHeaderExpiry(response)); + commit(types.default.SET_CURRENT_USER); + } catch (error) { + // Ignore error + } + }, + updateAvailability: ({ commit, dispatch }, { availability }) => { authAPI.updateAvailability({ availability }).then(response => { const userData = response.data; @@ -130,7 +146,7 @@ export const actions = { }; // mutations -const mutations = { +export const mutations = { [types.default.SET_CURRENT_USER_AVAILABILITY](_state, status) { Vue.set(_state.currentUser, 'availability_status', status); }, @@ -145,6 +161,15 @@ const mutations = { Vue.set(_state, 'currentUser', currentUser); }, + [types.default.SET_CURRENT_USER_UI_SETTINGS](_state, { uiSettings }) { + Vue.set(_state, 'currentUser', { + ..._state.currentUser, + ui_settings: { + ..._state.currentUser.ui_settings, + ...uiSettings, + }, + }); + }, [types.default.SET_CURRENT_ACCOUNT_ID](_state, accountId) { Vue.set(_state, 'currentAccountId', Number(accountId)); }, diff --git a/app/javascript/dashboard/store/modules/specs/auth/actions.spec.js b/app/javascript/dashboard/store/modules/specs/auth/actions.spec.js index 21043898e..adbca0d94 100644 --- a/app/javascript/dashboard/store/modules/specs/auth/actions.spec.js +++ b/app/javascript/dashboard/store/modules/specs/auth/actions.spec.js @@ -70,6 +70,32 @@ describe('#actions', () => { }); }); + describe('#updateUISettings', () => { + it('sends correct actions if API is success', async () => { + axios.put.mockResolvedValue({ + data: { + id: 1, + name: 'John', + availability_status: 'offline', + ui_settings: { is_contact_sidebar_open: true }, + }, + headers: { expiry: 581842904 }, + }); + await actions.updateUISettings( + { commit, dispatch }, + { uiSettings: { is_contact_sidebar_open: false } } + ); + expect(setUser).toHaveBeenCalledTimes(1); + expect(commit.mock.calls).toEqual([ + [ + types.default.SET_CURRENT_USER_UI_SETTINGS, + { uiSettings: { is_contact_sidebar_open: false } }, + ], + [types.default.SET_CURRENT_USER], + ]); + }); + }); + describe('#setUser', () => { it('sends correct actions if user is logged in', async () => { Cookies.getJSON.mockImplementation(() => true); diff --git a/app/javascript/dashboard/store/modules/specs/auth/getters.spec.js b/app/javascript/dashboard/store/modules/specs/auth/getters.spec.js index 96d696085..c934d3824 100644 --- a/app/javascript/dashboard/store/modules/specs/auth/getters.spec.js +++ b/app/javascript/dashboard/store/modules/specs/auth/getters.spec.js @@ -25,4 +25,12 @@ describe('#getters', () => { }) ).toEqual('busy'); }); + + it('getUISettings', () => { + expect( + getters.getUISettings({ + currentUser: { ui_settings: { is_contact_sidebar_open: true } }, + }) + ).toEqual({ is_contact_sidebar_open: true }); + }); }); diff --git a/app/javascript/dashboard/store/modules/specs/auth/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/auth/mutations.spec.js new file mode 100644 index 000000000..98e7530fa --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/auth/mutations.spec.js @@ -0,0 +1,21 @@ +import types from '../../../mutation-types'; +import { mutations } from '../../auth'; + +describe('#mutations', () => { + describe('#SET_CURRENT_USER_UI_SETTINGS', () => { + it('set ui flags', () => { + const state = { + currentUser: { + ui_settings: { is_contact_sidebar_open: true, icon_type: 'emoji' }, + }, + }; + mutations[types.SET_CURRENT_USER_UI_SETTINGS](state, { + uiSettings: { is_contact_sidebar_open: false }, + }); + expect(state.currentUser.ui_settings).toEqual({ + is_contact_sidebar_open: false, + icon_type: 'emoji', + }); + }); + }); +}); diff --git a/app/javascript/dashboard/store/mutation-types.js b/app/javascript/dashboard/store/mutation-types.js index dd7fdd2b7..4dc8a32b7 100755 --- a/app/javascript/dashboard/store/mutation-types.js +++ b/app/javascript/dashboard/store/mutation-types.js @@ -4,6 +4,7 @@ export default { SET_CURRENT_USER: 'SET_CURRENT_USER', SET_CURRENT_ACCOUNT_ID: 'SET_CURRENT_ACCOUNT_ID', SET_CURRENT_USER_AVAILABILITY: 'SET_CURRENT_USER_AVAILABILITY', + SET_CURRENT_USER_UI_SETTINGS: 'SET_CURRENT_USER_UI_SETTINGS', // Chat List RECEIVE_CHAT_LIST: 'RECEIVE_CHAT_LIST', diff --git a/app/models/user.rb b/app/models/user.rb index 19b17731c..d6f3dd967 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -22,6 +22,7 @@ # reset_password_token :string # sign_in_count :integer default(0), not null # tokens :json +# ui_settings :jsonb # uid :string default(""), not null # unconfirmed_email :string # created_at :datetime not null diff --git a/app/views/api/v1/models/_user.json.jbuilder b/app/views/api/v1/models/_user.json.jbuilder index bbe1414ac..d103061c8 100644 --- a/app/views/api/v1/models/_user.json.jbuilder +++ b/app/views/api/v1/models/_user.json.jbuilder @@ -13,6 +13,7 @@ json.confirmed resource.confirmed? json.avatar_url resource.avatar_url json.access_token resource.access_token.token json.availability_status resource.availability_status +json.ui_settings resource.ui_settings json.accounts do json.array! resource.account_users do |account_user| json.id account_user.account_id diff --git a/db/migrate/20210109211805_add_ui_settings_to_users.rb b/db/migrate/20210109211805_add_ui_settings_to_users.rb new file mode 100644 index 000000000..f8b18a971 --- /dev/null +++ b/db/migrate/20210109211805_add_ui_settings_to_users.rb @@ -0,0 +1,9 @@ +class AddUiSettingsToUsers < ActiveRecord::Migration[6.0] + def up + add_column :users, :ui_settings, :jsonb, default: {} + end + + def down + remove_column :users, :ui_settings, :jsonb + end +end diff --git a/db/schema.rb b/db/schema.rb index 63aa46271..8c92821dc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_01_05_185632) do +ActiveRecord::Schema.define(version: 2021_01_09_211805) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" @@ -501,6 +501,7 @@ ActiveRecord::Schema.define(version: 2021_01_05_185632) do t.datetime "updated_at", null: false t.string "pubsub_token" t.integer "availability", default: 0 + t.jsonb "ui_settings", default: {} t.index ["email"], name: "index_users_on_email" t.index ["pubsub_token"], name: "index_users_on_pubsub_token", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true diff --git a/spec/controllers/api/v1/profiles_controller_spec.rb b/spec/controllers/api/v1/profiles_controller_spec.rb index b8e2b60f9..4282b02a7 100644 --- a/spec/controllers/api/v1/profiles_controller_spec.rb +++ b/spec/controllers/api/v1/profiles_controller_spec.rb @@ -87,6 +87,17 @@ RSpec.describe 'Profile API', type: :request do expect(response).to have_http_status(:success) expect(::OnlineStatusTracker.get_status(account.id, agent.id)).to eq('offline') end + + it 'updates the ui settings' do + put '/api/v1/profile', + params: { profile: { ui_settings: { is_contact_sidebar_open: false } } }, + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + expect(json_response['ui_settings']['is_contact_sidebar_open']).to eq(false) + end end end end