From 54507bb11519b56670cad4e1a9c3e45ee4e115fe Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Thu, 15 Feb 2024 12:25:11 -0500 Subject: [PATCH] Fix types of various fields in GlobalState (#26125) * Fix types of entities.admin.analytics and entities.admin.teamAnalytics * Fix type of entities.channels.channelsInTeam * Fix types of entities.users.stats and entities.users.filteredStats * Fix types of various profilesInX fields in entities.users * Fix incorrect field name last_password_update_at used when updating password We never noticed this bug before because the settings modal reloads the current user after updating their password. * MM-56760 Fix users not being removed from state.entities.users properly --- .../src/actions/channel_actions.test.ts | 2 +- .../src/actions/global_actions.test.ts | 24 ++--- .../src/actions/status_actions.test.ts | 6 +- .../channels/src/actions/user_actions.test.ts | 4 +- .../src/actions/views/channel_sidebar.test.ts | 2 +- .../src/actions/websocket_actions.test.jsx | 2 +- .../feature_discovery/feature_discovery.tsx | 4 +- .../channel/details/channel_members/index.ts | 6 +- .../team/details/team_members/index.ts | 6 +- .../dashboard_checks/easy_management.ts | 2 +- .../system_analytics/system_analytics.tsx | 6 +- .../team_analytics/team_analytics.tsx | 6 +- .../about_area_channel.test.tsx | 2 +- .../channel_info_rhs/about_area_dm.test.tsx | 2 +- .../channel_info_rhs/about_area_gm.test.tsx | 2 +- .../channel_identifier_router/actions.test.ts | 2 +- .../components/more_direct_channels/index.ts | 2 +- .../more_direct_channels/list/index.test.ts | 10 +- .../sidebar/invite_members_button.test.tsx | 2 +- .../src/components/sidebar/sidebar.test.tsx | 2 +- .../tests/app_command_parser_test_data.ts | 2 +- ...channel_with_permissions_provider.test.tsx | 4 +- .../switch_channel_provider.test.tsx | 6 +- .../src/components/system_notice/notices.tsx | 2 +- .../system_notice/system_notice.tsx | 4 +- .../src/components/system_notice/types.ts | 4 +- .../user_group_popover.test.tsx | 2 +- .../src/actions/admin.test.ts | 8 +- .../src/actions/threads.test.js | 2 +- .../src/actions/users.test.ts | 2 +- .../mattermost-redux/src/actions/users.ts | 2 +- .../src/reducers/entities/admin.ts | 11 +-- .../src/reducers/entities/channels.ts | 9 +- .../src/reducers/entities/users.test.ts | 88 ++++++++++++++++- .../src/reducers/entities/users.ts | 96 +++++++++---------- .../src/selectors/entities/channels.test.ts | 80 ++++++++-------- .../src/selectors/entities/channels.ts | 25 +++-- .../src/selectors/entities/roles.test.ts | 5 - .../src/selectors/entities/threads.test.ts | 4 +- .../src/selectors/entities/users.ts | 21 ++-- .../src/selectors/entities/utils.test.ts | 2 +- .../src/store/initial_state.ts | 2 + webapp/channels/src/selectors/drafts.test.ts | 2 +- .../selectors/views/channel_sidebar.test.js | 10 +- webapp/channels/src/tests/constants/users.ts | 1 + webapp/platform/types/src/admin.ts | 29 +++++- webapp/platform/types/src/channels.ts | 4 +- webapp/platform/types/src/users.ts | 12 +-- 48 files changed, 316 insertions(+), 217 deletions(-) diff --git a/webapp/channels/src/actions/channel_actions.test.ts b/webapp/channels/src/actions/channel_actions.test.ts index 9218230a3c3..1e9c6562530 100644 --- a/webapp/channels/src/actions/channel_actions.test.ts +++ b/webapp/channels/src/actions/channel_actions.test.ts @@ -44,7 +44,7 @@ const initialState = { }), }, channelsInTeam: { - 'team-id': ['current_channel_id'], + 'team-id': new Set(['asdf']), }, messageCounts: { current_channel_id: {total: 10}, diff --git a/webapp/channels/src/actions/global_actions.test.ts b/webapp/channels/src/actions/global_actions.test.ts index 248931c4007..8dcb1b33271 100644 --- a/webapp/channels/src/actions/global_actions.test.ts +++ b/webapp/channels/src/actions/global_actions.test.ts @@ -112,8 +112,8 @@ describe('actions/global_actions', () => { }, }, channelsInTeam: { - team1: ['channel-in-team-1'], - team2: ['channel-in-team-2'], + team1: new Set(['channel-in-team-1']), + team2: new Set(['channel-in-team-2']), }, }, users: { @@ -185,8 +185,8 @@ describe('actions/global_actions', () => { }, }, channelsInTeam: { - team1: ['channel-in-team-1'], - team2: ['channel-in-team-2'], + team1: new Set(['channel-in-team-1']), + team2: new Set(['channel-in-team-2']), }, }, users: { @@ -257,8 +257,8 @@ describe('actions/global_actions', () => { }, }, channelsInTeam: { - team1: ['channel-in-team-1'], - team2: ['channel-in-team-2'], + team1: new Set(['channel-in-team-1']), + team2: new Set(['channel-in-team-2']), }, }, users: { @@ -382,8 +382,8 @@ describe('actions/global_actions', () => { }, }, channelsInTeam: { - team1: ['channel-in-team-1', directChannelId], - team2: ['channel-in-team-2'], + team1: new Set(['channel-in-team-1', directChannelId]), + team2: new Set(['channel-in-team-2']), }, }, users: { @@ -479,8 +479,8 @@ describe('actions/global_actions', () => { }, }, channelsInTeam: { - team1: ['channel-in-team-1', directChannelId, groupChannelId], - team2: ['channel-in-team-2'], + team1: new Set(['channel-in-team-1', directChannelId, groupChannelId]), + team2: new Set(['channel-in-team-2']), }, }, users: { @@ -554,8 +554,8 @@ describe('actions/global_actions', () => { }, }, channelsInTeam: { - team1: ['channel-in-team-1'], - team2: ['channel-in-team-2'], + team1: new Set(['channel-in-team-1']), + team2: new Set(['channel-in-team-2']), }, }, users: { diff --git a/webapp/channels/src/actions/status_actions.test.ts b/webapp/channels/src/actions/status_actions.test.ts index 0481c8951fa..8b88e5cb4e8 100644 --- a/webapp/channels/src/actions/status_actions.test.ts +++ b/webapp/channels/src/actions/status_actions.test.ts @@ -12,8 +12,6 @@ import * as Actions from 'actions/status_actions'; import mockStore from 'tests/test_store'; -import type {GlobalState} from 'types/store'; - jest.mock('mattermost-redux/actions/users', () => ({ getStatusesByIds: jest.fn(() => { return {type: ''}; @@ -38,7 +36,7 @@ describe('actions/status_actions', () => { currentChannelId: 'channel_id1', channels: {channel_id1: {id: 'channel_id1', name: 'channel1', team_id: 'team_id1'}, channel_id2: {id: 'channel_id2', name: 'channel2', team_id: 'team_id1'}}, myMembers: {channel_id1: {channel_id: 'channel_id1', user_id: 'current_user_id'}}, - channelsInTeam: {team_id1: ['channel_id1']}, + channelsInTeam: {team_id1: new Set(['channel_id1'])}, }, general: { config: { @@ -76,7 +74,7 @@ describe('actions/status_actions', () => { }, }, }, - } as unknown as GlobalState; + }; describe('loadStatusesForChannelAndSidebar', () => { test('load statuses with posts in channel and user in sidebar', () => { diff --git a/webapp/channels/src/actions/user_actions.test.ts b/webapp/channels/src/actions/user_actions.test.ts index bb62a4c6c68..9188d0e377e 100644 --- a/webapp/channels/src/actions/user_actions.test.ts +++ b/webapp/channels/src/actions/user_actions.test.ts @@ -90,7 +90,7 @@ describe('Actions.User', () => { } as Channel, }, channelsInTeam: { - team_1: ['current_channel_id'], + team_1: new Set(['current_channel_id']), }, messageCounts: { current_channel_id: {total: 10} as ChannelMessageCount, @@ -539,7 +539,7 @@ describe('Actions.User', () => { }; const channelsInTeam = { - '': [gmChannel.id], + '': new Set([gmChannel.id]), }; const myMembers = { diff --git a/webapp/channels/src/actions/views/channel_sidebar.test.ts b/webapp/channels/src/actions/views/channel_sidebar.test.ts index 97e357b6203..69de1ca4205 100644 --- a/webapp/channels/src/actions/views/channel_sidebar.test.ts +++ b/webapp/channels/src/actions/views/channel_sidebar.test.ts @@ -212,7 +212,7 @@ describe('multiSelectChannelTo', () => { }, {}), }, channelsInTeam: { - team1: channelIds.map((id) => `category1_${id}`).concat(channelIds.map((id) => `category2_${id}`)), + team1: new Set(channelIds.map((id) => `category1_${id}`).concat(channelIds.map((id) => `category2_${id}`))), }, }, teams: { diff --git a/webapp/channels/src/actions/websocket_actions.test.jsx b/webapp/channels/src/actions/websocket_actions.test.jsx index 958d46f7cde..8c5728434a6 100644 --- a/webapp/channels/src/actions/websocket_actions.test.jsx +++ b/webapp/channels/src/actions/websocket_actions.test.jsx @@ -132,7 +132,7 @@ let mockState = { }, }, channelsInTeam: { - team: ['channel1', 'channel2'], + team: new Set(['channel1', 'channel2']), }, membersInChannel: { otherChannel: {}, diff --git a/webapp/channels/src/components/admin_console/feature_discovery/feature_discovery.tsx b/webapp/channels/src/components/admin_console/feature_discovery/feature_discovery.tsx index 10fc72cf997..b89561f5cfe 100644 --- a/webapp/channels/src/components/admin_console/feature_discovery/feature_discovery.tsx +++ b/webapp/channels/src/components/admin_console/feature_discovery/feature_discovery.tsx @@ -4,7 +4,7 @@ import React from 'react'; import {FormattedMessage, useIntl} from 'react-intl'; -import type {AnalyticsRow} from '@mattermost/types/admin'; +import type {AnalyticsState} from '@mattermost/types/admin'; import type {CloudCustomer} from '@mattermost/types/cloud'; import type {ClientLicense} from '@mattermost/types/config'; @@ -46,7 +46,7 @@ type Props = { prevTrialLicense: ClientLicense; - stats?: Record; + stats?: AnalyticsState; actions: { getPrevTrialLicense: () => void; getCloudSubscription: () => void; diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_members/index.ts b/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_members/index.ts index d3491c6b6c9..6b1068067b6 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_members/index.ts +++ b/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_members/index.ts @@ -6,7 +6,7 @@ import {bindActionCreators} from 'redux'; import type {Dispatch} from 'redux'; import type {ChannelStats} from '@mattermost/types/channels'; -import type {UserProfile, UsersStats} from '@mattermost/types/users'; +import type {UserProfile} from '@mattermost/types/users'; import {getChannelStats} from 'mattermost-redux/actions/channels'; import {getFilteredUsersStats} from 'mattermost-redux/actions/users'; @@ -69,10 +69,10 @@ function makeMapStateToProps() { }; totalCount = stats.member_count; } else { - const filteredUserStats: UsersStats = selectFilteredUsersStats(state) || { + const filteredUserStats = selectFilteredUsersStats(state) || { total_users_count: 0, }; - totalCount = filteredUserStats.total_users_count; + totalCount = filteredUserStats.total_users_count ?? 0; } let users = []; diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_members/index.ts b/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_members/index.ts index 8a48d25b9ab..faab78eb85c 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_members/index.ts +++ b/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_members/index.ts @@ -5,7 +5,7 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import type {Dispatch} from 'redux'; -import type {UserProfile, UsersStats} from '@mattermost/types/users'; +import type {UserProfile} from '@mattermost/types/users'; import {getTeamStats as loadTeamStats} from 'mattermost-redux/actions/teams'; import {getFilteredUsersStats} from 'mattermost-redux/actions/users'; @@ -49,10 +49,10 @@ function mapStateToProps(state: GlobalState, props: Props) { const stats = getTeamStats(state)[teamId] || {active_member_count: 0}; totalCount = stats.active_member_count; } else { - const filteredUserStats: UsersStats = selectFilteredUsersStats(state) || { + const filteredUserStats = selectFilteredUsersStats(state) || { total_users_count: 0, }; - totalCount = filteredUserStats.total_users_count; + totalCount = filteredUserStats.total_users_count ?? 0; } let users = []; diff --git a/webapp/channels/src/components/admin_console/workspace-optimization/dashboard_checks/easy_management.ts b/webapp/channels/src/components/admin_console/workspace-optimization/dashboard_checks/easy_management.ts index 51fde21671f..abe6e1ffa64 100644 --- a/webapp/channels/src/components/admin_console/workspace-optimization/dashboard_checks/easy_management.ts +++ b/webapp/channels/src/components/admin_console/workspace-optimization/dashboard_checks/easy_management.ts @@ -72,7 +72,7 @@ const usesLDAP = async ( // // @see discussion here: https://github.com/mattermost/mattermost-webapp/pull/9822#discussion_r806879385 // const fetchGuestAccounts = async ( // config: Partial, -// analytics: Record | undefined, +// analytics: AnalyticsState | undefined, // ) => { // if (config.TeamSettings?.EnableOpenServer && config.GuestAccountsSettings?.Enable) { // let usersArray = await fetch(`${Client4.getBaseRoute()}/users/invalid_emails`).then((result) => result.json()); diff --git a/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx b/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx index de6862e13db..f8547735a04 100644 --- a/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx +++ b/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx @@ -4,7 +4,7 @@ import React from 'react'; import {FormattedMessage, defineMessages} from 'react-intl'; -import type {AnalyticsRow, PluginAnalyticsRow, IndexedPluginAnalyticsRow} from '@mattermost/types/admin'; +import type {AnalyticsRow, PluginAnalyticsRow, IndexedPluginAnalyticsRow, AnalyticsState} from '@mattermost/types/admin'; import type {ClientLicense} from '@mattermost/types/config'; import * as AdminActions from 'actions/admin_actions.jsx'; @@ -33,7 +33,7 @@ const StatTypes = Constants.StatTypes; type Props = { isLicensed: boolean; - stats?: Record; + stats?: AnalyticsState; license: ClientLicense; pluginStatHandlers: GlobalState['plugins']['siteStatsHandlers']; } @@ -119,7 +119,7 @@ export default class SystemAnalytics extends React.PureComponent { this.setState({pluginSiteStats: allStatsIndexed}); } - private getStatValue(stat: number | AnalyticsRow[]): number | undefined { + private getStatValue(stat: number | AnalyticsRow[] | undefined): number | undefined { if (typeof stat === 'number') { return stat; } diff --git a/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx b/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx index 634b44e8c51..c5cddaa8ccb 100644 --- a/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx +++ b/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type {MessageDescriptor} from 'react-intl'; import {FormattedDate, FormattedMessage, defineMessages} from 'react-intl'; -import type {AnalyticsRow} from '@mattermost/types/admin'; +import type {AnalyticsRow, AnalyticsState} from '@mattermost/types/admin'; import type {ClientLicense} from '@mattermost/types/config'; import type {Team} from '@mattermost/types/teams'; import type {UserProfile} from '@mattermost/types/users'; @@ -52,7 +52,7 @@ type Props = { license: ClientLicense; - stats: RelationOneToOne>; + stats: RelationOneToOne; actions: { @@ -129,7 +129,7 @@ export default class TeamAnalytics extends React.PureComponent { } } - private getStatValue(stat: number | AnalyticsRow[]): number | undefined { + private getStatValue(stat: number | AnalyticsRow[] | undefined): number | undefined { if (typeof stat === 'number') { return stat; } diff --git a/webapp/channels/src/components/channel_info_rhs/about_area_channel.test.tsx b/webapp/channels/src/components/channel_info_rhs/about_area_channel.test.tsx index 85ccaf64f2e..83629fd5f0b 100644 --- a/webapp/channels/src/components/channel_info_rhs/about_area_channel.test.tsx +++ b/webapp/channels/src/components/channel_info_rhs/about_area_channel.test.tsx @@ -44,7 +44,7 @@ const initialState: DeepPartial = { }, }, channelsInTeam: { - 'team-id': ['current_channel_id'], + 'team-id': new Set(['current_channel_id']), }, messageCounts: { current_channel_id: {total: 10}, diff --git a/webapp/channels/src/components/channel_info_rhs/about_area_dm.test.tsx b/webapp/channels/src/components/channel_info_rhs/about_area_dm.test.tsx index 5e603890991..30295ba21a4 100644 --- a/webapp/channels/src/components/channel_info_rhs/about_area_dm.test.tsx +++ b/webapp/channels/src/components/channel_info_rhs/about_area_dm.test.tsx @@ -46,7 +46,7 @@ const initialState: DeepPartial = { }, }, channelsInTeam: { - 'team-id': ['current_channel_id'], + 'team-id': new Set(['current_channel_id']), }, messageCounts: { current_channel_id: {total: 10}, diff --git a/webapp/channels/src/components/channel_info_rhs/about_area_gm.test.tsx b/webapp/channels/src/components/channel_info_rhs/about_area_gm.test.tsx index 2aa63717490..536b5672e3c 100644 --- a/webapp/channels/src/components/channel_info_rhs/about_area_gm.test.tsx +++ b/webapp/channels/src/components/channel_info_rhs/about_area_gm.test.tsx @@ -45,7 +45,7 @@ const initialState: DeepPartial = { }, }, channelsInTeam: { - 'team-id': ['current_channel_id'], + 'team-id': new Set(['current_channel_id']), }, messageCounts: { current_channel_id: {total: 10}, diff --git a/webapp/channels/src/components/channel_layout/channel_identifier_router/actions.test.ts b/webapp/channels/src/components/channel_layout/channel_identifier_router/actions.test.ts index 287bf659da9..939c8fb0831 100644 --- a/webapp/channels/src/components/channel_layout/channel_identifier_router/actions.test.ts +++ b/webapp/channels/src/components/channel_layout/channel_identifier_router/actions.test.ts @@ -59,7 +59,7 @@ describe('Actions', () => { currentChannelId: 'channel_id1', channels: {channel_id1: channel1, channel_id2: channel2, channel_id3: channel3, channel_id4: channel4, channel_id5: channel5, channel_id6: channel6}, myMembers: {channel_id1: {channel_id: 'channel_id1', user_id: 'current_user_id'}, channel_id2: {channel_id: 'channel_id2', user_id: 'current_user_id'}}, - channelsInTeam: {team_id1: ['channel_id1'], team_id2: ['channel_id2']}, + channelsInTeam: {team_id1: new Set(['channel_id1']), team_id2: new Set(['channel_id2'])}, }, teams: { currentTeamId: 'team_id1', diff --git a/webapp/channels/src/components/more_direct_channels/index.ts b/webapp/channels/src/components/more_direct_channels/index.ts index f8d35ac2fc2..f5b6d62f6e2 100644 --- a/webapp/channels/src/components/more_direct_channels/index.ts +++ b/webapp/channels/src/components/more_direct_channels/index.ts @@ -78,7 +78,7 @@ const makeMapStateToProps = () => { currentChannelMembers, currentUserId, restrictDirectMessage, - totalCount: stats.total_users_count, + totalCount: stats.total_users_count ?? 0, }; }; }; diff --git a/webapp/channels/src/components/more_direct_channels/list/index.test.ts b/webapp/channels/src/components/more_direct_channels/list/index.test.ts index 05b8b60fa2f..0c4fc9d8c33 100644 --- a/webapp/channels/src/components/more_direct_channels/list/index.test.ts +++ b/webapp/channels/src/components/more_direct_channels/list/index.test.ts @@ -216,7 +216,7 @@ describe('makeGetOptions', () => { [gmChannel3.id]: gmChannel3, }, channelsInTeam: { - '': [gmChannel1.id, gmChannel2.id, gmChannel3.id], + '': new Set([gmChannel1.id, gmChannel2.id, gmChannel3.id]), }, }, users: { @@ -291,7 +291,7 @@ describe('makeGetOptions', () => { [gmChannel2.id]: gmChannel2, }, channelsInTeam: { - '': [gmChannel1.id, gmChannel2.id], + '': new Set([gmChannel1.id, gmChannel2.id]), }, }, users: { @@ -378,7 +378,7 @@ describe('makeGetOptions', () => { [gmChannel3.id]: gmChannel3, }, channelsInTeam: { - '': [gmChannel1.id, gmChannel2.id, gmChannel3.id], + '': new Set([gmChannel1.id, gmChannel2.id, gmChannel3.id]), }, }, users: { @@ -480,7 +480,7 @@ describe('makeGetOptions', () => { [gmChannel2.id]: gmChannel2, }, channelsInTeam: { - '': [gmChannel1.id, gmChannel2.id], + '': new Set([gmChannel1.id, gmChannel2.id]), }, }, users: { @@ -591,7 +591,7 @@ describe('makeGetOptions', () => { [dm1.id]: dm1, }, channelsInTeam: { - '': [dm1.id], + '': new Set([dm1.id]), }, }, users: { diff --git a/webapp/channels/src/components/sidebar/invite_members_button.test.tsx b/webapp/channels/src/components/sidebar/invite_members_button.test.tsx index a86c1ead98a..e33d55b6795 100644 --- a/webapp/channels/src/components/sidebar/invite_members_button.test.tsx +++ b/webapp/channels/src/components/sidebar/invite_members_button.test.tsx @@ -35,7 +35,7 @@ describe('components/sidebar/invite_members_button', () => { }, stats: { total_users_count: 10, - } as any, // HARRISONTODO The defined type of entities.users.stats is incorrect + }, }, roles: { roles: { diff --git a/webapp/channels/src/components/sidebar/sidebar.test.tsx b/webapp/channels/src/components/sidebar/sidebar.test.tsx index d59fe613b56..51aa8deaf3f 100644 --- a/webapp/channels/src/components/sidebar/sidebar.test.tsx +++ b/webapp/channels/src/components/sidebar/sidebar.test.tsx @@ -141,7 +141,7 @@ describe('components/sidebar', () => { channel2, }, channelsInTeam: { - [currentTeamId]: [channel1.id, channel2.id], + [currentTeamId]: new Set([channel1.id, channel2.id]), }, messageCounts: { channel1: {total: 10}, diff --git a/webapp/channels/src/components/suggestion/command_provider/app_command_parser/tests/app_command_parser_test_data.ts b/webapp/channels/src/components/suggestion/command_provider/app_command_parser/tests/app_command_parser_test_data.ts index 49d5b522c00..0e49088b6c5 100644 --- a/webapp/channels/src/components/suggestion/command_provider/app_command_parser/tests/app_command_parser_test_data.ts +++ b/webapp/channels/src/components/suggestion/command_provider/app_command_parser/tests/app_command_parser_test_data.ts @@ -40,7 +40,7 @@ export const reduxTestState = { }, }, channelsInTeam: { - 'team-id': ['current_channel_id'], + 'team-id': new Set(['current_channel_id']), }, messageCounts: { current_channel_id: {total: 10}, diff --git a/webapp/channels/src/components/suggestion/search_channel_with_permissions_provider.test.tsx b/webapp/channels/src/components/suggestion/search_channel_with_permissions_provider.test.tsx index 57b59f8151c..2387b14bc7d 100644 --- a/webapp/channels/src/components/suggestion/search_channel_with_permissions_provider.test.tsx +++ b/webapp/channels/src/components/suggestion/search_channel_with_permissions_provider.test.tsx @@ -91,14 +91,14 @@ describe('components/SearchChannelWithPermissionsProvider', () => { }), }, channelsInTeam: { - someTeamId: [ + someTeamId: new Set([ 'somePublicMemberChannelId', 'somePrivateMemberChannelId', 'somePublicNonMemberChannelId', 'somePrivateNonMemberChannelId', 'someDirectConversation', 'someGroupConversation', - ], + ]), }, }, roles: { diff --git a/webapp/channels/src/components/suggestion/switch_channel_provider.test.tsx b/webapp/channels/src/components/suggestion/switch_channel_provider.test.tsx index f6e99b9d728..9491e56e448 100644 --- a/webapp/channels/src/components/suggestion/switch_channel_provider.test.tsx +++ b/webapp/channels/src/components/suggestion/switch_channel_provider.test.tsx @@ -726,7 +726,7 @@ describe('components/SwitchChannelProvider', () => { }, }, channelsInTeam: { - '': ['other_gm_channel'], + '': new Set(['other_gm_channel']), }, }, }, @@ -793,7 +793,7 @@ describe('components/SwitchChannelProvider', () => { }), }, channelsInTeam: { - '': ['other_gm_channel'], + '': new Set(['other_gm_channel']), }, }, preferences: { @@ -960,7 +960,7 @@ describe('components/SwitchChannelProvider', () => { }, }, channelsInTeam: { - '': ['thread_gm_channel'], + '': new Set(['thread_gm_channel']), }, }, }, diff --git a/webapp/channels/src/components/system_notice/notices.tsx b/webapp/channels/src/components/system_notice/notices.tsx index 408fe3a06b5..5f9dbacb791 100644 --- a/webapp/channels/src/components/system_notice/notices.tsx +++ b/webapp/channels/src/components/system_notice/notices.tsx @@ -132,7 +132,7 @@ const notices: Notice[] = [ return false; } - if (analytics.TOTAL_USERS < USERS_THRESHOLD) { + if (analytics.TOTAL_USERS && analytics.TOTAL_USERS < USERS_THRESHOLD) { return false; } diff --git a/webapp/channels/src/components/system_notice/system_notice.tsx b/webapp/channels/src/components/system_notice/system_notice.tsx index 4acd4a5e1f3..103194a86d9 100644 --- a/webapp/channels/src/components/system_notice/system_notice.tsx +++ b/webapp/channels/src/components/system_notice/system_notice.tsx @@ -4,7 +4,7 @@ import React from 'react'; import {FormattedMessage, injectIntl, type WrappedComponentProps} from 'react-intl'; -import type {AnalyticsRow} from '@mattermost/types/admin'; +import type {AnalyticsState} from '@mattermost/types/admin'; import type {Channel} from '@mattermost/types/channels'; import type {ClientConfig, ClientLicense} from '@mattermost/types/config'; import type {PreferenceType} from '@mattermost/types/preferences'; @@ -23,7 +23,7 @@ export interface Props extends WrappedComponentProps { serverVersion: string; config: Partial; license: ClientLicense; - analytics?: Record; + analytics?: AnalyticsState; currentChannel?: Channel; actions: { savePreferences(userId: string, preferences: PreferenceType[]): void; diff --git a/webapp/channels/src/components/system_notice/types.ts b/webapp/channels/src/components/system_notice/types.ts index adc6d3d974a..c261779b7d8 100644 --- a/webapp/channels/src/components/system_notice/types.ts +++ b/webapp/channels/src/components/system_notice/types.ts @@ -3,7 +3,7 @@ import type React from 'react'; -import type {AnalyticsRow} from '@mattermost/types/admin'; +import type {AnalyticsState} from '@mattermost/types/admin'; import type {Channel} from '@mattermost/types/channels'; export type Notice = { @@ -17,7 +17,7 @@ export type Notice = { serverVersion: string, config: any, license: any, - analytics?: Record, + analytics?: AnalyticsState, currentChannel?: Channel, ): boolean; } diff --git a/webapp/channels/src/components/user_group_popover/user_group_popover.test.tsx b/webapp/channels/src/components/user_group_popover/user_group_popover.test.tsx index 8ec099eafa0..a5cc7b887ef 100644 --- a/webapp/channels/src/components/user_group_popover/user_group_popover.test.tsx +++ b/webapp/channels/src/components/user_group_popover/user_group_popover.test.tsx @@ -84,7 +84,7 @@ describe('component/user_group_popover', () => { }, users: { profiles, - profilesInGroup: profilesInGroup as any, // HARRISONTODO The type entities.users.profilesInGroup is incorrectly an array when it should be a Set + profilesInGroup, }, preferences: { myPreferences: {}, diff --git a/webapp/channels/src/packages/mattermost-redux/src/actions/admin.test.ts b/webapp/channels/src/packages/mattermost-redux/src/actions/admin.test.ts index 982d81b1d3c..b935cd9479a 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/actions/admin.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/actions/admin.test.ts @@ -619,12 +619,12 @@ describe('Actions.Admin', () => { const analytics = state.entities.admin.analytics; expect(analytics).toBeTruthy(); - expect(analytics[Stats.TOTAL_PUBLIC_CHANNELS] > 0).toBeTruthy(); + expect(analytics[Stats.TOTAL_PUBLIC_CHANNELS]).toBeGreaterThan(0); const teamAnalytics = state.entities.admin.teamAnalytics; expect(teamAnalytics).toBeTruthy(); expect(teamAnalytics[TestHelper.basicTeam!.id]).toBeTruthy(); - expect(teamAnalytics[TestHelper.basicTeam!.id][Stats.TOTAL_PUBLIC_CHANNELS] > 0).toBeTruthy(); + expect(teamAnalytics[TestHelper.basicTeam!.id][Stats.TOTAL_PUBLIC_CHANNELS]).toBeGreaterThan(0); }); it('getAdvancedAnalytics', async () => { @@ -641,12 +641,12 @@ describe('Actions.Admin', () => { const analytics = state.entities.admin.analytics; expect(analytics).toBeTruthy(); - expect(analytics[Stats.TOTAL_SESSIONS] > 0).toBeTruthy(); + expect(analytics[Stats.TOTAL_SESSIONS]).toBeGreaterThan(0); const teamAnalytics = state.entities.admin.teamAnalytics; expect(teamAnalytics).toBeTruthy(); expect(teamAnalytics[TestHelper.basicTeam!.id]).toBeTruthy(); - expect(teamAnalytics[TestHelper.basicTeam!.id][Stats.TOTAL_SESSIONS] > 0).toBeTruthy(); + expect(teamAnalytics[TestHelper.basicTeam!.id][Stats.TOTAL_SESSIONS]).toBeGreaterThan(0); }); it('getPostsPerDayAnalytics', async () => { diff --git a/webapp/channels/src/packages/mattermost-redux/src/actions/threads.test.js b/webapp/channels/src/packages/mattermost-redux/src/actions/threads.test.js index 2938d3ed978..8ec5b5c284e 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/actions/threads.test.js +++ b/webapp/channels/src/packages/mattermost-redux/src/actions/threads.test.js @@ -91,7 +91,7 @@ describe('Actions.Threads', () => { }, channels: { channelsInTeam: { - [currentTeamId]: [channel.id], + [currentTeamId]: new Set([channel.id]), }, channels: { [channel.id]: channel, diff --git a/webapp/channels/src/packages/mattermost-redux/src/actions/users.test.ts b/webapp/channels/src/packages/mattermost-redux/src/actions/users.test.ts index 8219940de07..f8740ced6f2 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/actions/users.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/actions/users.test.ts @@ -1007,7 +1007,7 @@ describe('Actions.Users', () => { const currentUser = profiles[currentUserId]; expect(currentUser).toBeTruthy(); - expect(currentUser.last_password_update_at > beforeTime).toBeTruthy(); + expect(currentUser.last_password_update > beforeTime).toBeTruthy(); }); it('generateMfaSecret', async () => { diff --git a/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts b/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts index 8e42864d68a..2573542f80d 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts @@ -962,7 +962,7 @@ export function updateUserPassword(userId: string, currentPassword: string, newP const profile = getState().entities.users.profiles[userId]; if (profile) { - dispatch({type: UserTypes.RECEIVED_PROFILE, data: {...profile, last_password_update_at: new Date().getTime()}}); + dispatch({type: UserTypes.RECEIVED_PROFILE, data: {...profile, last_password_update: new Date().getTime()}}); } return {data: true}; diff --git a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/admin.ts b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/admin.ts index 8f056d2f3ce..9fcc116adf2 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/admin.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/admin.ts @@ -4,7 +4,7 @@ import type {AnyAction} from 'redux'; import {combineReducers} from 'redux'; -import type {ClusterInfo, AnalyticsRow} from '@mattermost/types/admin'; +import type {ClusterInfo, AnalyticsRow, AnalyticsState, AdminState} from '@mattermost/types/admin'; import type {Audit} from '@mattermost/types/audits'; import type {Compliance} from '@mattermost/types/compliance'; import type {AdminConfig, EnvironmentConfig} from '@mattermost/types/config'; @@ -12,7 +12,6 @@ import type {DataRetentionCustomPolicy} from '@mattermost/types/data_retention'; import type {MixedUnlinkedGroupRedux} from '@mattermost/types/groups'; import type {PluginRedux, PluginStatusRedux} from '@mattermost/types/plugins'; import type {SamlCertificateStatus, SamlMetadataResponse} from '@mattermost/types/saml'; -import type {Team} from '@mattermost/types/teams'; import type {UserAccessToken, UserProfile} from '@mattermost/types/users'; import type {RelationOneToOne, IDMappedObjects} from '@mattermost/types/utilities'; @@ -161,8 +160,8 @@ function samlCertStatus(state: Partial = {}, action: AnyA } } -export function convertAnalyticsRowsToStats(data: AnalyticsRow[], name: string): Record { - const stats: any = {}; +export function convertAnalyticsRowsToStats(data: AnalyticsRow[], name: string): AnalyticsState { + const stats: AnalyticsState = {}; const clonedData = [...data]; if (name === 'post_counts_day') { @@ -250,7 +249,7 @@ export function convertAnalyticsRowsToStats(data: AnalyticsRow[], name: string): return stats; } -function analytics(state: Record = {}, action: AnyAction) { +function analytics(state: AdminState['analytics'] = {}, action: AnyAction) { switch (action.type) { case AdminTypes.RECEIVED_SYSTEM_ANALYTICS: { const stats = convertAnalyticsRowsToStats(action.data, action.name); @@ -264,7 +263,7 @@ function analytics(state: Record = {}, action: } } -function teamAnalytics(state: RelationOneToOne> = {}, action: AnyAction) { +function teamAnalytics(state: AdminState['teamAnalytics'] = {}, action: AnyAction) { switch (action.type) { case AdminTypes.RECEIVED_TEAM_ANALYTICS: { const nextState = {...state}; diff --git a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/channels.ts b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/channels.ts index c746d15f65e..8ac5cb7b6ac 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/channels.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/channels.ts @@ -12,11 +12,12 @@ import type { ChannelMemberCountByGroup, ChannelMemberCountsByGroup, ServerChannel, + ChannelsState, } from '@mattermost/types/channels'; import type {Group} from '@mattermost/types/groups'; import type {Team} from '@mattermost/types/teams'; import type { - RelationOneToMany, + RelationOneToManyUnique, RelationOneToOne, IDMappedObjects, } from '@mattermost/types/utilities'; @@ -37,7 +38,7 @@ function removeMemberFromChannels(state: RelationOneToOne, action: AnyAction) { const nextState = {...state}; action.data.forEach((channel: Channel) => { @@ -49,7 +50,7 @@ function channelListToSet(state: any, action: AnyAction) { return nextState; } -function removeChannelFromSet(state: any, action: AnyAction) { +function removeChannelFromSet(state: RelationOneToManyUnique, action: AnyAction): RelationOneToManyUnique { const id = action.data.team_id; const nextSet = new Set(state[id]); nextSet.delete(action.data.id); @@ -220,7 +221,7 @@ function toClientChannel(serverChannel: ServerChannel): Channel { return channel; } -function channelsInTeam(state: RelationOneToMany = {}, action: AnyAction) { +function channelsInTeam(state: ChannelsState['channelsInTeam'] = {}, action: AnyAction) { switch (action.type) { case ChannelTypes.RECEIVED_CHANNEL: { const nextSet = new Set(state[action.data.team_id]); diff --git a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.test.ts b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.test.ts index faedf95f5d8..c86b1c1edac 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import type {UserProfile} from '@mattermost/types/users'; +import type {UserProfile, UsersState} from '@mattermost/types/users'; import type {IDMappedObjects} from '@mattermost/types/utilities'; import {UserTypes, ChannelTypes} from 'mattermost-redux/action_types'; @@ -1006,4 +1006,90 @@ describe('Reducers.users', () => { expect(newProfiles.third_user_id).toEqual(thirdUser); }); }); + + test('PROFILE_NO_LONGER_VISIBLE should remove references to users from state', () => { + const user = TestHelper.getUserMock({id: 'user'}); + + let state: UsersState = { + currentUserId: '', + mySessions: [], + myAudits: [], + myUserAccessTokens: {}, + profiles: { + user, + }, + profilesInTeam: { + team1: new Set([user.id]), + }, + profilesNotInTeam: { + team2: new Set([user.id]), + }, + profilesWithoutTeam: new Set([user.id]), + profilesInChannel: { + channel1: new Set([user.id]), + }, + profilesNotInChannel: { + channel2: new Set([user.id]), + }, + profilesInGroup: { + group1: new Set([user.id]), + }, + profilesNotInGroup: { + group2: new Set([user.id]), + }, + statuses: { + [user.id]: 'online', + }, + isManualStatus: { + [user.id]: true, + }, + stats: {}, + filteredStats: { + total_users_count: 0, + }, + lastActivity: {}, + }; + state = deepFreezeAndThrowOnMutation(state); + + const nextState = reducer(state, { + type: UserTypes.PROFILE_NO_LONGER_VISIBLE, + data: { + user_id: user.id, + }, + }); + + expect(nextState).toEqual({ + currentUserId: '', + mySessions: [], + myAudits: [], + myUserAccessTokens: {}, + profiles: {}, + profilesInTeam: { + team1: new Set(), + }, + profilesNotInTeam: { + team2: new Set(), + }, + profilesWithoutTeam: new Set(), + profilesInChannel: { + channel1: new Set(), + }, + profilesNotInChannel: { + channel2: new Set(), + }, + profilesInGroup: { + group1: new Set(), + }, + profilesNotInGroup: { + group2: new Set(), + }, + statuses: {}, + isManualStatus: {}, + stats: {}, + filteredStats: { + total_users_count: 0, + }, + lastActivity: {}, + }); + }); }); diff --git a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.ts b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.ts index 269342ddbc0..94451e0111e 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.ts @@ -5,22 +5,20 @@ import isEqual from 'lodash/isEqual'; import type {AnyAction} from 'redux'; import {combineReducers} from 'redux'; -import type {Channel} from '@mattermost/types/channels'; -import type {Group} from '@mattermost/types/groups'; import type {Team} from '@mattermost/types/teams'; -import type {UserAccessToken, UserProfile, UserStatus} from '@mattermost/types/users'; -import type {RelationOneToMany, IDMappedObjects, RelationOneToOne} from '@mattermost/types/utilities'; +import type {UserAccessToken, UserProfile, UserStatus, UsersState} from '@mattermost/types/users'; +import type {IDMappedObjects, RelationOneToManyUnique, RelationOneToOne} from '@mattermost/types/utilities'; import {UserTypes, ChannelTypes} from 'mattermost-redux/action_types'; -function profilesToSet(state: RelationOneToMany, action: AnyAction) { +function profilesToSet(state: RelationOneToManyUnique, action: AnyAction) { const id = action.id; const users: UserProfile[] = Object.values(action.data); return users.reduce((nextState, user) => addProfileToSet(nextState, id, user.id), state); } -function profileListToSet(state: RelationOneToMany, action: AnyAction, replace = false) { +function profileListToSet(state: RelationOneToManyUnique, action: AnyAction, replace = false) { const id = action.id; const users: UserProfile[] = action.data || []; @@ -34,7 +32,7 @@ function profileListToSet(state: RelationOneToMany, action: A return users.reduce((nextState, user) => addProfileToSet(nextState, id, user.id), state); } -function removeProfileListFromSet(state: RelationOneToMany, action: AnyAction) { +function removeProfileListFromSet(state: RelationOneToManyUnique, action: AnyAction) { const id = action.id; const nextSet = new Set(state[id]); if (action.data) { @@ -51,50 +49,31 @@ function removeProfileListFromSet(state: RelationOneToMany, a return state; } -function addProfileToSet(state: RelationOneToMany, id: string, userId: string) { - if (state[id]) { - // The type definitions for this function expect state[id] to be an array, but we seem to use Sets, so handle - // both of those just in case - if (Array.isArray(state[id]) && state[id].includes(userId)) { - return state; - } else if (!Array.isArray(state[id]) && (state[id] as unknown as Set).has(userId)) { - return state; - } - } - +function addProfileToSet(state: RelationOneToManyUnique, id: string, userId: string) { const nextSet = new Set(state[id]); nextSet.add(userId); return { ...state, [id]: nextSet, - } as RelationOneToMany; + }; } -function removeProfileFromTeams(state: RelationOneToMany, action: AnyAction) { +function removeProfileFromSets(state: RelationOneToManyUnique, action: AnyAction) { const newState = {...state}; let removed = false; Object.keys(state).forEach((key) => { - if (newState[key][action.data.user_id]) { - delete newState[key][action.data.user_id]; + if (newState[key].has(action.data.user_id)) { + newState[key] = new Set(newState[key]); + newState[key].delete(action.data.user_id); removed = true; } }); return removed ? newState : state; } -function removeProfileFromSet(state: RelationOneToMany, action: AnyAction) { +function removeProfileFromSet(state: RelationOneToManyUnique, action: AnyAction) { const {id, user_id: userId} = action.data; - if (state[id]) { - // The type definitions for this function expect state[id] to be an array, but we seem to use Sets, so handle - // both of those just in case - if (Array.isArray(state[id]) && !state[id].includes(userId)) { - return state; - } else if (!Array.isArray(state[id]) && !(state[id] as unknown as Set).has(userId)) { - return state; - } - } - const nextSet = new Set(state[id]); nextSet.delete(userId); return { @@ -161,7 +140,7 @@ function mySessions(state: Array<{id: string}> = [], action: AnyAction) { } } -function myAudits(state = [], action: AnyAction) { +function myAudits(state: UsersState['myAudits'] = [], action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_AUDITS: return [...action.data]; @@ -274,7 +253,7 @@ function profiles(state: IDMappedObjects = {}, action: AnyAction) { } } -function profilesInTeam(state: RelationOneToMany = {}, action: AnyAction) { +function profilesInTeam(state: UsersState['profilesInTeam'] = {}, action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILE_IN_TEAM: return addProfileToSet(state, action.data.id, action.data.user_id); @@ -295,14 +274,14 @@ function profilesInTeam(state: RelationOneToMany = {}, action return {}; case UserTypes.PROFILE_NO_LONGER_VISIBLE: - return removeProfileFromTeams(state, action); + return removeProfileFromSets(state, action); default: return state; } } -function profilesNotInTeam(state: RelationOneToMany = {}, action: AnyAction) { +function profilesNotInTeam(state: UsersState['profilesNotInTeam'] = {}, action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILE_NOT_IN_TEAM: return addProfileToSet(state, action.data.id, action.data.user_id); @@ -323,14 +302,14 @@ function profilesNotInTeam(state: RelationOneToMany = {}, act return {}; case UserTypes.PROFILE_NO_LONGER_VISIBLE: - return removeProfileFromTeams(state, action); + return removeProfileFromSets(state, action); default: return state; } } -function profilesWithoutTeam(state: Set = new Set(), action: AnyAction) { +function profilesWithoutTeam(state: UsersState['profilesWithoutTeam'] = new Set(), action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILE_WITHOUT_TEAM: { const nextSet = new Set(state); @@ -342,21 +321,26 @@ function profilesWithoutTeam(state: Set = new Set(), action: AnyAction) action.data.forEach((user: UserProfile) => nextSet.add(user.id)); return nextSet; } - case UserTypes.PROFILE_NO_LONGER_VISIBLE: case UserTypes.RECEIVED_PROFILE_IN_TEAM: { const nextSet = new Set(state); nextSet.delete(action.data.id); return nextSet; } + case UserTypes.PROFILE_NO_LONGER_VISIBLE: { + const nextSet = new Set(state); + nextSet.delete(action.data.user_id); + return nextSet; + } + case UserTypes.LOGOUT_SUCCESS: - return new Set(); + return new Set(); default: return state; } } -function profilesInChannel(state: RelationOneToMany = {}, action: AnyAction) { +function profilesInChannel(state: UsersState['profilesInChannel'] = {}, action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILE_IN_CHANNEL: return addProfileToSet(state, action.data.id, action.data.user_id); @@ -379,7 +363,7 @@ function profilesInChannel(state: RelationOneToMany = {}, }}); case UserTypes.PROFILE_NO_LONGER_VISIBLE: - return removeProfileFromTeams(state, action); + return removeProfileFromSets(state, action); case UserTypes.LOGOUT_SUCCESS: return {}; @@ -388,7 +372,7 @@ function profilesInChannel(state: RelationOneToMany = {}, } } -function profilesNotInChannel(state: RelationOneToMany = {}, action: AnyAction) { +function profilesNotInChannel(state: UsersState['profilesNotInChannel'] = {}, action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILE_NOT_IN_CHANNEL: return addProfileToSet(state, action.data.id, action.data.user_id); @@ -417,14 +401,14 @@ function profilesNotInChannel(state: RelationOneToMany = { return {}; case UserTypes.PROFILE_NO_LONGER_VISIBLE: - return removeProfileFromTeams(state, action); + return removeProfileFromSets(state, action); default: return state; } } -function profilesInGroup(state: RelationOneToMany = {}, action: AnyAction) { +function profilesInGroup(state: UsersState['profilesInGroup'] = {}, action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILES_LIST_IN_GROUP: { return profileListToSet(state, action); @@ -459,12 +443,18 @@ function profilesInGroup(state: RelationOneToMany = {}, acti } return state; } + + case UserTypes.PROFILE_NO_LONGER_VISIBLE: + return removeProfileFromSets(state, action); + + case UserTypes.LOGOUT_SUCCESS: + return {}; default: return state; } } -function profilesNotInGroup(state: RelationOneToMany = {}, action: AnyAction) { +function profilesNotInGroup(state: UsersState['profilesNotInGroup'] = {}, action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILES_FOR_GROUP: { const id = action.id; @@ -484,6 +474,12 @@ function profilesNotInGroup(state: RelationOneToMany = {}, a case UserTypes.RECEIVED_PROFILES_LIST_NOT_IN_GROUP: { return profileListToSet(state, action); } + + case UserTypes.PROFILE_NO_LONGER_VISIBLE: + return removeProfileFromSets(state, action); + + case UserTypes.LOGOUT_SUCCESS: + return {}; default: return state; } @@ -611,7 +607,7 @@ function myUserAccessTokens(state: Record = {}, action: } } -function stats(state = {}, action: AnyAction) { +function stats(state: UsersState['stats'] = {}, action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_USER_STATS: { const stat = action.data; @@ -625,7 +621,7 @@ function stats(state = {}, action: AnyAction) { } } -function filteredStats(state = {}, action: AnyAction) { +function filteredStats(state: UsersState['filteredStats'] = {}, action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_FILTERED_USER_STATS: { const stat = action.data; @@ -639,7 +635,7 @@ function filteredStats(state = {}, action: AnyAction) { } } -function lastActivity(state: RelationOneToOne = {}, action: AnyAction) { +function lastActivity(state: UsersState['lastActivity'] = {}, action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_STATUS: { const nextState = Object.assign({}, state); diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.test.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.test.ts index 50fb49f522c..c4bb775159b 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.test.ts @@ -40,9 +40,9 @@ describe('Selectors.Channels.getChannelsInCurrentTeam', () => { }; const channelsInTeam = { - [team1.id]: [channel1.id, channel3.id], - [team2.id]: [channel2.id], - '': [channel4.id], + [team1.id]: new Set([channel1.id, channel3.id]), + [team2.id]: new Set([channel2.id]), + '': new Set([channel4.id]), }; const testState = deepFreezeAndThrowOnMutation({ @@ -97,7 +97,7 @@ describe('Selectors.Channels.getChannelsInCurrentTeam', () => { }; const channelsInTeam = { - [team1.id]: [channel1.id, channel2.id], + [team1.id]: new Set([channel1.id, channel2.id]), }; const testStateDe = deepFreezeAndThrowOnMutation({ @@ -188,9 +188,9 @@ describe('Selectors.Channels.getMyChannels', () => { }; const channelsInTeam = { - [team1.id]: [channel1.id, channel3.id], - [team2.id]: [channel2.id], - '': [channel4.id, channel5.id], + [team1.id]: new Set([channel1.id, channel3.id]), + [team2.id]: new Set([channel2.id]), + '': new Set([channel4.id, channel5.id]), }; const myMembers = { @@ -319,9 +319,9 @@ describe('Selectors.Channels.getOtherChannels', () => { }; const channelsInTeam = { - [team1.id]: [channel1.id, channel3.id, channel5.id, channel6.id], - [team2.id]: [channel2.id], - '': [channel4.id], + [team1.id]: new Set([channel1.id, channel3.id, channel5.id, channel6.id]), + [team2.id]: new Set([channel2.id]), + '': new Set([channel4.id]), }; const myMembers = { @@ -625,8 +625,8 @@ describe('Selectors.Channels.getChannelsNameMapInCurrentTeam', () => { }; const channelsInTeam = { - [team1.id]: [channel1.id, channel4.id], - [team2.id]: [channel2.id, channel3.id], + [team1.id]: new Set([channel1.id, channel4.id]), + [team2.id]: new Set([channel2.id, channel3.id]), }; const testState = deepFreezeAndThrowOnMutation({ @@ -741,8 +741,8 @@ describe('Selectors.Channels.getChannelsNameMapInTeam', () => { }; const channelsInTeam = { - [team1.id]: [channel1.id, channel4.id], - [team2.id]: [channel2.id, channel3.id], + [team1.id]: new Set([channel1.id, channel4.id]), + [team2.id]: new Set([channel2.id, channel3.id]), }; const testState = deepFreezeAndThrowOnMutation({ @@ -796,8 +796,8 @@ describe('Selectors.Channels.getChannelNameToDisplayNameMap', () => { channel4, }, channelsInTeam: { - [team1.id]: [channel1.id, channel2.id, channel3.id], - [team2.id]: [channel4.id], + [team1.id]: new Set([channel1.id, channel2.id, channel3.id]), + [team2.id]: new Set([channel4.id]), }, }, teams: { @@ -913,7 +913,7 @@ describe('Selectors.Channels.getChannelNameToDisplayNameMap', () => { newChannel, }, channelsInTeam: { - [team1.id]: [channel1.id, channel2.id, channel3.id, newChannel.id], + [team1.id]: new Set([channel1.id, channel2.id, channel3.id, newChannel.id]), }, }, }, @@ -980,8 +980,8 @@ describe('Selectors.Channels.getGroupChannels', () => { }; const channelsInTeam = { - [team1.id]: [channel1.id, channel2.id], - '': [channel3.id, channel4.id, channel5.id], + [team1.id]: new Set([channel1.id, channel2.id]), + '': new Set([channel3.id, channel4.id, channel5.id]), }; const testState = deepFreezeAndThrowOnMutation({ @@ -1028,10 +1028,9 @@ describe('Selectors.Channels.getChannelIdsInCurrentTeam', () => { const channel5 = TestHelper.fakeChannelWithId(''); const channelsInTeam = { - [team1.id]: [channel1.id, channel2.id], - [team2.id]: [channel3.id, channel4.id], - // eslint-disable-next-line no-useless-computed-key - ['']: [channel5.id], + [team1.id]: new Set([channel1.id, channel2.id]), + [team2.id]: new Set([channel3.id, channel4.id]), + '': new Set([channel5.id]), }; const testState = deepFreezeAndThrowOnMutation({ @@ -1055,10 +1054,10 @@ describe('Selectors.Channels.getChannelIdsInCurrentTeam', () => { ...testState.entities.channels, channelsInTeam: { ...testState.entities.channels.channelsInTeam, - [team2.id]: [ + [team2.id]: new Set([ ...testState.entities.channels.channelsInTeam[team2.id], newChannel.id, - ], + ]), }, }, }, @@ -1085,10 +1084,9 @@ describe('Selectors.Channels.getChannelIdsForCurrentTeam', () => { const channel5 = TestHelper.fakeChannelWithId(''); const channelsInTeam = { - [team1.id]: [channel1.id, channel2.id], - [team2.id]: [channel3.id, channel4.id], - // eslint-disable-next-line no-useless-computed-key - ['']: [channel5.id], + [team1.id]: new Set([channel1.id, channel2.id]), + [team2.id]: new Set([channel3.id, channel4.id]), + '': new Set([channel5.id]), }; const testState = deepFreezeAndThrowOnMutation({ @@ -1112,10 +1110,10 @@ describe('Selectors.Channels.getChannelIdsForCurrentTeam', () => { ...testState.entities.channels, channelsInTeam: { ...testState.entities.channels.channelsInTeam, - [team2.id]: [ + [team2.id]: new Set([ ...testState.entities.channels.channelsInTeam[team2.id], anotherChannel.id, - ], + ]), }, }, }, @@ -1273,8 +1271,8 @@ describe('Selectors.Channels.getChannelsWithUserProfiles', () => { }; const channelsInTeam = { - [team1.id]: [channel1.id], - '': [channel2.id], + [team1.id]: new Set([channel1.id]), + '': new Set([channel2.id]), }; const user1 = TestHelper.fakeUserWithId(); @@ -1333,7 +1331,7 @@ describe('Selectors.Channels.getChannelsWithUserProfiles', () => { [unloadedChannel.id]: unloadedChannel, }, channelsInTeam: { - '': [channel2.id, unloadedChannel.id], + '': new Set([channel2.id, unloadedChannel.id]), }, }, }, @@ -2006,7 +2004,7 @@ describe('Selectors.Channels.getUnreadStatusInCurrentTeam', () => { }; const channelsInTeam = { - [team1.id]: [channel1.id, channel2.id], + [team1.id]: new Set([channel1.id, channel2.id]), }; const testState = deepFreezeAndThrowOnMutation({ @@ -3078,9 +3076,9 @@ describe('Selectors.Channels.getUnreadChannelIds', () => { }; const channelsInTeam = { - [team1.id]: [channel1.id, channel3.id], - [team2.id]: [channel2.id], - '': [channel4.id], + [team1.id]: new Set([channel1.id, channel3.id]), + [team2.id]: new Set([channel2.id]), + '': new Set([channel4.id]), }; const messageCounts = { @@ -3152,9 +3150,9 @@ describe('Selectors.Channels.getUnreadChannelIds', () => { }; const channelsInTeam = { - [team1.id]: [channel1.id, channel3.id], - [team2.id]: [channel2.id], - '': [channel4.id], + [team1.id]: new Set([channel1.id, channel3.id]), + [team2.id]: new Set([channel2.id]), + '': new Set([channel4.id]), }; const messageCounts = { diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.ts index f3e9377d37f..e7c993be5d9 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.ts @@ -17,7 +17,6 @@ import type {Team} from '@mattermost/types/teams'; import type {UserProfile, UsersState} from '@mattermost/types/users'; import type { IDMappedObjects, - RelationOneToMany, RelationOneToManyUnique, RelationOneToOne, } from '@mattermost/types/utilities'; @@ -99,7 +98,7 @@ export function getChannelsMemberCount(state: GlobalState): Record { +export function getChannelsInTeam(state: GlobalState): RelationOneToManyUnique { return state.entities.channels.channelsInTeam; } @@ -131,7 +130,7 @@ export function getChannelsInPolicy() { export const getDirectChannelsSet: (state: GlobalState) => Set = createSelector( 'getDirectChannelsSet', getChannelsInTeam, - (channelsInTeam: RelationOneToMany): Set => { + (channelsInTeam: RelationOneToManyUnique): Set => { if (!channelsInTeam) { return new Set(); } @@ -364,12 +363,12 @@ export function getChannelByTeamIdAndChannelName(state: GlobalState, teamId: str ); } -export const getChannelSetInCurrentTeam: (state: GlobalState) => string[] = createSelector( +export const getChannelSetInCurrentTeam: (state: GlobalState) => Set = createSelector( 'getChannelSetInCurrentTeam', getCurrentTeamId, getChannelsInTeam, - (currentTeamId: string, channelsInTeam: RelationOneToMany): string[] => { - return (channelsInTeam && channelsInTeam[currentTeamId]) || []; + (currentTeamId: string, channelsInTeam: RelationOneToManyUnique) => { + return (channelsInTeam && channelsInTeam[currentTeamId]) || new Set(); }, ); @@ -387,7 +386,7 @@ export const getChannelSetForAllTeams: (state: GlobalState) => string[] = create }, ); -function sortAndInjectChannels(channels: IDMappedObjects, channelSet: string[], locale: string): Channel[] { +function sortAndInjectChannels(channels: IDMappedObjects, channelSet: string[] | Set, locale: string): Channel[] { const currentChannels: Channel[] = []; if (typeof channelSet === 'undefined') { @@ -406,7 +405,7 @@ export const getChannelsInCurrentTeam: (state: GlobalState) => Channel[] = creat getAllChannels, getChannelSetInCurrentTeam, getCurrentUser, - (channels: IDMappedObjects, currentTeamChannelSet: string[], currentUser: UserProfile): Channel[] => { + (channels: IDMappedObjects, currentTeamChannelSet: Set, currentUser: UserProfile): Channel[] => { let locale = General.DEFAULT_LOCALE; if (currentUser && currentUser.locale) { @@ -433,8 +432,8 @@ export const getChannelsNameMapInTeam: (state: GlobalState, teamId: string) => R getAllChannels, getChannelsInTeam, (state: GlobalState, teamId: string): string => teamId, - (channels: IDMappedObjects, channelsInTeams: RelationOneToMany, teamId: string): Record => { - const channelsInTeam = channelsInTeams[teamId] || []; + (channels: IDMappedObjects, channelsInTeams: RelationOneToManyUnique, teamId: string): Record => { + const channelsInTeam = channelsInTeams[teamId] || new Set(); const channelMap: Record = {}; channelsInTeam.forEach((id) => { const channel = channels[id]; @@ -448,7 +447,7 @@ export const getChannelsNameMapInCurrentTeam: (state: GlobalState) => Record, currentTeamChannelSet: string[]): Record => { + (channels: IDMappedObjects, currentTeamChannelSet: Set): Record => { const channelMap: Record = {}; currentTeamChannelSet.forEach((id) => { const channel = channels[id]; @@ -462,7 +461,7 @@ export const getChannelNameToDisplayNameMap: (state: GlobalState) => Record, currentTeamChannelSet: string[]) => { + (channels: IDMappedObjects, currentTeamChannelSet: Set) => { const channelMap: Record = {}; for (const id of currentTeamChannelSet) { const channel = channels[id]; @@ -948,7 +947,7 @@ export const getChannelIdsInCurrentTeam: (state: GlobalState) => string[] = crea 'getChannelIdsInCurrentTeam', getCurrentTeamId, getChannelsInTeam, - (currentTeamId: string, channelsInTeam: RelationOneToMany): string[] => { + (currentTeamId: string, channelsInTeam: RelationOneToManyUnique): string[] => { return Array.from(channelsInTeam[currentTeamId] || []); }, ); diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/roles.test.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/roles.test.ts index 2d891889c86..7115b76739f 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/roles.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/roles.test.ts @@ -65,11 +65,6 @@ describe('Selectors.Roles', () => { channels[channel11.id] = channel11; channels[channel12.id] = channel12; - const channelsInTeam: Record> = {}; - channelsInTeam[team1.id] = [channel1.id, channel2.id, channel5.id, channel6.id, channel8.id, channel10.id, channel11.id]; - channelsInTeam[team2.id] = [channel3.id]; - channelsInTeam[''] = [channel4.id, channel7.id, channel9.id]; - const user = TestHelper.fakeUserWithId(); const profiles: Record = {}; profiles[user.id] = user; diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/threads.test.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/threads.test.ts index e99f794596a..705ad349dac 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/threads.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/threads.test.ts @@ -49,7 +49,7 @@ describe('Selectors.Threads.getThreadOrderInCurrentTeam', () => { }, channels: { channelsInTeam: { - [team1.id]: [post1.channel_id, post2.channel_id], + [team1.id]: new Set([post1.channel_id, post2.channel_id]), }, channels: { [post1.channel_id]: { @@ -116,7 +116,7 @@ describe('Selectors.Threads.getUnreadThreadOrderInCurrentTeam', () => { }, channels: { channelsInTeam: { - [team1.id]: [post1.channel_id, post2.channel_id], + [team1.id]: new Set([post1.channel_id, post2.channel_id]), }, channels: { [post1.channel_id]: { diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.ts index f92c370f937..cdbc3d6867e 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.ts @@ -9,7 +9,6 @@ import type {Team, TeamMembership} from '@mattermost/types/teams'; import type {UserProfile} from '@mattermost/types/users'; import type { IDMappedObjects, - RelationOneToMany, RelationOneToManyUnique, RelationOneToOne, } from '@mattermost/types/utilities'; @@ -60,11 +59,11 @@ export function getUserIdsNotInChannels(state: GlobalState): RelationOneToManyUn return state.entities.users.profilesNotInChannel; } -export function getUserIdsInTeams(state: GlobalState): RelationOneToMany { +export function getUserIdsInTeams(state: GlobalState): RelationOneToManyUnique { return state.entities.users.profilesInTeam; } -export function getUserIdsNotInTeams(state: GlobalState): RelationOneToMany { +export function getUserIdsNotInTeams(state: GlobalState): RelationOneToManyUnique { return state.entities.users.profilesNotInTeam; } @@ -72,11 +71,11 @@ export function getUserIdsWithoutTeam(state: GlobalState): Set { +export function getUserIdsInGroups(state: GlobalState): RelationOneToManyUnique { return state.entities.users.profilesInGroup; } -export function getUserIdsNotInGroups(state: GlobalState): RelationOneToMany { +export function getUserIdsNotInGroups(state: GlobalState): RelationOneToManyUnique { return state.entities.users.profilesNotInGroup; } @@ -262,7 +261,7 @@ export const getProfileSetNotInCurrentChannel: (state: GlobalState) => Set Array = createSelector( +export const getProfileSetInCurrentTeam: (state: GlobalState) => Set = createSelector( 'getProfileSetInCurrentTeam', (state) => state.entities.teams.currentTeamId, getUserIdsInTeams, @@ -271,7 +270,7 @@ export const getProfileSetInCurrentTeam: (state: GlobalState) => Array Array = createSelector( +export const getProfileSetNotInCurrentTeam: (state: GlobalState) => Set = createSelector( 'getProfileSetNotInCurrentTeam', (state) => state.entities.teams.currentTeamId, getUserIdsNotInTeams, @@ -281,12 +280,12 @@ export const getProfileSetNotInCurrentTeam: (state: GlobalState) => Array, profileSet?: 'all' | Array | Set): UserProfile[] { +function sortAndInjectProfiles(profiles: IDMappedObjects, profileSet?: 'all' | Set): UserProfile[] { const currentProfiles = injectProfiles(profiles, profileSet); return currentProfiles.sort(sortByUsername); } -function injectProfiles(profiles: IDMappedObjects, profileSet?: 'all' | Array | Set): UserProfile[] { +function injectProfiles(profiles: IDMappedObjects, profileSet?: 'all' | Set): UserProfile[] { let currentProfiles: UserProfile[] = []; if (typeof profileSet === 'undefined') { @@ -436,11 +435,11 @@ export function getStatusForUserId(state: GlobalState, userId: UserProfile['id'] return getUserStatuses(state)[userId]; } -export function getTotalUsersStats(state: GlobalState): any { +export function getTotalUsersStats(state: GlobalState) { return state.entities.users.stats; } -export function getFilteredUsersStats(state: GlobalState): any { +export function getFilteredUsersStats(state: GlobalState) { return state.entities.users.filteredStats; } diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/utils.test.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/utils.test.ts index 93886bac7df..9e6c6656134 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/utils.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/utils.test.ts @@ -51,7 +51,7 @@ describe('utils.makeAddLastViewAtToProfiles', () => { }; const channelsInTeam = { - '': [channel1.id, channel2.id, channel3.id], + '': new Set([channel1.id, channel2.id, channel3.id]), }; const testState = deepFreezeAndThrowOnMutation({ diff --git a/webapp/channels/src/packages/mattermost-redux/src/store/initial_state.ts b/webapp/channels/src/packages/mattermost-redux/src/store/initial_state.ts index 05c7255ee5f..45b39625a26 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/store/initial_state.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/store/initial_state.ts @@ -31,6 +31,7 @@ const state: GlobalState = { profilesNotInGroup: {}, statuses: {}, stats: {}, + filteredStats: {}, myUserAccessTokens: {}, lastActivity: {}, }, @@ -110,6 +111,7 @@ const state: GlobalState = { userAccessTokens: {}, clusterInfo: [], analytics: {}, + teamAnalytics: {}, dataRetentionCustomPolicies: {}, dataRetentionCustomPoliciesCount: 0, prevTrialLicense: {}, diff --git a/webapp/channels/src/selectors/drafts.test.ts b/webapp/channels/src/selectors/drafts.test.ts index db729beb129..0aad7925d60 100644 --- a/webapp/channels/src/selectors/drafts.test.ts +++ b/webapp/channels/src/selectors/drafts.test.ts @@ -29,7 +29,7 @@ const initialState = { currentChannelId: {id: currentChannelId, team_id: currentTeamId}, }, channelsInTeam: { - currentTeamId: [currentChannelId], + currentTeamId: new Set([currentChannelId]), }, myMembers: { currentChannelId: { diff --git a/webapp/channels/src/selectors/views/channel_sidebar.test.js b/webapp/channels/src/selectors/views/channel_sidebar.test.js index 859c804345a..48993b00eb5 100644 --- a/webapp/channels/src/selectors/views/channel_sidebar.test.js +++ b/webapp/channels/src/selectors/views/channel_sidebar.test.js @@ -84,7 +84,7 @@ describe('getUnreadChannels', () => { unreadChannel2, }, channelsInTeam: { - team1: ['unreadChannel1', 'unreadChannel2', 'readChannel'], + team1: new Set(['unreadChannel1', 'unreadChannel2', 'readChannel']), }, currentChannelId: 'currentChannel', messageCounts: { @@ -375,10 +375,10 @@ describe('getUnreadChannels', () => { }, channelsInTeam: { ...baseState.entities.channels.channelsInTeam, - team1: [ + team1: new Set([ ...baseState.entities.channels.channelsInTeam.team1, 'archivedChannel', - ], + ]), }, messageCounts: { ...baseState.entities.channels.messageCounts, @@ -435,7 +435,7 @@ describe('getDisplayedChannels', () => { unreadChannel2, }, channelsInTeam: { - team1: ['unreadChannel1', 'unreadChannel2', 'readChannel'], + team1: new Set(['unreadChannel1', 'unreadChannel2', 'readChannel']), }, currentChannelId: 'currentChannel', messageCounts: { @@ -608,7 +608,7 @@ describe('makeGetFilteredChannelIdsForCategory', () => { unreadChannel2, }, channelsInTeam: { - team1: ['unreadChannel1', 'unreadChannel2', 'readChannel'], + team1: new Set(['unreadChannel1', 'unreadChannel2', 'readChannel']), }, currentChannelId: 'currentChannel', messageCounts: { diff --git a/webapp/channels/src/tests/constants/users.ts b/webapp/channels/src/tests/constants/users.ts index 524b35c605a..65e953ca034 100644 --- a/webapp/channels/src/tests/constants/users.ts +++ b/webapp/channels/src/tests/constants/users.ts @@ -20,6 +20,7 @@ const emptyOtherUsersState: Omit; clusterInfo: ClusterInfo[]; samlCertStatus?: SamlCertificateStatus; - analytics?: Record; - teamAnalytics?: RelationOneToOne>; + analytics: AnalyticsState; + teamAnalytics: RelationOneToOne; userAccessTokensByUser?: RelationOneToOne>; plugins?: Record; pluginStatuses?: Record; @@ -71,6 +71,31 @@ export type AdminState = { prevTrialLicense: ClientLicense; }; +export type AnalyticsState = { + POST_PER_DAY?: AnalyticsRow[]; + BOT_POST_PER_DAY?: AnalyticsRow[]; + USERS_WITH_POSTS_PER_DAY?: AnalyticsRow[]; + + TOTAL_PUBLIC_CHANNELS?: number; + TOTAL_PRIVATE_GROUPS?: number; + TOTAL_POSTS?: number; + TOTAL_USERS?: number; + TOTAL_INACTIVE_USERS?: number; + TOTAL_TEAMS?: number; + TOTAL_WEBSOCKET_CONNECTIONS?: number; + TOTAL_MASTER_DB_CONNECTIONS?: number; + TOTAL_READ_DB_CONNECTIONS?: number; + DAILY_ACTIVE_USERS?: number; + MONTHLY_ACTIVE_USERS?: number; + TOTAL_FILE_POSTS?: number; + TOTAL_HASHTAG_POSTS?: number; + TOTAL_IHOOKS?: number; + TOTAL_OHOOKS?: number; + TOTAL_COMMANDS?: number; + TOTAL_SESSIONS?: number; + REGISTERED_USERS?: number; +} + export type ClusterInfo = { id: string; version: string; diff --git a/webapp/platform/types/src/channels.ts b/webapp/platform/types/src/channels.ts index 7b57510cfaf..ea582210ff9 100644 --- a/webapp/platform/types/src/channels.ts +++ b/webapp/platform/types/src/channels.ts @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {IDMappedObjects, RelationOneToMany, RelationOneToOne} from './utilities'; +import {IDMappedObjects, RelationOneToManyUnique, RelationOneToOne} from './utilities'; import {Team} from './teams'; // e.g. @@ -147,7 +147,7 @@ export type ChannelUnread = { export type ChannelsState = { currentChannelId: string; channels: IDMappedObjects; - channelsInTeam: RelationOneToMany; + channelsInTeam: RelationOneToManyUnique; myMembers: RelationOneToOne; roles: RelationOneToOne>; membersInChannel: RelationOneToOne>; diff --git a/webapp/platform/types/src/users.ts b/webapp/platform/types/src/users.ts index 9c291130da8..a792f4705aa 100644 --- a/webapp/platform/types/src/users.ts +++ b/webapp/platform/types/src/users.ts @@ -70,16 +70,16 @@ export type UsersState = { mySessions: Session[]; myAudits: Audit[]; profiles: IDMappedObjects; - profilesInTeam: RelationOneToMany; - profilesNotInTeam: RelationOneToMany; + profilesInTeam: RelationOneToManyUnique; + profilesNotInTeam: RelationOneToManyUnique; profilesWithoutTeam: Set; profilesInChannel: RelationOneToManyUnique; profilesNotInChannel: RelationOneToManyUnique; - profilesInGroup: RelationOneToMany; - profilesNotInGroup: RelationOneToMany; + profilesInGroup: RelationOneToManyUnique; + profilesNotInGroup: RelationOneToManyUnique; statuses: RelationOneToOne; - stats: RelationOneToOne; - filteredStats?: UsersStats; + stats: Partial; + filteredStats: Partial; myUserAccessTokens: Record; lastActivity: RelationOneToOne; };