From f89ccf1092743a5fa01e0b6b8d894067717eec7b Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Fri, 29 Oct 2021 15:34:06 +0530 Subject: [PATCH 1/5] Fixed a bug where user status is not displayed on first load of a product --- actions/user_actions.jsx | 8 +++++++- components/status_dropdown/index.ts | 4 +++- components/status_dropdown/status_dropdown.test.tsx | 1 + components/status_dropdown/status_dropdown.tsx | 5 +++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/actions/user_actions.jsx b/actions/user_actions.jsx index 2ebf7864ed22..9fa87db50bfb 100644 --- a/actions/user_actions.jsx +++ b/actions/user_actions.jsx @@ -422,10 +422,16 @@ export function autocompleteUsers(username) { }; } +export function setUserStatus(currentUserId) { + return (doDispatch, doGetState) => { + return UserActions.getStatus(currentUserId)(doDispatch, doGetState); + }; +} + export function autoResetStatus() { return async (doDispatch, doGetState) => { const {currentUserId} = getState().entities.users; - const {data: userStatus} = await UserActions.getStatus(currentUserId)(doDispatch, doGetState); + const {data: userStatus} = await setUserStatus(currentUserId)(doDispatch, doGetState); if (userStatus.status === UserStatuses.OUT_OF_OFFICE || !userStatus.manual) { return userStatus; diff --git a/components/status_dropdown/index.ts b/components/status_dropdown/index.ts index 207464507589..960c6e2210c2 100644 --- a/components/status_dropdown/index.ts +++ b/components/status_dropdown/index.ts @@ -20,6 +20,8 @@ import {isStatusDropdownOpen} from 'selectors/views/status_dropdown'; import {GenericAction} from 'mattermost-redux/types/actions'; import {GlobalState} from 'types/store'; +import {setUserStatus} from '../../actions/user_actions'; + import StatusDropdown from './status_dropdown'; function makeMapStateToProps() { @@ -27,7 +29,6 @@ function makeMapStateToProps() { return function mapStateToProps(state: GlobalState) { const currentUser = getCurrentUser(state); - const userId = currentUser?.id; const customStatus = getCustomStatus(state, userId); return { @@ -53,6 +54,7 @@ function mapDispatchToProps(dispatch: Dispatch) { setStatus, unsetCustomStatus, setStatusDropdown, + setUserStatus, }, dispatch), }; } diff --git a/components/status_dropdown/status_dropdown.test.tsx b/components/status_dropdown/status_dropdown.test.tsx index 0e936094f8b1..95d9791921ec 100644 --- a/components/status_dropdown/status_dropdown.test.tsx +++ b/components/status_dropdown/status_dropdown.test.tsx @@ -14,6 +14,7 @@ describe('components/StatusDropdown', () => { setStatus: jest.fn(), unsetCustomStatus: jest.fn(), setStatusDropdown: jest.fn(), + setUserStatus: jest.fn(), }; const baseProps = { diff --git a/components/status_dropdown/status_dropdown.tsx b/components/status_dropdown/status_dropdown.tsx index f403ef889d44..f25a1f24bb19 100644 --- a/components/status_dropdown/status_dropdown.tsx +++ b/components/status_dropdown/status_dropdown.tsx @@ -48,6 +48,7 @@ type Props = { setStatus: (status: UserStatus) => ActionFunc; unsetCustomStatus: () => ActionFunc; setStatusDropdown: (open: boolean) => void; + setUserStatus: (userID: string) => void; }; customStatus?: UserCustomStatus; currentUser: UserProfile; @@ -83,6 +84,10 @@ export default class StatusDropdown extends React.PureComponent { }; } + componentDidMount() { + this.props.actions.setUserStatus(this.props.userId); + } + setStatus = (status: string, dndEndTime?: number): void => { this.props.actions.setStatus({ user_id: this.props.userId, From 330fb01f866a74f1e9573c41329ae0ba5fa45ab8 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Tue, 2 Nov 2021 14:52:11 +0530 Subject: [PATCH 2/5] Saved user sttaus in stre --- actions/user_actions.jsx | 9 +-------- components/status_dropdown/index.ts | 3 --- components/status_dropdown/status_dropdown.tsx | 5 ----- packages/mattermost-redux/src/actions/users.ts | 1 + .../src/reducers/entities/users.ts | 14 ++++++++++++++ .../src/selectors/entities/users.ts | 6 +++++- .../mattermost-redux/src/store/initial_state.ts | 2 ++ packages/mattermost-redux/src/types/users.ts | 1 + 8 files changed, 24 insertions(+), 17 deletions(-) diff --git a/actions/user_actions.jsx b/actions/user_actions.jsx index 9fa87db50bfb..2dcaa25e8431 100644 --- a/actions/user_actions.jsx +++ b/actions/user_actions.jsx @@ -422,17 +422,10 @@ export function autocompleteUsers(username) { }; } -export function setUserStatus(currentUserId) { - return (doDispatch, doGetState) => { - return UserActions.getStatus(currentUserId)(doDispatch, doGetState); - }; -} - export function autoResetStatus() { return async (doDispatch, doGetState) => { const {currentUserId} = getState().entities.users; - const {data: userStatus} = await setUserStatus(currentUserId)(doDispatch, doGetState); - + const userStatus = Selectors.getCurrentUserStatus(doGetState()); if (userStatus.status === UserStatuses.OUT_OF_OFFICE || !userStatus.manual) { return userStatus; } diff --git a/components/status_dropdown/index.ts b/components/status_dropdown/index.ts index 960c6e2210c2..596ce591cec1 100644 --- a/components/status_dropdown/index.ts +++ b/components/status_dropdown/index.ts @@ -20,8 +20,6 @@ import {isStatusDropdownOpen} from 'selectors/views/status_dropdown'; import {GenericAction} from 'mattermost-redux/types/actions'; import {GlobalState} from 'types/store'; -import {setUserStatus} from '../../actions/user_actions'; - import StatusDropdown from './status_dropdown'; function makeMapStateToProps() { @@ -54,7 +52,6 @@ function mapDispatchToProps(dispatch: Dispatch) { setStatus, unsetCustomStatus, setStatusDropdown, - setUserStatus, }, dispatch), }; } diff --git a/components/status_dropdown/status_dropdown.tsx b/components/status_dropdown/status_dropdown.tsx index f25a1f24bb19..f403ef889d44 100644 --- a/components/status_dropdown/status_dropdown.tsx +++ b/components/status_dropdown/status_dropdown.tsx @@ -48,7 +48,6 @@ type Props = { setStatus: (status: UserStatus) => ActionFunc; unsetCustomStatus: () => ActionFunc; setStatusDropdown: (open: boolean) => void; - setUserStatus: (userID: string) => void; }; customStatus?: UserCustomStatus; currentUser: UserProfile; @@ -84,10 +83,6 @@ export default class StatusDropdown extends React.PureComponent { }; } - componentDidMount() { - this.props.actions.setUserStatus(this.props.userId); - } - setStatus = (status: string, dndEndTime?: number): void => { this.props.actions.setStatus({ user_id: this.props.userId, diff --git a/packages/mattermost-redux/src/actions/users.ts b/packages/mattermost-redux/src/actions/users.ts index cf0544e5a536..f9997a9cb4cc 100644 --- a/packages/mattermost-redux/src/actions/users.ts +++ b/packages/mattermost-redux/src/actions/users.ts @@ -249,6 +249,7 @@ export function loadMe(): ActionFunc { const user = getState().entities.users.profiles[currentUserId]; if (currentUserId) { Client4.setUserId(currentUserId); + await dispatch(getStatus(currentUserId)); } if (user) { diff --git a/packages/mattermost-redux/src/reducers/entities/users.ts b/packages/mattermost-redux/src/reducers/entities/users.ts index 095f25dbf347..1ea36ff01e01 100644 --- a/packages/mattermost-redux/src/reducers/entities/users.ts +++ b/packages/mattermost-redux/src/reducers/entities/users.ts @@ -419,6 +419,17 @@ function profilesInGroup(state: RelationOneToMany = {}, acti } } +function currentUserStatus(state: RelationOneToOne = {}, action: GenericAction) { + switch (action.type) { + case UserTypes.RECEIVED_STATUS: { + return action.data; + } + default: { + return state; + } + } +} + function statuses(state: RelationOneToOne = {}, action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_STATUS: { @@ -635,4 +646,7 @@ export default combineReducers({ // Total user stats after filters have been applied filteredStats, + + // Current user's status object, including details about manual status. + currentUserStatus, }); diff --git a/packages/mattermost-redux/src/selectors/entities/users.ts b/packages/mattermost-redux/src/selectors/entities/users.ts index 06cb11560939..afdee4d5dece 100644 --- a/packages/mattermost-redux/src/selectors/entities/users.ts +++ b/packages/mattermost-redux/src/selectors/entities/users.ts @@ -33,7 +33,7 @@ import {Reaction} from 'mattermost-redux/types/reactions'; import {GlobalState} from 'mattermost-redux/types/store'; import {Team, TeamMembership} from 'mattermost-redux/types/teams'; import {Group} from 'mattermost-redux/types/groups'; -import {UserProfile} from 'mattermost-redux/types/users'; +import {UserProfile, UserStatus} from 'mattermost-redux/types/users'; import { $Email, $ID, @@ -381,6 +381,10 @@ export const getProfilesWithoutTeam: (state: GlobalState, filters: Filters) => U }, ); +export function getCurrentUserStatus(state: GlobalState): UserStatus { + return state.entities.users.currentUserStatus; +} + export function getStatusForUserId(state: GlobalState, userId: $ID): string { return getUserStatuses(state)[userId]; } diff --git a/packages/mattermost-redux/src/store/initial_state.ts b/packages/mattermost-redux/src/store/initial_state.ts index f56d1a540e00..153524e749b5 100644 --- a/packages/mattermost-redux/src/store/initial_state.ts +++ b/packages/mattermost-redux/src/store/initial_state.ts @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import {GlobalState} from 'mattermost-redux/types/store'; +import {UserStatuses} from '../../../../utils/constants'; const state: GlobalState = { entities: { @@ -18,6 +19,7 @@ const state: GlobalState = { }, users: { currentUserId: '', + currentUserStatus: {user_id: '', status: UserStatuses.OFFLINE}, isManualStatus: {}, mySessions: [], myAudits: [], diff --git a/packages/mattermost-redux/src/types/users.ts b/packages/mattermost-redux/src/types/users.ts index d70eefe34e94..9d7fdac66472 100644 --- a/packages/mattermost-redux/src/types/users.ts +++ b/packages/mattermost-redux/src/types/users.ts @@ -64,6 +64,7 @@ export type UserProfileWithLastViewAt = UserProfile & { export type UsersState = { currentUserId: string; + currentUserStatus: UserStatus; isManualStatus: RelationOneToOne; mySessions: Session[]; myAudits: Audit[]; From 38ea7d702a714ba83dda873a5ea6799d770561e6 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Mon, 15 Nov 2021 10:14:21 +0530 Subject: [PATCH 3/5] Minor fixes --- packages/mattermost-redux/src/store/initial_state.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mattermost-redux/src/store/initial_state.ts b/packages/mattermost-redux/src/store/initial_state.ts index 153524e749b5..22ac51b634a0 100644 --- a/packages/mattermost-redux/src/store/initial_state.ts +++ b/packages/mattermost-redux/src/store/initial_state.ts @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import {GlobalState} from 'mattermost-redux/types/store'; -import {UserStatuses} from '../../../../utils/constants'; +import {General} from '../constants'; const state: GlobalState = { entities: { @@ -19,7 +19,7 @@ const state: GlobalState = { }, users: { currentUserId: '', - currentUserStatus: {user_id: '', status: UserStatuses.OFFLINE}, + currentUserStatus: {user_id: '', status: General.OFFLINE}, isManualStatus: {}, mySessions: [], myAudits: [], From 7e81b0ecd86c08044afc731ce123eb33628886e9 Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Mon, 15 Nov 2021 12:06:50 +0530 Subject: [PATCH 4/5] Type fix --- components/reset_status_modal/index.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/components/reset_status_modal/index.ts b/components/reset_status_modal/index.ts index d7ae7d1d5254..48d46dc8ba5c 100644 --- a/components/reset_status_modal/index.ts +++ b/components/reset_status_modal/index.ts @@ -37,12 +37,17 @@ type Actions = { }; function mapDispatchToProps(dispatch: Dispatch) { + const actions = bindActionCreators, Actions>({ + setStatus, + savePreferences, + }, dispatch); + + const autoResetStatusAction = bindActionCreators, any>({ + autoResetStatus, + }, dispatch); + return { - actions: bindActionCreators, Actions>({ - autoResetStatus, - setStatus, - savePreferences, - }, dispatch), + actions: {...actions, ...autoResetStatusAction}, }; } From 4a9d4b419105886ff3fc3bbc063210c7e748109a Mon Sep 17 00:00:00 2001 From: Harshil Sharma Date: Wed, 24 Nov 2021 15:42:08 +0530 Subject: [PATCH 5/5] WIP --- actions/user_actions.jsx | 5 ++- actions/websocket_actions.jsx | 4 +- client/websocket_client.tsx | 6 +++ components/logged_in/logged_in.tsx | 1 + components/reset_status_modal/index.ts | 5 ++- .../reset_status_modal/reset_status_modal.tsx | 43 ++++++++++++++----- .../mattermost-redux/src/actions/errors.ts | 1 + .../mattermost-redux/src/actions/users.ts | 2 +- .../mattermost-redux/src/client/client4.ts | 1 + .../src/store/initial_state.ts | 2 +- 10 files changed, 53 insertions(+), 17 deletions(-) diff --git a/actions/user_actions.jsx b/actions/user_actions.jsx index 2dcaa25e8431..68bb1a8b551e 100644 --- a/actions/user_actions.jsx +++ b/actions/user_actions.jsx @@ -424,9 +424,12 @@ export function autocompleteUsers(username) { export function autoResetStatus() { return async (doDispatch, doGetState) => { + console.log('autoResetStatus called'); const {currentUserId} = getState().entities.users; const userStatus = Selectors.getCurrentUserStatus(doGetState()); - if (userStatus.status === UserStatuses.OUT_OF_OFFICE || !userStatus.manual) { + console.log('userStatus'); + console.log(userStatus); + if (userStatus.status === UserStatuses.OUT_OF_OFFICE || !userStatus.manual || userStatus.status === '') { return userStatus; } diff --git a/actions/websocket_actions.jsx b/actions/websocket_actions.jsx index 5b37bd51b8a8..bcfcaceec6b6 100644 --- a/actions/websocket_actions.jsx +++ b/actions/websocket_actions.jsx @@ -61,7 +61,7 @@ import * as TeamActions from 'mattermost-redux/actions/teams'; import { checkForModifiedUsers, getMe, - getMissingProfilesByIds, + getMissingProfilesByIds, getStatus, getStatusesByIds, getUser as loadUser, } from 'mattermost-redux/actions/users'; @@ -270,6 +270,7 @@ export function unregisterAllPluginWebSocketEvents(pluginId) { } function handleFirstConnect() { + console.log('handleFirstConnect'); dispatch(batchActions([ { type: GeneralTypes.WEBSOCKET_SUCCESS, @@ -277,6 +278,7 @@ function handleFirstConnect() { }, clearErrors(), ])); + dispatch(getStatus(getCurrentUserId(getState()))) } function handleClose(failCount) { diff --git a/client/websocket_client.tsx b/client/websocket_client.tsx index 6df4c5184331..ee610c680043 100644 --- a/client/websocket_client.tsx +++ b/client/websocket_client.tsx @@ -5,6 +5,9 @@ import {getConfig} from 'mattermost-redux/selectors/entities/general'; import store from 'stores/redux_store.jsx'; import {SocketEvents} from 'utils/constants'; +import {dispatch} from 'jest-circus/build/state'; +import {getStatus} from 'mattermost-redux/actions/users'; +import {getCurrentUserId, getCurrentUserStatus} from 'mattermost-redux/selectors/entities/users'; const MAX_WEBSOCKET_FAILS = 7; const MIN_WEBSOCKET_RETRY_TIME = 3000; // 3 sec @@ -75,6 +78,7 @@ export default class WebSocketClient { const reliableWebSockets = config.EnableReliableWebSockets === 'true'; this.conn.onopen = () => { + console.log('this.conn.onopen called'); // No need to reset sequence number here. if (!reliableWebSockets) { this.serverSequence = 0; @@ -84,12 +88,14 @@ export default class WebSocketClient { this.sendMessage('authentication_challenge', {token}); } + console.log(`this.connectFailCount: ${this.connectFailCount}`); if (this.connectFailCount > 0) { console.log('websocket re-established connection'); //eslint-disable-line no-console if (this.reconnectCallback) { this.reconnectCallback(); } } else if (this.firstConnectCallback) { + console.log('calling firstConnectCallback'); this.firstConnectCallback(); } diff --git a/components/logged_in/logged_in.tsx b/components/logged_in/logged_in.tsx index c098451c0ec2..20f6e3fd3945 100644 --- a/components/logged_in/logged_in.tsx +++ b/components/logged_in/logged_in.tsx @@ -18,6 +18,7 @@ import WebSocketClient from 'client/web_websocket_client.jsx'; import BrowserStore from 'stores/browser_store'; import {UserProfile} from 'mattermost-redux/types/users'; import {Channel} from 'mattermost-redux/types/channels'; +import {getStatus} from 'mattermost-redux/actions/users'; const dispatch = store.dispatch; const getState = store.getState; diff --git a/components/reset_status_modal/index.ts b/components/reset_status_modal/index.ts index 48d46dc8ba5c..10581d5dddb7 100644 --- a/components/reset_status_modal/index.ts +++ b/components/reset_status_modal/index.ts @@ -16,7 +16,7 @@ import {savePreferences} from 'mattermost-redux/actions/preferences'; import {setStatus} from 'mattermost-redux/actions/users'; import {Preferences} from 'mattermost-redux/constants'; import {get} from 'mattermost-redux/selectors/entities/preferences'; -import {getStatusForUserId} from 'mattermost-redux/selectors/entities/users'; +import {getCurrentUserStatus, getStatusForUserId} from 'mattermost-redux/selectors/entities/users'; import {autoResetStatus} from 'actions/user_actions.jsx'; @@ -26,7 +26,8 @@ function mapStateToProps(state: GlobalState) { const {currentUserId} = state.entities.users; return { autoResetPref: get(state, Preferences.CATEGORY_AUTO_RESET_MANUAL_STATUS, currentUserId, ''), - currentUserStatus: getStatusForUserId(state, currentUserId), + // currentUserStatus: getStatusForUserId(state, currentUserId), + currentUserStatus: getCurrentUserStatus(state).status, }; } diff --git a/components/reset_status_modal/reset_status_modal.tsx b/components/reset_status_modal/reset_status_modal.tsx index 27d8349d0877..7eb271a0ae1c 100644 --- a/components/reset_status_modal/reset_status_modal.tsx +++ b/components/reset_status_modal/reset_status_modal.tsx @@ -12,6 +12,7 @@ import {UserStatuses} from 'utils/constants'; import {t} from 'utils/i18n'; import {UserStatus} from 'mattermost-redux/types/users'; import {PreferenceType} from 'mattermost-redux/types/preferences'; +import {dispatch} from 'jest-circus/build/state'; t('modal.manual_status.auto_responder.message_'); t('modal.manual_status.auto_responder.message_away'); @@ -103,17 +104,37 @@ export default class ResetStatusModal extends React.PureComponent } public componentDidMount(): void { - this.props.actions.autoResetStatus().then( - (status: UserStatus) => { - const statusIsManual = status.manual; - const autoResetPrefNotSet = this.props.autoResetPref === ''; - - this.setState({ - currentUserStatus: status, // Set in state until status refactor where we store 'manual' field in redux - show: Boolean(status.status === UserStatuses.OUT_OF_OFFICE || (statusIsManual && autoResetPrefNotSet)), - }); - }, - ); + console.log(`currentUserStatus: ${this.props.currentUserStatus}`); + // this.props.actions.autoResetStatus().then( + // (status: UserStatus) => { + // const statusIsManual = status.manual; + // const autoResetPrefNotSet = this.props.autoResetPref === ''; + // + // this.setState({ + // currentUserStatus: status, // Set in state until status refactor where we store 'manual' field in redux + // show: Boolean(status.status === UserStatuses.OUT_OF_OFFICE || (statusIsManual && autoResetPrefNotSet)), + // }); + // }, + // ); + } + + componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any) { + console.log(`prev ${prevProps.currentUserStatus}, now: ${this.props.currentUserStatus}`); + + + if (prevProps.currentUserStatus === '' && this.props.currentUserStatus != '') { + this.props.actions.autoResetStatus().then( + (status: UserStatus) => { + const statusIsManual = status.manual; + const autoResetPrefNotSet = this.props.autoResetPref === ''; + + this.setState({ + currentUserStatus: status, // Set in state until status refactor where we store 'manual' field in redux + show: Boolean(status.status === UserStatuses.OUT_OF_OFFICE || (statusIsManual && autoResetPrefNotSet)), + }); + }, + ); + } } private hideModal = (): void => this.setState({show: false}); diff --git a/packages/mattermost-redux/src/actions/errors.ts b/packages/mattermost-redux/src/actions/errors.ts index 9448819a3ef3..d9779b8b0f9d 100644 --- a/packages/mattermost-redux/src/actions/errors.ts +++ b/packages/mattermost-redux/src/actions/errors.ts @@ -68,6 +68,7 @@ export function logError(error: ServerError, displayable = false): ActionFunc { } export function clearErrors(): ActionFunc { + console.log('B'); return async (dispatch: DispatchFunc) => { dispatch({type: ErrorTypes.CLEAR_ERRORS, data: null}); diff --git a/packages/mattermost-redux/src/actions/users.ts b/packages/mattermost-redux/src/actions/users.ts index f9997a9cb4cc..59b5224043a2 100644 --- a/packages/mattermost-redux/src/actions/users.ts +++ b/packages/mattermost-redux/src/actions/users.ts @@ -249,7 +249,7 @@ export function loadMe(): ActionFunc { const user = getState().entities.users.profiles[currentUserId]; if (currentUserId) { Client4.setUserId(currentUserId); - await dispatch(getStatus(currentUserId)); + // window.setTimeout(() => dispatch(getStatus(currentUserId)), 15000) } if (user) { diff --git a/packages/mattermost-redux/src/client/client4.ts b/packages/mattermost-redux/src/client/client4.ts index e847b433bfaa..a052b00d17b0 100644 --- a/packages/mattermost-redux/src/client/client4.ts +++ b/packages/mattermost-redux/src/client/client4.ts @@ -968,6 +968,7 @@ export default class Client4 { }; getStatus = (userId: string) => { + console.log('Fetching user status'); return this.doFetch( `${this.getUserRoute(userId)}/status`, {method: 'get'}, diff --git a/packages/mattermost-redux/src/store/initial_state.ts b/packages/mattermost-redux/src/store/initial_state.ts index 22ac51b634a0..afe26b4bd02e 100644 --- a/packages/mattermost-redux/src/store/initial_state.ts +++ b/packages/mattermost-redux/src/store/initial_state.ts @@ -19,7 +19,7 @@ const state: GlobalState = { }, users: { currentUserId: '', - currentUserStatus: {user_id: '', status: General.OFFLINE}, + currentUserStatus: {user_id: '', status: ''}, isManualStatus: {}, mySessions: [], myAudits: [],