diff --git a/assets/base/config.json b/assets/base/config.json index 8060edfae..e1ba2e15d 100644 --- a/assets/base/config.json +++ b/assets/base/config.json @@ -1,5 +1,6 @@ { "DefaultServerUrl": "http://localhost:8065", + "TestServerUrl": "http://localhost:8065", "DefaultLocale": "en", "DefaultTheme": "default" } diff --git a/service/actions/preferences.js b/service/actions/preferences.js index 219a4bf94..69934e2f5 100644 --- a/service/actions/preferences.js +++ b/service/actions/preferences.js @@ -1,13 +1,16 @@ // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import Client from 'service/client'; - -import {PreferencesTypes} from 'service/constants'; -import {bindClientFunc, forceLogoutIfNecessary} from './helpers'; - import {batchActions} from 'redux-batched-actions'; +import Client from 'service/client'; +import {Preferences, PreferencesTypes} from 'service/constants'; +import {getMyPreferences as getMyPreferencesSelector} from 'service/selectors/entities/preferences'; +import {getCurrentUserId} from 'service/selectors/entities/users'; +import {getPreferenceKey} from 'service/utils/preference_utils'; + +import {bindClientFunc, forceLogoutIfNecessary} from './helpers'; + export function getMyPreferences() { return bindClientFunc( Client.getMyPreferences, @@ -65,8 +68,23 @@ export function deletePreferences(preferences) { }; } -export default { - getMyPreferences, - savePreferences, - deletePreferences -}; +export function makeDirectChannelVisibleIfNecessary(otherUserId) { + return async (dispatch, getState) => { + const state = getState(); + const myPreferences = getMyPreferencesSelector(state); + const currentUserId = getCurrentUserId(state); + + let preference = myPreferences[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUserId)]; + + if (!preference || preference.value === 'false') { + preference = { + user_id: currentUserId, + category: Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, + name: otherUserId, + value: 'true' + }; + + await savePreferences([preference])(dispatch, getState); + } + }; +} diff --git a/service/actions/websocket.js b/service/actions/websocket.js index d118db7f6..d8ca7740a 100644 --- a/service/actions/websocket.js +++ b/service/actions/websocket.js @@ -1,9 +1,23 @@ // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +import {getProfilesByIds, getStatusesByIds} from 'service/actions/users'; import Client from 'service/client'; -import EventEmitter from 'service/utils/event_emitter'; import websocketClient from 'service/client/websocket_client'; +import { + fetchMyChannelsAndMembers, + getChannel, + getChannelStats, + updateChannelHeader, + updateChannelPurpose, + markChannelAsUnread, + markChannelAsRead +} from 'service/actions/channels'; +import { + getPosts, + getPostsSince +} from 'service/actions/posts'; +import {makeDirectChannelVisibleIfNecessary} from 'service/actions/preferences'; import { Constants, ChannelTypes, @@ -14,23 +28,8 @@ import { UsersTypes, WebsocketEvents } from 'service/constants'; - -import { - fetchMyChannelsAndMembers, - getChannel, - getChannelStats, - updateChannelHeader, - updateChannelPurpose, - markChannelAsUnread, - markChannelAsRead -} from 'service/actions/channels'; - -import { - getPosts, - getPostsSince -} from 'service/actions/posts'; - -import {getProfilesByIds, getStatusesByIds} from 'service/actions/users'; +import {getUserIdFromChannelName} from 'service/utils/channel_utils'; +import EventEmitter from 'service/utils/event_emitter'; export function init(siteUrl, token, optionalWebSocket) { return async (dispatch, getState) => { @@ -167,6 +166,12 @@ async function handleNewPostEvent(msg, dispatch, getState) { break; } + if (msg.data.channel_type === Constants.DM_CHANNEL) { + const otherUserId = getUserIdFromChannelName(users.currentId, msg.data.channel_name); + + makeDirectChannelVisibleIfNecessary(otherUserId)(dispatch, getState); + } + if (post.root_id && !posts[post.root_id]) { await Client.getPost(teamId, post.channel_id, post.root_id).then((data) => { const rootUserId = data.posts[post.root_id].user_id; diff --git a/service/selectors/entities/preferences.js b/service/selectors/entities/preferences.js index d4a1b88e4..47a342e40 100644 --- a/service/selectors/entities/preferences.js +++ b/service/selectors/entities/preferences.js @@ -7,6 +7,7 @@ import Config from 'assets/config.json'; import Themes from 'assets/themes.json'; import {Preferences} from 'service/constants'; +import {getPreferenceKey} from 'service/utils/preference_utils'; import {getCurrentTeamId} from './teams'; @@ -21,11 +22,11 @@ export const getTheme = createSelector( // Prefer the user's current team-specific theme over the user's current global theme over the default theme let themePreference; if (currentTeamId) { - themePreference = myPreferences[`${Preferences.CATEGORY_THEME}--${currentTeamId}`]; + themePreference = myPreferences[getPreferenceKey(Preferences.CATEGORY_THEME, currentTeamId)]; } if (!themePreference) { - themePreference = myPreferences[`${Preferences.CATEGORY_THEME}--`]; + themePreference = myPreferences[getPreferenceKey(Preferences.CATEGORY_THEME, '')]; } let theme; diff --git a/service/utils/channel_utils.js b/service/utils/channel_utils.js index 8bb9268a3..f1b8cd435 100644 --- a/service/utils/channel_utils.js +++ b/service/utils/channel_utils.js @@ -91,7 +91,7 @@ function isDirectChannel(channel) { } export function isDirectChannelVisible(userId, myPreferences, channel) { - const channelId = getUserIdFromChannelName(userId, channel); + const channelId = getUserIdFromChannelName(userId, channel.name); const dm = myPreferences[`${Constants.CATEGORY_DIRECT_CHANNEL_SHOW}--${channelId}`]; return dm && dm.value === 'true'; } @@ -114,11 +114,11 @@ function createMissingDirectChannels(currentUserId, allChannels, myPreferences) } function isDirectChannelForUser(userId, otherUserId, channel) { - return channel.type === Constants.DM_CHANNEL && getUserIdFromChannelName(userId, channel) === otherUserId; + return channel.type === Constants.DM_CHANNEL && getUserIdFromChannelName(userId, channel.name) === otherUserId; } -export function getUserIdFromChannelName(userId, channel) { - const ids = channel.name.split('__'); +export function getUserIdFromChannelName(userId, channelName) { + const ids = channelName.split('__'); let otherUserId = ''; if (ids[0] === userId) { otherUserId = ids[1]; @@ -149,7 +149,7 @@ export function completeDirectChannelInfo(usersState, myPreferences, channel) { } const dmChannelClone = {...channel}; - const teammateId = getUserIdFromChannelName(usersState.currentId, channel); + const teammateId = getUserIdFromChannelName(usersState.currentId, channel.name); return Object.assign(dmChannelClone, { display_name: displayUsername(usersState.profiles[teammateId], myPreferences), diff --git a/service/utils/preference_utils.js b/service/utils/preference_utils.js index 8669cc96e..14e2618a4 100644 --- a/service/utils/preference_utils.js +++ b/service/utils/preference_utils.js @@ -1,6 +1,10 @@ // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +export function getPreferenceKey(category, name) { + return `${category}--${name}`; +} + export function getPreferencesByCategory(myPreferences, category) { const prefix = `${category}--`; const preferences = new Map(); diff --git a/test/service/actions/preferences.test.js b/test/service/actions/preferences.test.js index b96bbac09..95f3556b4 100644 --- a/test/service/actions/preferences.test.js +++ b/test/service/actions/preferences.test.js @@ -3,10 +3,13 @@ import assert from 'assert'; -import * as Actions from 'service/actions/preferences'; -import Client from 'service/client'; import configureStore from 'app/store'; -import {RequestStatus} from 'service/constants'; + +import * as Actions from 'service/actions/preferences'; +import {login} from 'service/actions/users'; +import Client from 'service/client'; +import {Preferences, RequestStatus} from 'service/constants'; + import TestHelper from 'test/test_helper'; describe('Actions.Preferences', () => { @@ -147,4 +150,40 @@ describe('Actions.Preferences', () => { assert.deepEqual(existingPreferences[1], myPreferences['test--test2']); assert.ok(!myPreferences['test--test3'], 'third preference doesn\'t exist'); }); + + it('makeDirectChannelVisibleIfNecessary', async () => { + const user = TestHelper.basicUser; + const user2 = await TestHelper.createClient().createUser(TestHelper.fakeUser()); + + await login(user.email, 'password1')(store.dispatch, store.getState); + + // Test that a new preference is created if non exists + await Actions.makeDirectChannelVisibleIfNecessary(user2.id)(store.dispatch, store.getState); + + let state = store.getState(); + let myPreferences = state.entities.preferences.myPreferences; + let preference = myPreferences[`${Preferences.CATEGORY_DIRECT_CHANNEL_SHOW}--${user2.id}`]; + assert.ok(preference, 'preference for showing direct channel doesn\'t exist'); + assert.equal(preference.value, 'true', 'preference for showing direct channel is not true'); + + // Test that nothing changes if the preference already exists and is true + await Actions.makeDirectChannelVisibleIfNecessary(user2.id)(store.dispatch, store.getState); + + const state2 = store.getState(); + assert.equal(state, state2, 'store should not change since direct channel is already visible'); + + // Test that the preference is updated if it already exists and is false + await Actions.savePreferences([{ + ...preference, + value: 'false' + }])(store.dispatch, store.getState); + + await Actions.makeDirectChannelVisibleIfNecessary(user2.id)(store.dispatch, store.getState); + + state = store.getState(); + myPreferences = state.entities.preferences.myPreferences; + preference = myPreferences[`${Preferences.CATEGORY_DIRECT_CHANNEL_SHOW}--${user2.id}`]; + assert.ok(preference, 'preference for showing direct channel doesn\'t exist'); + assert.equal(preference.value, 'true', 'preference for showing direct channel is not true'); + }).timeout(2000); }); diff --git a/test/test_helper.js b/test/test_helper.js index 58afbb82d..6057b24e2 100644 --- a/test/test_helper.js +++ b/test/test_helper.js @@ -110,7 +110,7 @@ class TestHelper { }; initBasic = async (client = this.createClient()) => { - client.setUrl(Config.DefaultServerUrl); + client.setUrl(Config.TestServerUrl || Config.DefaultServerUrl); this.basicClient = client; this.basicUser = await client.createUser(this.fakeUser());