feat: Save UI state in the database (#1635)
feat: Save UI state in the database
This commit is contained in:
parent
37d8e1c9a0
commit
160a6fc6cf
13 changed files with 129 additions and 15 deletions
|
@ -23,7 +23,8 @@ class Api::V1::ProfilesController < Api::BaseController
|
||||||
:password,
|
:password,
|
||||||
:password_confirmation,
|
:password_confirmation,
|
||||||
:avatar,
|
:avatar,
|
||||||
:availability
|
:availability,
|
||||||
|
ui_settings: {}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -140,6 +140,12 @@ export default {
|
||||||
return axios.put(endPoints('profileUpdate').url, formData);
|
return axios.put(endPoints('profileUpdate').url, formData);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateUISettings({ uiSettings }) {
|
||||||
|
return axios.put(endPoints('profileUpdate').url, {
|
||||||
|
profile: { ui_settings: uiSettings },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
updateAvailability({ availability }) {
|
updateAvailability({ availability }) {
|
||||||
return axios.put(endPoints('profileUpdate').url, {
|
return axios.put(endPoints('profileUpdate').url, {
|
||||||
profile: { availability },
|
profile: { availability },
|
||||||
|
|
|
@ -63,25 +63,23 @@ export default {
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
panelToggleState: true,
|
|
||||||
showSearchModal: false,
|
showSearchModal: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
|
uiSettings: 'getUISettings',
|
||||||
chatList: 'getAllConversations',
|
chatList: 'getAllConversations',
|
||||||
currentChat: 'getSelectedChat',
|
currentChat: 'getSelectedChat',
|
||||||
}),
|
}),
|
||||||
isContactPanelOpen: {
|
isContactPanelOpen() {
|
||||||
get() {
|
if (this.currentChat.id) {
|
||||||
if (this.currentChat.id) {
|
const {
|
||||||
return this.panelToggleState;
|
is_contact_sidebar_open: isContactSidebarOpen,
|
||||||
}
|
} = this.uiSettings;
|
||||||
return false;
|
return isContactSidebarOpen;
|
||||||
},
|
}
|
||||||
set(val) {
|
return false;
|
||||||
this.panelToggleState = val;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -132,7 +130,12 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onToggleContactPanel() {
|
onToggleContactPanel() {
|
||||||
this.isContactPanelOpen = !this.isContactPanelOpen;
|
this.$store.dispatch('updateUISettings', {
|
||||||
|
uiSettings: {
|
||||||
|
...this.uiSettings,
|
||||||
|
is_contact_sidebar_open: !this.isContactPanelOpen,
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
onSearch() {
|
onSearch() {
|
||||||
this.showSearchModal = true;
|
this.showSearchModal = true;
|
||||||
|
|
|
@ -34,6 +34,10 @@ export const getters = {
|
||||||
return _state.currentUser.id;
|
return _state.currentUser.id;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getUISettings(_state) {
|
||||||
|
return _state.currentUser.ui_settings || {};
|
||||||
|
},
|
||||||
|
|
||||||
getCurrentUserAvailabilityStatus(_state) {
|
getCurrentUserAvailabilityStatus(_state) {
|
||||||
return _state.currentUser.availability_status;
|
return _state.currentUser.availability_status;
|
||||||
},
|
},
|
||||||
|
@ -95,6 +99,7 @@ export const actions = {
|
||||||
logout({ commit }) {
|
logout({ commit }) {
|
||||||
commit(types.default.CLEAR_USER);
|
commit(types.default.CLEAR_USER);
|
||||||
},
|
},
|
||||||
|
|
||||||
updateProfile: async ({ commit }, params) => {
|
updateProfile: async ({ commit }, params) => {
|
||||||
try {
|
try {
|
||||||
const response = await authAPI.profileUpdate(params);
|
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 }) => {
|
updateAvailability: ({ commit, dispatch }, { availability }) => {
|
||||||
authAPI.updateAvailability({ availability }).then(response => {
|
authAPI.updateAvailability({ availability }).then(response => {
|
||||||
const userData = response.data;
|
const userData = response.data;
|
||||||
|
@ -130,7 +146,7 @@ export const actions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// mutations
|
// mutations
|
||||||
const mutations = {
|
export const mutations = {
|
||||||
[types.default.SET_CURRENT_USER_AVAILABILITY](_state, status) {
|
[types.default.SET_CURRENT_USER_AVAILABILITY](_state, status) {
|
||||||
Vue.set(_state.currentUser, 'availability_status', status);
|
Vue.set(_state.currentUser, 'availability_status', status);
|
||||||
},
|
},
|
||||||
|
@ -145,6 +161,15 @@ const mutations = {
|
||||||
|
|
||||||
Vue.set(_state, 'currentUser', currentUser);
|
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) {
|
[types.default.SET_CURRENT_ACCOUNT_ID](_state, accountId) {
|
||||||
Vue.set(_state, 'currentAccountId', Number(accountId));
|
Vue.set(_state, 'currentAccountId', Number(accountId));
|
||||||
},
|
},
|
||||||
|
|
|
@ -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', () => {
|
describe('#setUser', () => {
|
||||||
it('sends correct actions if user is logged in', async () => {
|
it('sends correct actions if user is logged in', async () => {
|
||||||
Cookies.getJSON.mockImplementation(() => true);
|
Cookies.getJSON.mockImplementation(() => true);
|
||||||
|
|
|
@ -25,4 +25,12 @@ describe('#getters', () => {
|
||||||
})
|
})
|
||||||
).toEqual('busy');
|
).toEqual('busy');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getUISettings', () => {
|
||||||
|
expect(
|
||||||
|
getters.getUISettings({
|
||||||
|
currentUser: { ui_settings: { is_contact_sidebar_open: true } },
|
||||||
|
})
|
||||||
|
).toEqual({ is_contact_sidebar_open: true });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -4,6 +4,7 @@ export default {
|
||||||
SET_CURRENT_USER: 'SET_CURRENT_USER',
|
SET_CURRENT_USER: 'SET_CURRENT_USER',
|
||||||
SET_CURRENT_ACCOUNT_ID: 'SET_CURRENT_ACCOUNT_ID',
|
SET_CURRENT_ACCOUNT_ID: 'SET_CURRENT_ACCOUNT_ID',
|
||||||
SET_CURRENT_USER_AVAILABILITY: 'SET_CURRENT_USER_AVAILABILITY',
|
SET_CURRENT_USER_AVAILABILITY: 'SET_CURRENT_USER_AVAILABILITY',
|
||||||
|
SET_CURRENT_USER_UI_SETTINGS: 'SET_CURRENT_USER_UI_SETTINGS',
|
||||||
|
|
||||||
// Chat List
|
// Chat List
|
||||||
RECEIVE_CHAT_LIST: 'RECEIVE_CHAT_LIST',
|
RECEIVE_CHAT_LIST: 'RECEIVE_CHAT_LIST',
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
# reset_password_token :string
|
# reset_password_token :string
|
||||||
# sign_in_count :integer default(0), not null
|
# sign_in_count :integer default(0), not null
|
||||||
# tokens :json
|
# tokens :json
|
||||||
|
# ui_settings :jsonb
|
||||||
# uid :string default(""), not null
|
# uid :string default(""), not null
|
||||||
# unconfirmed_email :string
|
# unconfirmed_email :string
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
|
|
|
@ -13,6 +13,7 @@ json.confirmed resource.confirmed?
|
||||||
json.avatar_url resource.avatar_url
|
json.avatar_url resource.avatar_url
|
||||||
json.access_token resource.access_token.token
|
json.access_token resource.access_token.token
|
||||||
json.availability_status resource.availability_status
|
json.availability_status resource.availability_status
|
||||||
|
json.ui_settings resource.ui_settings
|
||||||
json.accounts do
|
json.accounts do
|
||||||
json.array! resource.account_users do |account_user|
|
json.array! resource.account_users do |account_user|
|
||||||
json.id account_user.account_id
|
json.id account_user.account_id
|
||||||
|
|
9
db/migrate/20210109211805_add_ui_settings_to_users.rb
Normal file
9
db/migrate/20210109211805_add_ui_settings_to_users.rb
Normal file
|
@ -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
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pg_stat_statements"
|
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.datetime "updated_at", null: false
|
||||||
t.string "pubsub_token"
|
t.string "pubsub_token"
|
||||||
t.integer "availability", default: 0
|
t.integer "availability", default: 0
|
||||||
|
t.jsonb "ui_settings", default: {}
|
||||||
t.index ["email"], name: "index_users_on_email"
|
t.index ["email"], name: "index_users_on_email"
|
||||||
t.index ["pubsub_token"], name: "index_users_on_pubsub_token", unique: true
|
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
|
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||||
|
|
|
@ -87,6 +87,17 @@ RSpec.describe 'Profile API', type: :request do
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(::OnlineStatusTracker.get_status(account.id, agent.id)).to eq('offline')
|
expect(::OnlineStatusTracker.get_status(account.id, agent.id)).to eq('offline')
|
||||||
end
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue