diff --git a/Makefile b/Makefile index e2741491d..c0e30c10c 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ clean: pre-run pre-run: @echo Make sure no previous build are in the folder - @rm -rf build/* actions client constants reducers selectors store utils types mattermost.client4* index.* mattermost.websocket_client* + @rm -rf build/* action_types actions client constants reducers selectors store utils types mattermost.client4* index.* mattermost.websocket_client* test: check-style npm test diff --git a/package-lock.json b/package-lock.json index 47be7b58d..ab4d0fb6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1300,6 +1300,12 @@ "@types/node": "*" } }, + "@types/shallow-equals": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/shallow-equals/-/shallow-equals-1.0.0.tgz", + "integrity": "sha512-XtGSj7GYPfJwaklDtMEONj+kmpyCP8OLYoPqp/ROM8BL1VaF2IgYbxiEKfLvOyHN7c2d1KAFYzy6EIu8CSFt1A==", + "dev": true + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -6844,9 +6850,19 @@ "dev": true }, "serialize-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", - "integrity": "sha1-ULZ51WNc34Rme9yOWa9OW4HV9go=" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-5.0.0.tgz", + "integrity": "sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==", + "requires": { + "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + } + } }, "serialize-javascript": { "version": "1.9.1", diff --git a/package.json b/package.json index 1f91a36e1..6049c0995 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "redux-thunk": "2.3.0", "remote-redux-devtools": "0.5.16", "reselect": "4.0.0", - "serialize-error": "2.1.0", + "serialize-error": "5.0.0", "shallow-equals": "1.0.0" }, "husky": { @@ -56,6 +56,7 @@ "@babel/preset-env": "7.6.0", "@babel/preset-typescript": "7.6.0", "@babel/runtime": "7.6.0", + "@types/shallow-equals": "1.0.0", "@typescript-eslint/eslint-plugin": "2.3.1", "@typescript-eslint/parser": "2.3.1", "@types/jest": "24.0.18", diff --git a/src/actions/bots.ts b/src/actions/bots.ts index ec96b7a59..4254e6825 100644 --- a/src/actions/bots.ts +++ b/src/actions/bots.ts @@ -6,10 +6,11 @@ import {BotTypes} from 'action_types'; import {bindClientFunc} from './helpers'; import {ActionFunc} from 'types/actions'; +import {Bot, BotPatch} from 'types/bots'; const BOTS_PER_PAGE_DEFAULT = 20; -export function createBot(bot): ActionFunc { +export function createBot(bot: Bot): ActionFunc { return bindClientFunc({ clientFunc: Client4.createBot, onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT, @@ -19,7 +20,7 @@ export function createBot(bot): ActionFunc { }); } -export function patchBot(botUserId, botPatch): ActionFunc { +export function patchBot(botUserId: string, botPatch: BotPatch): ActionFunc { return bindClientFunc({ clientFunc: Client4.patchBot, onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT, @@ -30,7 +31,7 @@ export function patchBot(botUserId, botPatch): ActionFunc { }); } -export function loadBot(botUserId): ActionFunc { +export function loadBot(botUserId: string): ActionFunc { return bindClientFunc({ clientFunc: Client4.getBot, onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT, @@ -51,7 +52,7 @@ export function loadBots(page = 0, perPage = BOTS_PER_PAGE_DEFAULT): ActionFunc }); } -export function disableBot(botUserId): ActionFunc { +export function disableBot(botUserId: string): ActionFunc { return bindClientFunc({ clientFunc: Client4.disableBot, onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT, @@ -61,7 +62,7 @@ export function disableBot(botUserId): ActionFunc { }); } -export function enableBot(botUserId): ActionFunc { +export function enableBot(botUserId: string): ActionFunc { return bindClientFunc({ clientFunc: Client4.enableBot, onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT, @@ -71,7 +72,7 @@ export function enableBot(botUserId): ActionFunc { }); } -export function assignBot(botUserId, newOwnerId): ActionFunc { +export function assignBot(botUserId: string, newOwnerId: string): ActionFunc { return bindClientFunc({ clientFunc: Client4.assignBot, onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT, diff --git a/src/actions/channels.ts b/src/actions/channels.ts index f956a2537..2babeaf43 100644 --- a/src/actions/channels.ts +++ b/src/actions/channels.ts @@ -573,7 +573,7 @@ export function getMyChannelMembers(teamId: string): ActionFunc { export function getChannelMembers(channelId: string, page = 0, perPage: number = General.CHANNELS_CHUNK_SIZE): ActionFunc { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { - let channelMembers; + let channelMembers: ChannelMembership[]; try { const channelMembersRequest = Client4.getChannelMembers(channelId, page, perPage); diff --git a/src/actions/errors.ts b/src/actions/errors.ts index 16b46b6c5..d5ba17dc5 100644 --- a/src/actions/errors.ts +++ b/src/actions/errors.ts @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import {ErrorTypes} from 'action_types'; -import serializeError from 'serialize-error'; +import {serializeError, ErrorObject} from 'serialize-error'; import {Client4} from 'client'; import EventEmitter from 'utils/event_emitter'; import {DispatchFunc, ActionFunc} from 'types/actions'; @@ -22,7 +22,7 @@ export function dismissError(index: number): ActionFunc { }; } -export function getLogErrorAction(error: serializeError.ErrorObject, displayable = false) { +export function getLogErrorAction(error: ErrorObject, displayable = false) { return { type: ErrorTypes.LOG_ERROR, displayable, diff --git a/src/actions/files.ts b/src/actions/files.ts index 8a04d97c9..53cf6fc8a 100644 --- a/src/actions/files.ts +++ b/src/actions/files.ts @@ -7,6 +7,7 @@ import {Action, batchActions, DispatchFunc, GetStateFunc, ActionFunc} from 'type import {logError} from './errors'; import {bindClientFunc, forceLogoutIfNecessary} from './helpers'; +import {FileUploadResponse} from 'types/files'; export function getFilesForPost(postId: string): ActionFunc { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { @@ -34,7 +35,7 @@ export function uploadFile(channelId: string, rootId: string, clientIds: Array { dispatch({type: FileTypes.UPLOAD_FILES_REQUEST, data: {}}, getState); - let files; + let files: FileUploadResponse; try { files = await Client4.uploadFile(fileFormData, formBoundary); } catch (error) { diff --git a/src/actions/gifs.ts b/src/actions/gifs.ts index 5b9fcc246..d7070ab89 100644 --- a/src/actions/gifs.ts +++ b/src/actions/gifs.ts @@ -3,63 +3,64 @@ import {GifTypes} from 'action_types'; import {Client4} from 'client'; import gfycatSdk from 'utils/gfycat_sdk'; -import {ActionResult, DispatchFunc, GetStateFunc} from 'types/actions'; +import {DispatchFunc, GetStateFunc} from 'types/actions'; +import {GlobalState} from 'types/store'; // APP PROPS -export function saveAppPropsRequest(props) { +export function saveAppPropsRequest(props: any) { return { type: GifTypes.SAVE_APP_PROPS, props, }; } -export function saveAppProps(appProps) { +export function saveAppProps(appProps: any) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const {GfycatApiKey, GfycatApiSecret} = getState().entities.general.config; - gfycatSdk(GfycatApiKey, GfycatApiSecret).authenticate(); + gfycatSdk(GfycatApiKey!, GfycatApiSecret!).authenticate(); dispatch(saveAppPropsRequest(appProps)); }; } // SEARCH -export function selectSearchText(searchText) { +export function selectSearchText(searchText: string) { return { type: GifTypes.SELECT_SEARCH_TEXT, searchText, }; } -export function updateSearchText(searchText) { +export function updateSearchText(searchText: string) { return { type: GifTypes.UPDATE_SEARCH_TEXT, searchText, }; } -export function searchBarTextSave(searchBarText) { +export function searchBarTextSave(searchBarText: string) { return { type: GifTypes.SAVE_SEARCH_BAR_TEXT, searchBarText, }; } -export function invalidateSearchText(searchText) { +export function invalidateSearchText(searchText: string) { return { type: GifTypes.INVALIDATE_SEARCH_TEXT, searchText, }; } -export function requestSearch(searchText) { +export function requestSearch(searchText: string) { return { type: GifTypes.REQUEST_SEARCH, searchText, }; } -export function receiveSearch({searchText, count, start, json}) { +export function receiveSearch({searchText, count, start, json}: {searchText: string; count: number; start: number; json: any}) { return { type: GifTypes.RECEIVE_SEARCH, searchText, @@ -71,14 +72,14 @@ export function receiveSearch({searchText, count, start, json}) { }; } -export function receiveSearchEnd(searchText) { +export function receiveSearchEnd(searchText: string) { return { type: GifTypes.RECEIVE_SEARCH_END, searchText, }; } -export function errorSearching(err, searchText) { +export function errorSearching(err: any, searchText: string) { return { type: GifTypes.SEARCH_FAILURE, searchText, @@ -86,7 +87,7 @@ export function errorSearching(err, searchText) { }; } -export function receiveCategorySearch({tagName, json}) { +export function receiveCategorySearch({tagName, json}: {tagName: string; json: any}) { return { type: GifTypes.RECEIVE_CATEGORY_SEARCH, searchText: tagName, @@ -101,7 +102,7 @@ export function clearSearchResults() { }; } -export function requestSearchById(gfyId) { +export function requestSearchById(gfyId: string) { return { type: GifTypes.SEARCH_BY_ID_REQUEST, payload: { @@ -110,7 +111,7 @@ export function requestSearchById(gfyId) { }; } -export function receiveSearchById(gfyId, gfyItem) { +export function receiveSearchById(gfyId: string, gfyItem: any) { return { type: GifTypes.SEARCH_BY_ID_SUCCESS, payload: { @@ -120,7 +121,7 @@ export function receiveSearchById(gfyId, gfyItem) { }; } -export function errorSearchById(err, gfyId) { +export function errorSearchById(err: any, gfyId: string) { return { type: GifTypes.SEARCH_BY_ID_FAILURE, err, @@ -128,21 +129,21 @@ export function errorSearchById(err, gfyId) { }; } -export function searchScrollPosition(scrollPosition) { +export function searchScrollPosition(scrollPosition: number) { return { type: GifTypes.SAVE_SEARCH_SCROLL_POSITION, scrollPosition, }; } -export function searchPriorLocation(priorLocation) { +export function searchPriorLocation(priorLocation: number) { return { type: GifTypes.SAVE_SEARCH_PRIOR_LOCATION, priorLocation, }; } -export function searchGfycat({searchText, count = 30, startIndex = 0}) { +export function searchGfycat({searchText, count = 30, startIndex = 0}: { searchText: string; count?: number; startIndex?: number}) { let start = startIndex; return (dispatch: DispatchFunc, getState: GetStateFunc) => { const {GfycatApiKey, GfycatApiSecret} = getState().entities.general.config; @@ -151,8 +152,9 @@ export function searchGfycat({searchText, count = 30, startIndex = 0}) { start = resultsByTerm[searchText].start + count; } dispatch(requestSearch(searchText)); - gfycatSdk(GfycatApiKey, GfycatApiSecret).authenticate(); - return gfycatSdk(GfycatApiKey, GfycatApiSecret).search({search_text: searchText, count, start}).then((json) => { + const sdk = gfycatSdk(GfycatApiKey!, GfycatApiSecret!); + sdk.authenticate(); + return sdk.search({search_text: searchText, count, start}).then((json: any) => { if (json.errorMessage) { // There was no results before if (resultsByTerm[searchText].items) { @@ -175,7 +177,7 @@ export function searchGfycat({searchText, count = 30, startIndex = 0}) { ); } }).catch( - (err) => dispatch(errorSearching(err, searchText)) + (err: any) => dispatch(errorSearching(err, searchText)) ); }; } @@ -189,8 +191,8 @@ export function searchCategory({tagName = '', gfyCount = 30, cursorPos = undefin cursor = resultsByTerm[tagName].cursor; } dispatch(requestSearch(tagName)); - return gfycatSdk(GfycatApiKey, GfycatApiSecret).getTrendingCategories({tagName, gfyCount, cursor}).then( - (json) => { + return gfycatSdk(GfycatApiKey!, GfycatApiSecret!).getTrendingCategories({tagName, gfyCount, cursor}).then( + (json: any) => { if (json.errorMessage) { if (resultsByTerm[tagName].gfycats) { dispatch(receiveSearchEnd(tagName)); @@ -214,12 +216,12 @@ export function searchCategory({tagName = '', gfyCount = 30, cursorPos = undefin } } } - ).catch((err) => dispatch(errorSearching(err, tagName))); + ).catch((err: any) => dispatch(errorSearching(err, tagName))); }; } -export function shouldSearch(state, searchText) { - const resultsByTerm = state.search.resultsByTerm[searchText]; +export function shouldSearch(state: GlobalState, searchText: string) { + const resultsByTerm = state.entities.gifs.search.resultsByTerm[searchText]; if (!resultsByTerm) { return true; } else if (resultsByTerm.isFetching) { @@ -230,7 +232,7 @@ export function shouldSearch(state, searchText) { return resultsByTerm.didInvalidate; } -export function searchIfNeeded(searchText) { +export function searchIfNeeded(searchText: string) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { if (shouldSearch(getState(), searchText)) { if (searchText.toLowerCase() === 'trending') { @@ -242,7 +244,7 @@ export function searchIfNeeded(searchText) { }; } -export function searchIfNeededInitial(searchText) { +export function searchIfNeededInitial(searchText: string) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch(updateSearchText(searchText)); if (shouldSearchInitial(getState(), searchText)) { @@ -255,7 +257,7 @@ export function searchIfNeededInitial(searchText) { }; } -export function shouldSearchInitial(state, searchText) { +export function shouldSearchInitial(state: GlobalState, searchText: string) { const resultsByTerm = state.entities.gifs.search.resultsByTerm[searchText]; if (!resultsByTerm) { return true; @@ -266,24 +268,24 @@ export function shouldSearchInitial(state, searchText) { return false; } -export function searchById(gfyId) { +export function searchById(gfyId: string) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const {GfycatApiKey, GfycatApiSecret} = getState().entities.general.config; dispatch(requestSearchById(gfyId)); - return gfycatSdk(GfycatApiKey, GfycatApiSecret).searchById({id: gfyId}).then( - (response) => { + return gfycatSdk(GfycatApiKey!, GfycatApiSecret!).searchById({id: gfyId}).then( + (response: any) => { dispatch(receiveSearchById(gfyId, response.gfyItem)); dispatch(cacheGifsRequest([response.gfyItem])); } - ).catch((err) => dispatch(errorSearchById(err, gfyId))); + ).catch((err: any) => dispatch(errorSearchById(err, gfyId))); }; } -export function shouldSearchById(state, gfyId) { +export function shouldSearchById(state: GlobalState, gfyId: string) { return !state.entities.gifs.cache.gifs[gfyId]; //TODO investigate, used to be !state.cache.gifs[gfyId]; } -export function searchByIdIfNeeded(gfyId) { +export function searchByIdIfNeeded(gfyId: string) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { if (shouldSearchById(getState(), gfyId)) { return dispatch(searchById(gfyId)); @@ -293,25 +295,25 @@ export function searchByIdIfNeeded(gfyId) { }; } -export function saveSearchScrollPosition(scrollPosition) { +export function saveSearchScrollPosition(scrollPosition: number) { return (dispatch: DispatchFunc) => { dispatch(searchScrollPosition(scrollPosition)); }; } -export function saveSearchPriorLocation(priorLocation) { +export function saveSearchPriorLocation(priorLocation: number) { return (dispatch: DispatchFunc) => { dispatch(searchPriorLocation(priorLocation)); }; } -export function searchTextUpdate(searchText) { +export function searchTextUpdate(searchText: string) { return (dispatch: DispatchFunc) => { dispatch(updateSearchText(searchText)); }; } -export function saveSearchBarText(searchBarText) { +export function saveSearchBarText(searchBarText: string) { return (dispatch: DispatchFunc) => { dispatch(searchBarTextSave(searchBarText)); }; @@ -325,14 +327,14 @@ export function categoriesListRequest() { }; } -export function categoriesListReceived(json) { +export function categoriesListReceived(json: any) { return { type: GifTypes.CATEGORIES_LIST_RECEIVED, ...json, }; } -export function categoriesListFailure(err) { +export function categoriesListFailure(err: any) { return { type: GifTypes.CATEGORIES_LIST_FAILURE, err, @@ -352,8 +354,8 @@ export function requestCategoriesList({count = 60} = {}) { ...(count && {count}), ...(cursor && {cursor}), }; - return gfycatSdk(GfycatApiKey, GfycatApiSecret).getCategories(options).then((json) => { - const newGfycats = json.tags.reduce((gfycats, tag) => { + return gfycatSdk(GfycatApiKey!, GfycatApiSecret!).getCategories(options).then((json: any) => { + const newGfycats = json.tags.reduce((gfycats: any[], tag: any) => { if (tag.gfycats[0] && tag.gfycats[0].width) { return [...gfycats, ...tag.gfycats]; } @@ -362,7 +364,7 @@ export function requestCategoriesList({count = 60} = {}) { dispatch(cacheGifsRequest(newGfycats)); dispatch(categoriesListReceived(json)); }).catch( - (err) => { + (err: any) => { dispatch(categoriesListFailure(err)); } ); @@ -381,7 +383,7 @@ export function requestCategoriesListIfNeeded({ }; } -export function shouldRequestCategoriesList(state) { +export function shouldRequestCategoriesList(state: {hasMore: boolean; isFetching: boolean; tagsList: any[]}) { const {hasMore, isFetching, tagsList} = state; if (!tagsList || !tagsList.length) { return true; @@ -404,14 +406,14 @@ export function cacheRequest() { }; } -export function cacheGifs(gifs) { +export function cacheGifs(gifs: any) { return { type: GifTypes.CACHE_GIFS, gifs, }; } -export function cacheGifsRequest(gifs) { +export function cacheGifsRequest(gifs: any) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch(cacheRequest()); dispatch(cacheGifs(gifs)); diff --git a/src/actions/groups.ts b/src/actions/groups.ts index 8e9e3a6ca..20f72f0da 100644 --- a/src/actions/groups.ts +++ b/src/actions/groups.ts @@ -27,7 +27,7 @@ export function linkGroupSyncable(groupID: string, syncableID: string, syncableT const dispatches: Action[] = []; - let type; + let type = ''; switch (syncableType) { case Groups.SYNCABLE_TYPE_TEAM: dispatches.push({type: GroupTypes.RECEIVED_GROUPS_ASSOCIATED_TO_TEAM, data: {teamID: syncableID, groups: [{id: groupID}]}}); @@ -65,7 +65,7 @@ export function unlinkGroupSyncable(groupID: string, syncableID: string, syncabl const dispatches: Action[] = []; - let type; + let type = ''; const data = {group_id: groupID, syncable_id: syncableID}; switch (syncableType) { case Groups.SYNCABLE_TYPE_TEAM: @@ -105,7 +105,7 @@ export function getGroupSyncables(groupID: string, syncableType: SyncableType): return {error}; } - let type; + let type = ''; switch (syncableType) { case Groups.SYNCABLE_TYPE_TEAM: type = GroupTypes.RECEIVED_GROUP_TEAMS; diff --git a/src/actions/helpers.ts b/src/actions/helpers.ts index 9c598829e..c3a74c6ad 100644 --- a/src/actions/helpers.ts +++ b/src/actions/helpers.ts @@ -68,7 +68,7 @@ export function bindClientFunc({ onFailure, params = [], }: { - clientFunc: (...args) => Promise; + clientFunc: (...args: any[]) => Promise; onRequest?: ActionType; onSuccess?: ActionType | Array; onFailure?: ActionType; @@ -79,7 +79,7 @@ export function bindClientFunc({ dispatch(requestData(onRequest), getState); } - let data = null; + let data: any = null; try { data = await clientFunc(...params); } catch (error) { @@ -107,7 +107,7 @@ export function bindClientFunc({ // Debounce function based on underscores modified to use es6 and a cb export function debounce(func: (...args: any) => unknown, wait: number, immediate: boolean, cb: () => unknown) { - let timeout; + let timeout: NodeJS.Timeout|null; return function fx(...args: Array) { const runLater = () => { timeout = null; diff --git a/src/actions/plugins.ts b/src/actions/plugins.ts index 7380a23cd..1951e9bc6 100644 --- a/src/actions/plugins.ts +++ b/src/actions/plugins.ts @@ -8,7 +8,14 @@ import {PluginTypes} from 'action_types'; import {ActionFunc} from 'types/actions'; -export function getMarketplacePlugins(filter): ActionFunc { +export type MarketplacePluginFilter = { + Page: number; + PerPage: number; + Filter: string; + ServerVersion: string; +} + +export function getMarketplacePlugins(filter: MarketplacePluginFilter): ActionFunc { return bindClientFunc({ clientFunc: Client4.getMarketplacePlugins, onSuccess: PluginTypes.RECEIVED_MARKETPLACE_PLUGINS, diff --git a/src/actions/posts.ts b/src/actions/posts.ts index d0484d7de..bf3643b38 100644 --- a/src/actions/posts.ts +++ b/src/actions/posts.ts @@ -27,13 +27,18 @@ import { savePreferences, } from './preferences'; import {getProfilesByIds, getProfilesByUsernames, getStatusesByIds} from './users'; -import {Action, ActionFunc, ActionResult, batchActions, DispatchFunc, GetStateFunc} from 'types/actions'; +import {Action, ActionFunc, ActionResult, batchActions, DispatchFunc, GetStateFunc, GenericAction} from 'types/actions'; import {GlobalState} from 'types/store'; import {Post} from 'types/posts'; +import {Error} from 'types/errors'; +import {Reaction} from 'types/reactions'; +import {UserProfile} from 'types/users'; +import {Dictionary} from 'types/utilities'; +import {CustomEmoji} from 'types/emojis'; // receivedPost should be dispatched after a single post from the server. This typically happens when an existing post // is updated. -export function receivedPost(post) { +export function receivedPost(post: Post) { return { type: PostTypes.RECEIVED_POST, data: post, @@ -42,7 +47,7 @@ export function receivedPost(post) { // receivedNewPost should be dispatched when receiving a newly created post or when sending a request to the server // to make a new post. -export function receivedNewPost(post) { +export function receivedNewPost(post: Post) { return { type: PostTypes.RECEIVED_NEW_POST, data: post, @@ -51,7 +56,7 @@ export function receivedNewPost(post) { // receivedPosts should be dispatched when receiving multiple posts from the server that may or may not be ordered. // This will typically be used alongside other actions like receivedPostsAfter which require the posts to be ordered. -export function receivedPosts(posts) { +export function receivedPosts(posts: CombinedPostList) { return { type: PostTypes.RECEIVED_POSTS, data: posts, @@ -59,7 +64,7 @@ export function receivedPosts(posts) { } // receivedPostsAfter should be dispatched when receiving an ordered list of posts that come before a given post. -export function receivedPostsAfter(posts, channelId, afterPostId, recent = false) { +export function receivedPostsAfter(posts: Array, channelId: string, afterPostId: string, recent = false) { return { type: PostTypes.RECEIVED_POSTS_AFTER, channelId, @@ -70,7 +75,7 @@ export function receivedPostsAfter(posts, channelId, afterPostId, recent = false } // receivedPostsBefore should be dispatched when receiving an ordered list of posts that come after a given post. -export function receivedPostsBefore(posts, channelId, beforePostId, oldest = false) { +export function receivedPostsBefore(posts: Array, channelId: string, beforePostId: string, oldest = false) { return { type: PostTypes.RECEIVED_POSTS_BEFORE, channelId, @@ -83,7 +88,7 @@ export function receivedPostsBefore(posts, channelId, beforePostId, oldest = fal // receivedPostsSince should be dispatched when receiving a list of posts that have been updated since a certain time. // Due to how the API endpoint works, some of these posts will be ordered, but others will not, so this needs special // handling from the reducers. -export function receivedPostsSince(posts, channelId) { +export function receivedPostsSince(posts: Array, channelId: string) { return { type: PostTypes.RECEIVED_POSTS_SINCE, channelId, @@ -93,7 +98,7 @@ export function receivedPostsSince(posts, channelId) { // receivedPostsInChannel should be dispatched when receiving a list of ordered posts within a channel when the // the adjacent posts are not known. -export function receivedPostsInChannel(posts, channelId, recent = false, oldest = false) { +export function receivedPostsInChannel(posts: CombinedPostList, channelId: string, recent = false, oldest = false) { return { type: PostTypes.RECEIVED_POSTS_IN_CHANNEL, channelId, @@ -104,7 +109,7 @@ export function receivedPostsInChannel(posts, channelId, recent = false, oldest } // receivedPostsInThread should be dispatched when receiving a list of unordered posts in a thread. -export function receivedPostsInThread(posts, rootId) { +export function receivedPostsInThread(posts: Array, rootId: string) { return { type: PostTypes.RECEIVED_POSTS_IN_THREAD, data: posts, @@ -114,7 +119,7 @@ export function receivedPostsInThread(posts, rootId) { // postDeleted should be dispatched when a post has been deleted and should be replaced with a "message deleted" // placeholder. This typically happens when a post is deleted by another user. -export function postDeleted(post) { +export function postDeleted(post: Post) { return { type: PostTypes.POST_DELETED, data: post, @@ -123,14 +128,14 @@ export function postDeleted(post) { // postRemoved should be dispatched when a post should be immediately removed. This typically happens when a post is // deleted by the current user. -export function postRemoved(post) { +export function postRemoved(post: Post) { return { type: PostTypes.POST_REMOVED, data: post, }; } -export function getPost(postId) { +export function getPost(postId: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { let post; @@ -157,7 +162,7 @@ export function getPost(postId) { }; } -export function createPost(post, files: any[] = []) { +export function createPost(post: Post, files: any[] = []) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const state = getState(); const currentUserId = state.entities.users.currentUserId; @@ -205,7 +210,7 @@ export function createPost(post, files: any[] = []) { meta: { offline: { effect: () => Client4.createPost({...newPost, create_at: 0}), - commit: (success, payload) => { + commit: (result: any, payload: any) => { const actions: Action[] = [ receivedPost(payload), { @@ -239,7 +244,7 @@ export function createPost(post, files: any[] = []) { }, maxRetry: 0, offlineRollback: true, - rollback: (success, error) => { + rollback: (result: any, error: Error) => { const data = { ...newPost, id: pendingPostId, @@ -354,11 +359,11 @@ export function resetCreatePostRequest() { return {type: PostTypes.CREATE_POST_RESET_REQUEST}; } -export function deletePost(post) { +export function deletePost(post: ExtendedPost) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const state = getState(); const delPost = {...post}; - if (delPost.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY) { + if (delPost.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY && delPost.system_post_ids) { delPost.system_post_ids.forEach((systemPostId) => { const systemPost = Selectors.getPost(state, systemPostId); if (systemPost) { @@ -383,7 +388,7 @@ export function deletePost(post) { }; } -export function editPost(post) { +export function editPost(post: Post) { return bindClientFunc({ clientFunc: Client4.patchPost, onRequest: PostTypes.EDIT_POST_REQUEST, @@ -395,7 +400,7 @@ export function editPost(post) { }); } -export function pinPost(postId) { +export function pinPost(postId: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch({type: PostTypes.EDIT_POST_REQUEST}, getState); let posts; @@ -432,7 +437,7 @@ export function pinPost(postId) { }; } -export function unpinPost(postId) { +export function unpinPost(postId: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch({type: PostTypes.EDIT_POST_REQUEST}, getState); let posts; @@ -469,7 +474,7 @@ export function unpinPost(postId) { }; } -export function addReaction(postId, emojiName) { +export function addReaction(postId: string, emojiName: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const currentUserId = getState().entities.users.currentUserId; @@ -491,7 +496,7 @@ export function addReaction(postId, emojiName) { }; } -export function removeReaction(postId, emojiName) { +export function removeReaction(postId: string, emojiName: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const currentUserId = getState().entities.users.currentUserId; @@ -512,7 +517,7 @@ export function removeReaction(postId, emojiName) { }; } -export function getCustomEmojiForReaction(name) { +export function getCustomEmojiForReaction(name: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const nonExistentEmoji = getState().entities.emojis.nonExistentEmoji; const customEmojisByName = selectCustomEmojisByName(getState()); @@ -533,7 +538,7 @@ export function getCustomEmojiForReaction(name) { }; } -export function getReactionsForPost(postId) { +export function getReactionsForPost(postId: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { let reactions; try { @@ -549,7 +554,7 @@ export function getReactionsForPost(postId) { const customEmojisByName = selectCustomEmojisByName(getState()); const emojisToLoad = new Set(); - reactions.forEach((r) => { + reactions.forEach((r: Reaction) => { const name = r.emoji_name; if (systemEmojis.has(name)) { @@ -585,7 +590,7 @@ export function getReactionsForPost(postId) { }; } -export function flagPost(postId) { +export function flagPost(postId: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const {currentUserId} = getState().entities.users; const preference = { @@ -601,7 +606,7 @@ export function flagPost(postId) { }; } -export function getPostThread(rootId, fetchThreads = true) { +export function getPostThread(rootId: string, fetchThreads = true) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch({type: PostTypes.GET_POST_THREAD_REQUEST}, getState); @@ -630,7 +635,7 @@ export function getPostThread(rootId, fetchThreads = true) { }; } -export function getPosts(channelId, page = 0, perPage = Posts.POST_CHUNK_SIZE, fetchThreads = true) { +export function getPosts(channelId: string, page = 0, perPage = Posts.POST_CHUNK_SIZE, fetchThreads = true) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { let posts; @@ -652,7 +657,7 @@ export function getPosts(channelId, page = 0, perPage = Posts.POST_CHUNK_SIZE, f }; } -export function getPostsUnread(channelId, fetchThreads = true) { +export function getPostsUnread(channelId: string, fetchThreads = true) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const userId = getCurrentUserId(getState()); let posts; @@ -679,7 +684,7 @@ export function getPostsUnread(channelId, fetchThreads = true) { }; } -export function getPostsSince(channelId, since, fetchThreads = true) { +export function getPostsSince(channelId: string, since: number, fetchThreads = true) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { let posts; try { @@ -703,7 +708,7 @@ export function getPostsSince(channelId, since, fetchThreads = true) { }; } -export function getPostsBefore(channelId, postId, page = 0, perPage = Posts.POST_CHUNK_SIZE, fetchThreads = true) { +export function getPostsBefore(channelId: string, postId: string, page = 0, perPage = Posts.POST_CHUNK_SIZE, fetchThreads = true) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { let posts; try { @@ -724,7 +729,7 @@ export function getPostsBefore(channelId, postId, page = 0, perPage = Posts.POST }; } -export function getPostsAfter(channelId, postId, page = 0, perPage = Posts.POST_CHUNK_SIZE, fetchThreads = true) { +export function getPostsAfter(channelId: string, postId: string, page = 0, perPage = Posts.POST_CHUNK_SIZE, fetchThreads = true) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { let posts; try { @@ -744,8 +749,14 @@ export function getPostsAfter(channelId, postId, page = 0, perPage = Posts.POST_ return {data: posts}; }; } +export type CombinedPostList = { + posts: Array; + order: Array; + next_post_id: string; + prev_post_id: string; +} -export function getPostsAround(channelId, postId, perPage = Posts.POST_CHUNK_SIZE / 2, fetchThreads = true) { +export function getPostsAround(channelId: string, postId: string, perPage = Posts.POST_CHUNK_SIZE / 2, fetchThreads = true) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { let after; let thread; @@ -764,7 +775,7 @@ export function getPostsAround(channelId, postId, perPage = Posts.POST_CHUNK_SIZ } // Dispatch a combined post list so that the order is correct for postsInChannel - const posts = { + const posts: CombinedPostList = { posts: { ...after.posts, ...thread.posts, @@ -792,7 +803,7 @@ export function getPostsAround(channelId, postId, perPage = Posts.POST_CHUNK_SIZ // getThreadsForPosts is intended for an array of posts that have been batched // (see the actions/websocket_actions/handleNewPostEvents function in the webapp) -export function getThreadsForPosts(posts, fetchThreads = true) { +export function getThreadsForPosts(posts: Array, fetchThreads = true) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { if (!Array.isArray(posts) || !posts.length) { return {data: true}; @@ -878,8 +889,8 @@ export function getProfilesAndStatusesForPosts(postsArrayOrMap: Array|Map< return Promise.all(promises); } -export function getNeededAtMentionedUsernames(state, posts): Set { - let usersByUsername; // Populate this lazily since it's relatively expensive +export function getNeededAtMentionedUsernames(state: GlobalState, posts: Array): Set { + let usersByUsername: Dictionary; // Populate this lazily since it's relatively expensive const usernamesToLoad = new Set(); @@ -916,12 +927,12 @@ export function getNeededAtMentionedUsernames(state, posts): Set { return usernamesToLoad; } -function buildPostAttachmentText(attachments) { +function buildPostAttachmentText(attachments: Array) { let attachmentText = ''; attachments.forEach((a) => { if (a.fields && a.fields.length) { - a.fields.forEach((f) => { + a.fields.forEach((f: any) => { attachmentText += ' ' + (f.value || ''); }); } @@ -938,7 +949,7 @@ function buildPostAttachmentText(attachments) { return attachmentText; } -export function getNeededCustomEmojis(state: GlobalState, posts): Set { +export function getNeededCustomEmojis(state: GlobalState, posts: Array): Set { if (getConfig(state).EnableCustomEmoji !== 'true') { return new Set(); } @@ -950,7 +961,7 @@ export function getNeededCustomEmojis(state: GlobalState, posts): Set { let customEmojisToLoad = new Set(); - let customEmojisByName; // Populate this lazily since it's relatively expensive + let customEmojisByName: Map; // Populate this lazily since it's relatively expensive const nonExistentEmoji = state.entities.emojis.nonExistentEmoji; posts.forEach((post) => { @@ -986,17 +997,18 @@ export function getNeededCustomEmojis(state: GlobalState, posts): Set { return customEmojisToLoad; } +export type ExtendedPost = Post & { system_post_ids?: string[] }; -export function removePost(post) { +export function removePost(post: ExtendedPost) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { - if (post.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY) { + if (post.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY && post.system_post_ids) { const state = getState(); for (const systemPostId of post.system_post_ids) { const systemPost = Selectors.getPost(state, systemPostId); if (systemPost) { - dispatch(removePost(systemPost) as any); + dispatch(removePost(systemPost as any) as any); } } } else { @@ -1005,7 +1017,7 @@ export function removePost(post) { }; } -export function selectPost(postId) { +export function selectPost(postId: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch({ type: PostTypes.RECEIVED_POST_SELECTED, @@ -1016,14 +1028,14 @@ export function selectPost(postId) { }; } -export function selectFocusedPostId(postId) { +export function selectFocusedPostId(postId: string) { return { type: PostTypes.RECEIVED_FOCUSED_POST, data: postId, }; } -export function unflagPost(postId) { +export function unflagPost(postId: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const {currentUserId} = getState().entities.users; const preference = { @@ -1038,7 +1050,7 @@ export function unflagPost(postId) { }; } -export function getOpenGraphMetadata(url) { +export function getOpenGraphMetadata(url: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { let data; try { @@ -1061,11 +1073,11 @@ export function getOpenGraphMetadata(url) { }; } -export function doPostAction(postId, actionId, selectedOption = '') { +export function doPostAction(postId: string, actionId: string, selectedOption = '') { return doPostActionWithCookie(postId, actionId, '', selectedOption); } -export function doPostActionWithCookie(postId, actionId, actionCookie, selectedOption = '') { +export function doPostActionWithCookie(postId: string, actionId: string, actionCookie: string, selectedOption = '') { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { let data; try { @@ -1087,7 +1099,7 @@ export function doPostActionWithCookie(postId, actionId, actionCookie, selectedO }; } -export function addMessageIntoHistory(message) { +export function addMessageIntoHistory(message: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch({ type: PostTypes.ADD_MESSAGE_INTO_HISTORY, @@ -1098,7 +1110,7 @@ export function addMessageIntoHistory(message) { }; } -export function resetHistoryIndex(index) { +export function resetHistoryIndex(index: number) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch({ type: PostTypes.RESET_HISTORY_INDEX, @@ -1109,7 +1121,7 @@ export function resetHistoryIndex(index) { }; } -export function moveHistoryIndexBack(index) { +export function moveHistoryIndexBack(index: number) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch({ type: PostTypes.MOVE_HISTORY_INDEX_BACK, @@ -1120,7 +1132,7 @@ export function moveHistoryIndexBack(index) { }; } -export function moveHistoryIndexForward(index) { +export function moveHistoryIndexForward(index: number) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch({ type: PostTypes.MOVE_HISTORY_INDEX_FORWARD, @@ -1131,7 +1143,7 @@ export function moveHistoryIndexForward(index) { }; } -export function handleNewPost(msg) { +export function handleNewPost(msg: Omit) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const state = getState(); const currentUserId = getCurrentUserId(state); @@ -1156,7 +1168,7 @@ export function handleNewPost(msg) { }; } -function completePostReceive(post: Post, websocketMessageProps) { +function completePostReceive(post: Post, websocketMessageProps: any) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const state = getState(); const rootPost = Selectors.getPost(state, post.root_id); @@ -1169,7 +1181,7 @@ function completePostReceive(post: Post, websocketMessageProps) { }; } -function lastPostActions(post, websocketMessageProps) { +function lastPostActions(post: Post, websocketMessageProps: any) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const state = getState(); const actions = [ diff --git a/src/actions/search.ts b/src/actions/search.ts index 805a099f5..be05366ac 100644 --- a/src/actions/search.ts +++ b/src/actions/search.ts @@ -12,6 +12,7 @@ import {getProfilesAndStatusesForPosts, receivedPosts} from './posts'; import {ActionResult, batchActions, DispatchFunc, GetStateFunc, ActionFunc} from 'types/actions'; import {RelationOneToOne} from 'types/utilities'; import {Post} from 'types/posts'; +import {SearchParameter} from 'types/search'; const WEBAPP_SEARCH_PER_PAGE = 20; export function getMissingChannelsFromPosts(posts: RelationOneToOne): ActionFunc { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { @@ -36,7 +37,7 @@ export function getMissingChannelsFromPosts(posts: RelationOneToOne) }; } -export function searchPostsWithParams(teamId, params): ActionFunc { +export function searchPostsWithParams(teamId: string, params: SearchParameter): ActionFunc { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const isGettingMore = params.page > 0; dispatch({ @@ -85,7 +86,7 @@ export function searchPostsWithParams(teamId, params): ActionFunc { }; } -export function searchPosts(teamId, terms, isOrSearch, includeDeletedChannels) { +export function searchPosts(teamId: string, terms: string, isOrSearch: boolean, includeDeletedChannels: boolean) { return searchPostsWithParams(teamId, {terms, is_or_search: isOrSearch, include_deleted_channels: includeDeletedChannels, page: 0, per_page: WEBAPP_SEARCH_PER_PAGE}); } @@ -147,7 +148,7 @@ export function getFlaggedPosts(): ActionFunc { }; } -export function getPinnedPosts(channelId): ActionFunc { +export function getPinnedPosts(channelId: string): ActionFunc { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch({type: SearchTypes.SEARCH_PINNED_POSTS_REQUEST}); @@ -186,7 +187,7 @@ export function getPinnedPosts(channelId): ActionFunc { }; } -export function clearPinnedPosts(channelId): ActionFunc { +export function clearPinnedPosts(channelId: string): ActionFunc { return async (dispatch) => { dispatch({ type: SearchTypes.REMOVE_SEARCH_PINNED_POSTS, @@ -245,7 +246,7 @@ export function getRecentMentions(): ActionFunc { }; } -export function removeSearchTerms(teamId, terms): ActionFunc { +export function removeSearchTerms(teamId: string, terms: string): ActionFunc { return async (dispatch) => { dispatch({ type: SearchTypes.REMOVE_SEARCH_TERM, diff --git a/src/actions/teams.ts b/src/actions/teams.ts index be32df7e6..b03d4bee3 100644 --- a/src/actions/teams.ts +++ b/src/actions/teams.ts @@ -13,7 +13,7 @@ import {getCurrentUserId} from 'selectors/entities/users'; import {GetStateFunc, DispatchFunc, ActionFunc, ActionResult, batchActions, Action} from 'types/actions'; -import {Team} from 'types/teams'; +import {Team, TeamMembership} from 'types/teams'; import {selectChannel} from './channels'; import {logError} from './errors'; @@ -22,7 +22,7 @@ import {getProfilesByIds, getStatusesByIds} from './users'; import {loadRolesIfNeeded} from './roles'; import {UserProfile} from 'types/users'; -async function getProfilesAndStatusesForMembers(userIds, dispatch, getState) { +async function getProfilesAndStatusesForMembers(userIds: string[], dispatch: DispatchFunc, getState: GetStateFunc) { const { currentUserId, profiles, @@ -423,7 +423,7 @@ export function addUsersToTeam(teamId: string, userIds: Array): ActionFu } const profiles: Partial[] = []; - members.forEach((m) => profiles.push({id: m.user_id})); + members.forEach((m: TeamMembership) => profiles.push({id: m.user_id})); dispatch(batchActions([ { @@ -495,7 +495,7 @@ export function removeUserFromTeam(teamId: string, userId: string): ActionFunc { }; } -export function updateTeamMemberRoles(teamId: string, userId: string, roles: string): ActionFunc { +export function updateTeamMemberRoles(teamId: string, userId: string, roles: string[]): ActionFunc { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { try { await Client4.updateTeamMemberRoles(teamId, userId, roles); diff --git a/src/actions/users.ts b/src/actions/users.ts index 1987ca654..63e5bd631 100644 --- a/src/actions/users.ts +++ b/src/actions/users.ts @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import {Action, ActionFunc, ActionResult, batchActions, DispatchFunc, GetStateFunc} from 'types/actions'; -import {UserProfile} from 'types/users'; +import {UserProfile, UserStatus} from 'types/users'; import {TeamMembership} from 'types/teams'; import {Client4} from 'client'; import {General} from '../constants'; @@ -21,6 +21,7 @@ import {getCurrentUserId, getUsers} from 'selectors/entities/users'; import {logError} from './errors'; import {bindClientFunc, forceLogoutIfNecessary, debounce} from './helpers'; import {getMyPreferences, makeDirectChannelVisibleIfNecessary, makeGroupMessageVisibleIfNecessary} from './preferences'; +import {Dictionary} from 'types/utilities'; export function checkMfa(loginId: string): ActionFunc { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { dispatch({type: UserTypes.CHECK_MFA_REQUEST, data: null}, getState); @@ -314,7 +315,7 @@ export function getMissingProfilesByUsernames(usernames: Array): ActionF const usernameProfiles = Object.values(profiles).reduce((acc, profile: any) => { acc[profile.username] = profile; return acc; - }, {}); + }, {} as Dictionary); const missingUsernames: string[] = []; usernames.forEach((username) => { if (!usernameProfiles[username]) { @@ -709,7 +710,7 @@ export function getStatus(userId: string): ActionFunc { }); } -export function setStatus(status: string): ActionFunc { +export function setStatus(status: UserStatus): ActionFunc { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { try { await Client4.updateStatus(status); @@ -961,7 +962,7 @@ export function searchProfiles(term: string, options: any = {}): ActionFunc { }; } -let statusIntervalId; +let statusIntervalId: NodeJS.Timeout|null; export function startPeriodicStatusUpdates(): ActionFunc { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { if (statusIntervalId) { diff --git a/src/actions/websocket.ts b/src/actions/websocket.ts index b25f77f9e..7b9f8d0b8 100644 --- a/src/actions/websocket.ts +++ b/src/actions/websocket.ts @@ -22,7 +22,10 @@ import {getTeam, getMyTeamUnreads, getMyTeams, getMyTeamMembers} from './teams'; import {getPost, getPosts, getProfilesAndStatusesForPosts, getCustomEmojiForReaction, handleNewPost, postDeleted, receivedPost} from './posts'; import {fetchMyChannelsAndMembers, getChannelAndMyMember, getChannelStats, markChannelAsRead} from './channels'; import {checkForModifiedUsers, getMe, getProfilesByIds, getStatusesByIds, loadProfilesForDirect} from './users'; -let doDispatch; +import {ChannelMembership} from 'types/channels'; +import {Dictionary} from 'types/utilities'; +import {PreferenceType} from 'types/preferences'; +let doDispatch: DispatchFunc; export function init(platform: PlatformType, siteUrl: string | undefined | null, token: string | undefined | null, optionalWebSocket: any, additionalOptions: any = {}) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const config = getConfig(getState()); @@ -138,7 +141,7 @@ export function doReconnect(now: number) { dispatch(loadProfilesForDirect()); if (data && data.members) { - const stillMemberOfCurrentChannel = data.members.find((m) => m.channel_id === currentChannelId); + const stillMemberOfCurrentChannel = data.members.find((m: ChannelMembership) => m.channel_id === currentChannelId); if (!stillMemberOfCurrentChannel) { EventEmitter.emit(General.SWITCH_TO_DEFAULT_CHANNEL, currentTeamId); @@ -186,7 +189,7 @@ function handleReconnect() { doDispatch(doReconnect(Date.now())); } -function handleClose(connectFailCount) { +function handleClose(connectFailCount: number) { doDispatch({ type: GeneralTypes.WEBSOCKET_FAILURE, error: connectFailCount, @@ -195,7 +198,21 @@ function handleClose(connectFailCount) { }); } -function handleEvent(msg) { +export type WebsocketBroadcast = { + omit_users: Dictionary; + user_id: string; + channel_id: string; + team_id: string; +} + +export type WebSocketMessage = { + event: string; + data: any; + broadcast: WebsocketBroadcast; + seq: number; +} + +function handleEvent(msg: WebSocketMessage) { switch (msg.event) { case WebsocketEvents.POSTED: case WebsocketEvents.EPHEMERAL_MESSAGE: @@ -297,7 +314,7 @@ function handleEvent(msg) { } } -function handleNewPostEvent(msg) { +function handleNewPostEvent(msg: WebSocketMessage) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const post = JSON.parse(msg.data.post); @@ -314,7 +331,7 @@ function handleNewPostEvent(msg) { }; } -function handlePostEdited(msg) { +function handlePostEdited(msg: WebSocketMessage) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const data = JSON.parse(msg.data.post); @@ -324,13 +341,13 @@ function handlePostEdited(msg) { }; } -function handlePostDeleted(msg) { +function handlePostDeleted(msg: WebSocketMessage) { const data = JSON.parse(msg.data.post); return postDeleted(data); } -function handleLeaveTeamEvent(msg) { +function handleLeaveTeamEvent(msg: Partial) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const state = getState(); const teams = getTeamsSelector(state); @@ -349,15 +366,15 @@ function handleLeaveTeamEvent(msg) { }; } -function handleUpdateTeamEvent(msg) { +function handleUpdateTeamEvent(msg: WebSocketMessage) { return { type: TeamTypes.UPDATED_TEAM, data: JSON.parse(msg.data.team), }; } -function handleTeamAddedEvent(msg) { - return async (dispatch) => { +function handleTeamAddedEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc) => { await Promise.all([ dispatch(getTeam(msg.data.team_id)), dispatch(getMyTeamUnreads()), @@ -366,7 +383,7 @@ function handleTeamAddedEvent(msg) { }; } -function handleUserAddedEvent(msg) { +function handleUserAddedEvent(msg: WebSocketMessage) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const state = getState(); const currentChannelId = getCurrentChannelId(state); @@ -393,7 +410,7 @@ function handleUserAddedEvent(msg) { }; } -function handleUserRemovedEvent(msg) { +function handleUserRemovedEvent(msg: WebSocketMessage) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const state = getState(); const channels = getAllChannels(state); @@ -437,7 +454,7 @@ function handleUserRemovedEvent(msg) { }; } -function handleUserUpdatedEvent(msg) { +function handleUserUpdatedEvent(msg: WebSocketMessage) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const currentUser = getCurrentUser(getState()); const user = msg.data.user; @@ -460,7 +477,7 @@ function handleUserUpdatedEvent(msg) { }; } -function handleRoleAddedEvent(msg) { +function handleRoleAddedEvent(msg: WebSocketMessage) { const role = JSON.parse(msg.data.role); return { @@ -469,7 +486,7 @@ function handleRoleAddedEvent(msg) { }; } -function handleRoleRemovedEvent(msg) { +function handleRoleRemovedEvent(msg: WebSocketMessage) { const role = JSON.parse(msg.data.role); return { @@ -478,7 +495,7 @@ function handleRoleRemovedEvent(msg) { }; } -function handleRoleUpdatedEvent(msg) { +function handleRoleUpdatedEvent(msg: WebSocketMessage) { const role = JSON.parse(msg.data.role); return { @@ -487,7 +504,7 @@ function handleRoleUpdatedEvent(msg) { }; } -function handleChannelCreatedEvent(msg) { +function handleChannelCreatedEvent(msg: WebSocketMessage) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const {channel_id: channelId, team_id: teamId} = msg.data; const state = getState(); @@ -501,7 +518,7 @@ function handleChannelCreatedEvent(msg) { }; } -function handleChannelDeletedEvent(msg) { +function handleChannelDeletedEvent(msg: WebSocketMessage) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const state = getState(); const currentChannelId = getCurrentChannelId(state); @@ -527,7 +544,7 @@ function handleChannelDeletedEvent(msg) { }; } -function handleChannelUpdatedEvent(msg) { +function handleChannelUpdatedEvent(msg: WebSocketMessage) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { let channel; try { @@ -555,7 +572,7 @@ function handleChannelUpdatedEvent(msg) { // handleChannelConvertedEvent handles updating of channel which is converted from public to private -function handleChannelConvertedEvent(msg) { +function handleChannelConvertedEvent(msg: WebSocketMessage) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const channelId = msg.data.channel_id; if (channelId) { @@ -571,7 +588,7 @@ function handleChannelConvertedEvent(msg) { }; } -function handleChannelViewedEvent(msg) { +function handleChannelViewedEvent(msg: WebSocketMessage) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const state = getState(); const {channel_id: channelId} = msg.data; @@ -585,7 +602,7 @@ function handleChannelViewedEvent(msg) { }; } -function handleChannelMemberUpdatedEvent(msg) { +function handleChannelMemberUpdatedEvent(msg: WebSocketMessage) { const channelMember = JSON.parse(msg.data.channelMember); return { @@ -594,15 +611,15 @@ function handleChannelMemberUpdatedEvent(msg) { }; } -function handleDirectAddedEvent(msg) { - return (dispatch) => { +function handleDirectAddedEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc) => { dispatch(getChannelAndMyMember(msg.broadcast.channel_id)); return {data: true}; }; } -function handlePreferenceChangedEvent(msg) { - return (dispatch) => { +function handlePreferenceChangedEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc) => { const preference = JSON.parse(msg.data.preference); dispatch({type: PreferenceTypes.RECEIVED_PREFERENCES, data: [preference]}); @@ -611,9 +628,9 @@ function handlePreferenceChangedEvent(msg) { }; } -function handlePreferencesChangedEvent(msg) { +function handlePreferencesChangedEvent(msg: WebSocketMessage) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { - const preferences = JSON.parse(msg.data.preferences); + const preferences = JSON.parse(msg.data.preferences) as PreferenceType[]; const posts = getAllPosts(getState()); preferences.forEach((pref) => { @@ -628,20 +645,20 @@ function handlePreferencesChangedEvent(msg) { }; } -function handlePreferencesDeletedEvent(msg) { +function handlePreferencesDeletedEvent(msg: WebSocketMessage) { const preferences = JSON.parse(msg.data.preferences); return {type: PreferenceTypes.DELETED_PREFERENCES, data: preferences}; } -function handleStatusChangedEvent(msg) { +function handleStatusChangedEvent(msg: WebSocketMessage) { return { type: UserTypes.RECEIVED_STATUSES, data: [{user_id: msg.data.user_id, status: msg.data.status}], }; } -function handleHelloEvent(msg) { +function handleHelloEvent(msg: WebSocketMessage) { const serverVersion = msg.data.server_version; if (serverVersion && Client4.serverVersion !== serverVersion) { Client4.serverVersion = serverVersion; @@ -649,7 +666,7 @@ function handleHelloEvent(msg) { } } -function handleUserTypingEvent(msg) { +function handleUserTypingEvent(msg: WebSocketMessage) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const state = getState(); const profiles = getUsers(state); @@ -674,7 +691,7 @@ function handleUserTypingEvent(msg) { type: WebsocketEvents.STOP_TYPING, data, }); - }, parseInt(config.TimeBetweenUserTypingUpdatesMilliseconds, 10)); + }, parseInt(config.TimeBetweenUserTypingUpdatesMilliseconds!, 10)); if (!profiles[userId] && userId !== currentUserId) { dispatch(getProfilesByIds([userId])); @@ -688,8 +705,8 @@ function handleUserTypingEvent(msg) { }; } -function handleReactionAddedEvent(msg) { - return (dispatch) => { +function handleReactionAddedEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc) => { const {data} = msg; const reaction = JSON.parse(data.reaction); @@ -703,7 +720,7 @@ function handleReactionAddedEvent(msg) { }; } -function handleReactionRemovedEvent(msg) { +function handleReactionRemovedEvent(msg: WebSocketMessage) { const {data} = msg; const reaction = JSON.parse(data.reaction); @@ -713,7 +730,7 @@ function handleReactionRemovedEvent(msg) { }; } -function handleAddEmoji(msg) { +function handleAddEmoji(msg: WebSocketMessage) { const data = JSON.parse(msg.data.emoji); return { @@ -722,7 +739,7 @@ function handleAddEmoji(msg) { }; } -function handleLicenseChangedEvent(msg) { +function handleLicenseChangedEvent(msg: WebSocketMessage) { const data = msg.data.license; return { @@ -731,7 +748,7 @@ function handleLicenseChangedEvent(msg) { }; } -function handleConfigChangedEvent(msg) { +function handleConfigChangedEvent(msg: WebSocketMessage) { const data = msg.data.config; EventEmitter.emit(General.CONFIG_CHANGED, data); @@ -741,7 +758,7 @@ function handleConfigChangedEvent(msg) { }; } -function handlePluginStatusesChangedEvent(msg) { +function handlePluginStatusesChangedEvent(msg: WebSocketMessage) { const data = msg.data; return { @@ -750,8 +767,8 @@ function handlePluginStatusesChangedEvent(msg) { }; } -function handleOpenDialogEvent(msg) { - return (dispatch) => { +function handleOpenDialogEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc) => { const data = (msg.data && msg.data.dialog) || {}; dispatch({type: IntegrationTypes.RECEIVED_DIALOG, data: JSON.parse(data)}); return {data: true}; @@ -759,7 +776,7 @@ function handleOpenDialogEvent(msg) { } // Helpers -function getAddedDmUsersIfNecessary(preferences) { +function getAddedDmUsersIfNecessary(preferences: PreferenceType[]) { return (dispatch: DispatchFunc, getState: GetStateFunc) => { const userIds: string[] = []; @@ -811,8 +828,8 @@ export function userTyping(channelId: string, parentPostId: string): ActionFunc const stats = getCurrentChannelStats(state); const membersInChannel = stats ? stats.member_count : 0; - if (((t - lastTimeTypingSent) > config.TimeBetweenUserTypingUpdatesMilliseconds) && - (membersInChannel < config.MaxNotificationsPerChannel) && (config.EnableUserTypingMessages === 'true')) { + if (((t - lastTimeTypingSent) > parseInt(config.TimeBetweenUserTypingUpdatesMilliseconds!, 10)) && + (membersInChannel < parseInt(config.MaxNotificationsPerChannel!, 10)) && (config.EnableUserTypingMessages === 'true')) { websocketClient.userTyping(channelId, parentPostId); lastTimeTypingSent = t; } diff --git a/src/client/client4.ts b/src/client/client4.ts index f56a18f15..7e5c05301 100644 --- a/src/client/client4.ts +++ b/src/client/client4.ts @@ -8,7 +8,7 @@ import {General} from '../constants'; import {isSystemAdmin} from 'utils/user_utils'; import fetch from './fetch_etag'; -import {UserProfile} from 'types/users'; +import {UserProfile, UserStatus} from 'types/users'; import {Team} from 'types/teams'; import {Channel} from 'types/channels'; import {Post} from 'types/posts'; @@ -16,6 +16,13 @@ import {Job} from 'types/jobs'; import {Role} from 'types/roles'; import {Scheme} from 'types/schemes'; import {Options} from 'types/client4'; +import {PreferenceType} from 'types/preferences'; +import {IncomingWebhook, OutgoingWebhook, Command, OAuthApp, DialogSubmission} from 'types/integrations'; +import {CustomEmoji} from 'types/emojis'; +import {Config} from 'types/config'; +import {Bot, BotPatch} from 'types/bots'; +import {Dictionary} from 'types/utilities'; +import {SyncablePatch} from 'types/groups'; const FormData = require('form-data'); const HEADER_AUTH = 'Authorization'; @@ -238,7 +245,7 @@ export default class Client4 { return `${this.getBaseRoute()}/brand`; } - getBrandImageUrl(timestamp) { + getBrandImageUrl(timestamp: string) { return `${this.getBrandRoute()}/image?t=${timestamp}`; } @@ -789,7 +796,7 @@ export default class Client4 { ); }; - searchUsers = (term: string, options) => { + searchUsers = (term: string, options: any) => { this.trackEvent('api', 'api_search_users'); return this.doFetch( @@ -812,7 +819,7 @@ export default class Client4 { ); }; - updateStatus = async (status) => { + updateStatus = async (status: UserStatus) => { return this.doFetch( `${this.getUserRoute(status.user_id)}/status`, {method: 'put', body: JSON.stringify(status)} @@ -1158,7 +1165,7 @@ export default class Client4 { ); }; - updateTeamMemberRoles = async (teamId: string, userId: string, roles) => { + updateTeamMemberRoles = async (teamId: string, userId: string, roles: string[]) => { this.trackEvent('api', 'api_teams_update_member_roles', {team_id: teamId}); return this.doFetch( @@ -1185,7 +1192,7 @@ export default class Client4 { ); }; - importTeam = async (teamId: string, file, importFrom) => { + importTeam = async (teamId: string, file: File, importFrom: string) => { const formData = new FormData(); formData.append('file', file, file.name); formData.append('filesize', file.size); @@ -1327,7 +1334,7 @@ export default class Client4 { ); }; - updateChannelPrivacy = async (channelId: string, privacy) => { + updateChannelPrivacy = async (channelId: string, privacy: any) => { this.trackEvent('api', 'api_channels_update_privacy', {channel_id: channelId, privacy}); return this.doFetch( @@ -1345,7 +1352,7 @@ export default class Client4 { ); }; - updateChannelNotifyProps = async (props) => { + updateChannelNotifyProps = async (props: any) => { this.trackEvent('api', 'api_users_update_channel_notifcations', {channel_id: props.channel_id}); return this.doFetch( @@ -1465,7 +1472,7 @@ export default class Client4 { ); }; - updateChannelMemberRoles = async (channelId: string, userId: string, roles) => { + updateChannelMemberRoles = async (channelId: string, userId: string, roles: string) => { return this.doFetch( `${this.getChannelMemberRoute(channelId, userId)}/roles`, {method: 'put', body: JSON.stringify({roles})} @@ -1697,7 +1704,7 @@ export default class Client4 { ); }; - searchPostsWithParams = async (teamId: string, params) => { + searchPostsWithParams = async (teamId: string, params: any) => { this.trackEvent('api', 'api_posts_search', {team_id: teamId}); return this.doFetch( @@ -1721,7 +1728,7 @@ export default class Client4 { return this.doPostActionWithCookie(postId, actionId, '', selectedOption); }; - doPostActionWithCookie = async (postId: string, actionId: string, actionCookie, selectedOption = '') => { + doPostActionWithCookie = async (postId: string, actionId: string, actionCookie: string, selectedOption = '') => { if (selectedOption) { this.trackEvent('api', 'api_interactive_messages_menu_selected'); } else { @@ -1769,7 +1776,7 @@ export default class Client4 { return url; } - uploadFile = async (fileFormData, formBoundary) => { + uploadFile = async (fileFormData: any, formBoundary: string) => { this.trackEvent('api', 'api_files_upload'); const request: any = { method: 'post', @@ -1797,7 +1804,7 @@ export default class Client4 { // Preference Routes - savePreferences = async (userId: string, preferences) => { + savePreferences = async (userId: string, preferences: PreferenceType[]) => { return this.doFetch( `${this.getPreferencesRoute(userId)}`, {method: 'put', body: JSON.stringify(preferences)} @@ -1811,7 +1818,7 @@ export default class Client4 { ); }; - deletePreferences = async (userId: string, preferences) => { + deletePreferences = async (userId: string, preferences: PreferenceType[]) => { return this.doFetch( `${this.getPreferencesRoute(userId)}/delete`, {method: 'post', body: JSON.stringify(preferences)} @@ -1877,7 +1884,7 @@ export default class Client4 { // Integration Routes - createIncomingWebhook = async (hook) => { + createIncomingWebhook = async (hook: IncomingWebhook) => { this.trackEvent('api', 'api_integrations_created', {team_id: hook.team_id}); return this.doFetch( @@ -1918,7 +1925,7 @@ export default class Client4 { ); }; - updateIncomingWebhook = async (hook) => { + updateIncomingWebhook = async (hook: IncomingWebhook) => { this.trackEvent('api', 'api_integrations_updated', {team_id: hook.team_id}); return this.doFetch( @@ -1927,7 +1934,7 @@ export default class Client4 { ); }; - createOutgoingWebhook = async (hook) => { + createOutgoingWebhook = async (hook: OutgoingWebhook) => { this.trackEvent('api', 'api_integrations_created', {team_id: hook.team_id}); return this.doFetch( @@ -1972,7 +1979,7 @@ export default class Client4 { ); }; - updateOutgoingWebhook = async (hook) => { + updateOutgoingWebhook = async (hook: OutgoingWebhook) => { this.trackEvent('api', 'api_integrations_updated', {team_id: hook.team_id}); return this.doFetch( @@ -2009,7 +2016,7 @@ export default class Client4 { ); }; - executeCommand = async (command, commandArgs = {}) => { + executeCommand = async (command: Command, commandArgs = {}) => { this.trackEvent('api', 'api_integrations_used'); return this.doFetch( @@ -2018,7 +2025,7 @@ export default class Client4 { ); }; - addCommand = async (command) => { + addCommand = async (command: Command) => { this.trackEvent('api', 'api_integrations_created'); return this.doFetch( @@ -2027,7 +2034,7 @@ export default class Client4 { ); }; - editCommand = async (command) => { + editCommand = async (command: Command) => { this.trackEvent('api', 'api_integrations_created'); return this.doFetch( @@ -2052,7 +2059,7 @@ export default class Client4 { ); }; - createOAuthApp = async (app) => { + createOAuthApp = async (app: OAuthApp) => { this.trackEvent('api', 'api_apps_register'); return this.doFetch( @@ -2061,7 +2068,7 @@ export default class Client4 { ); }; - editOAuthApp = async (app) => { + editOAuthApp = async (app: OAuthApp) => { return this.doFetch( `${this.getOAuthAppsRoute()}/${app.id}`, {method: 'put', body: JSON.stringify(app)} @@ -2105,7 +2112,7 @@ export default class Client4 { ); }; - submitInteractiveDialog = async (data) => { + submitInteractiveDialog = async (data: DialogSubmission) => { this.trackEvent('api', 'api_interactive_messages_dialog_submitted'); return this.doFetch( `${this.getBaseRoute()}/actions/dialogs/submit`, @@ -2115,7 +2122,7 @@ export default class Client4 { // Emoji Routes - createCustomEmoji = async (emoji, imageData: File) => { + createCustomEmoji = async (emoji: CustomEmoji, imageData: File) => { this.trackEvent('api', 'api_emoji_custom_add'); const formData = new FormData(); @@ -2268,7 +2275,7 @@ export default class Client4 { ); }; - updateConfig = async (config) => { + updateConfig = async (config: Config) => { return this.doFetch( `${this.getBaseRoute()}/config`, {method: 'put', body: JSON.stringify(config)} @@ -2289,7 +2296,7 @@ export default class Client4 { ); }; - testEmail = async (config) => { + testEmail = async (config: Config) => { return this.doFetch( `${this.getBaseRoute()}/email/test`, {method: 'post', body: JSON.stringify(config)} @@ -2303,7 +2310,7 @@ export default class Client4 { ); }; - testS3Connection = async (config) => { + testS3Connection = async (config: Config) => { return this.doFetch( `${this.getBaseRoute()}/file/s3_test`, {method: 'post', body: JSON.stringify(config)} @@ -2482,7 +2489,7 @@ export default class Client4 { ); }; - testElasticsearch = async (config) => { + testElasticsearch = async (config: Config) => { return this.doFetch( `${this.getBaseRoute()}/elasticsearch/test`, {method: 'post', body: JSON.stringify(config)} @@ -2666,7 +2673,7 @@ export default class Client4 { ); }; - getMarketplacePlugins = async (filter) => { + getMarketplacePlugins = async (filter: string) => { return this.doFetch( `${this.getPluginsMarketplaceRoute()}${buildQueryString({filter: filter || ''})}`, {method: 'get'} @@ -2710,7 +2717,7 @@ export default class Client4 { // Groups - linkGroupSyncable = async (groupID: string, syncableID: string, syncableType: string, patch) => { + linkGroupSyncable = async (groupID: string, syncableID: string, syncableType: string, patch: SyncablePatch) => { return this.doFetch( `${this.getBaseRoute()}/groups/${groupID}/${syncableType}s/${syncableID}/link`, {method: 'post', body: JSON.stringify(patch)} @@ -2793,7 +2800,7 @@ export default class Client4 { // Redirect Location - getRedirectLocation = async (urlParam) => { + getRedirectLocation = async (urlParam: string) => { if (!urlParam.length) { return Promise.resolve(); } @@ -2803,14 +2810,14 @@ export default class Client4 { // Bot Routes - createBot = async (bot) => { + createBot = async (bot: Bot) => { return this.doFetch( `${this.getBotsRoute()}`, {method: 'post', body: JSON.stringify(bot)} ); } - patchBot = async (botUserId: string, botPatch) => { + patchBot = async (botUserId: string, botPatch: BotPatch) => { return this.doFetch( `${this.getBotRoute(botUserId)}`, {method: 'put', body: JSON.stringify(botPatch)} @@ -2884,13 +2891,13 @@ export default class Client4 { // Client Helpers - doFetch = async (url: string, options) => { + doFetch = async (url: string, options: Options) => { const {data} = await this.doFetchWithResponse(url, options); return data; }; - doFetchWithResponse = async (url: string, options) => { + doFetchWithResponse = async (url: string, options: Options) => { const response = await fetch(url, this.getOptions(options)); const headers = parseAndMergeNestedHeaders(response.headers); @@ -2944,7 +2951,7 @@ export default class Client4 { }); }; - trackEvent(category: string, event: string, props?) { + trackEvent(category: string, event: string, props?: any) { // Temporary change to allow only certain events to reduce data rate - see MM-13062 if (![ 'api_posts_create', @@ -2995,7 +3002,7 @@ export default class Client4 { } } -function parseAndMergeNestedHeaders(originalHeaders) { +function parseAndMergeNestedHeaders(originalHeaders: any) { const headers = new Map(); let nestedHeaders = new Map(); originalHeaders.forEach((val: string, key: string) => { @@ -3019,7 +3026,7 @@ export class ClientError extends Error { intl: { defaultMessage: string; id: string } | { defaultMessage: string; id: string } | { id: string; defaultMessage: string; values: any } | { id: string; defaultMessage: string }; server_error_id: any; status_code: any; - constructor(baseUrl: string, data) { + constructor(baseUrl: string, data: any) { super(data.message + ': ' + cleanUrlForLogging(baseUrl, data.url)); this.message = data.message; diff --git a/src/client/fetch_etag.ts b/src/client/fetch_etag.ts index a4c178fd4..7d9f2bd1e 100644 --- a/src/client/fetch_etag.ts +++ b/src/client/fetch_etag.ts @@ -11,7 +11,7 @@ export default ((url?: string, options: Options = {headers: {}}) => { const etag = etags[url!]; const cachedResponse = data[`${url}${etag}`]; // ensure etag is for url if (etag) { - options.headers['If-None-Match'] = etag; + options.headers!['If-None-Match'] = etag; } return fetch(url!, options). diff --git a/src/client/websocket_client.ts b/src/client/websocket_client.ts index 278cbfb89..a58f0f483 100644 --- a/src/client/websocket_client.ts +++ b/src/client/websocket_client.ts @@ -5,12 +5,12 @@ const MIN_WEBSOCKET_RETRY_TIME = 3000; // 3 sec const MAX_WEBSOCKET_RETRY_TIME = 300000; // 5 mins -let Socket; +let Socket: any; class WebSocketClient { conn?: WebSocket; connectionUrl: null; - token: null; + token: string|null; sequence: number; connectFailCount: number; eventCallback?: Function; @@ -32,7 +32,7 @@ class WebSocketClient { this.platform = ''; } - initialize(token, opts) { + initialize(token: string|null, opts: any) { const defaults = { forceConnection: true, connectionUrl: this.connectionUrl, @@ -182,27 +182,27 @@ class WebSocketClient { }); } - setConnectingCallback(callback) { + setConnectingCallback(callback: Function) { this.connectingCallback = callback; } - setEventCallback(callback) { + setEventCallback(callback: Function) { this.eventCallback = callback; } - setFirstConnectCallback(callback) { + setFirstConnectCallback(callback: Function) { this.firstConnectCallback = callback; } - setReconnectCallback(callback) { + setReconnectCallback(callback: Function) { this.reconnectCallback = callback; } - setErrorCallback(callback) { + setErrorCallback(callback: Function) { this.errorCallback = callback; } - setCloseCallback(callback) { + setCloseCallback(callback: Function) { this.closeCallback = callback; } @@ -218,7 +218,7 @@ class WebSocketClient { } } - sendMessage(action, data) { + sendMessage(action: string, data: any) { const msg = { action, seq: this.sequence++, @@ -233,7 +233,7 @@ class WebSocketClient { } } - userTyping(channelId, parentId) { + userTyping(channelId: string, parentId: string) { this.sendMessage('user_typing', { channel_id: channelId, parent_id: parentId, @@ -244,7 +244,7 @@ class WebSocketClient { this.sendMessage('get_statuses', null); } - getStatusesByIds(userIds) { + getStatusesByIds(userIds: string[]) { this.sendMessage('get_statuses_by_ids', { user_ids: userIds, }); diff --git a/src/constants/files.ts b/src/constants/files.ts index d5e860d9e..acc2a3dd5 100644 --- a/src/constants/files.ts +++ b/src/constants/files.ts @@ -1,6 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -export default { + +import {Dictionary} from 'types/utilities'; + +const Files: Dictionary = { AUDIO_TYPES: ['mp3', 'wav', 'wma', 'm4a', 'flac', 'aac', 'ogg'], CODE_TYPES: ['as', 'applescript', 'osascript', 'scpt', 'bash', 'sh', 'zsh', 'clj', 'boot', 'cl2', 'cljc', 'cljs', 'cljs.hl', 'cljscm', 'cljx', 'hic', 'coffee', '_coffee', 'cake', 'cjsx', 'cson', 'iced', 'cpp', 'c', 'cc', 'h', 'c++', 'h++', 'hpp', 'cs', 'csharp', 'css', 'd', 'di', 'dart', 'delphi', 'dpr', 'dfm', 'pas', 'pascal', 'freepascal', 'lazarus', 'lpr', 'lfm', 'diff', 'django', 'jinja', 'dockerfile', 'docker', 'erl', 'f90', 'f95', 'fsharp', 'fs', 'gcode', 'nc', 'go', 'groovy', 'handlebars', 'hbs', 'html.hbs', 'html.handlebars', 'hs', 'hx', 'java', 'jsp', 'js', 'jsx', 'json', 'jl', 'kt', 'ktm', 'kts', 'less', 'lisp', 'lua', 'mk', 'mak', 'md', 'mkdown', 'mkd', 'matlab', 'm', 'mm', 'objc', 'obj-c', 'ml', 'perl', 'pl', 'php', 'php3', 'php4', 'php5', 'php6', 'ps', 'ps1', 'pp', 'py', 'gyp', 'r', 'ruby', 'rb', 'gemspec', 'podspec', 'thor', 'irb', 'rs', 'scala', 'scm', 'sld', 'scss', 'st', 'sql', 'swift', 'tex', 'txt', 'vbnet', 'vb', 'bas', 'vbs', 'v', 'veo', 'xml', 'html', 'xhtml', 'rss', 'atom', 'xsl', 'plist', 'yaml'], IMAGE_TYPES: ['jpg', 'gif', 'bmp', 'png', 'jpeg', 'tiff', 'tif'], @@ -11,3 +14,5 @@ export default { VIDEO_TYPES: ['mp4', 'avi', 'webm', 'mkv', 'wmv', 'mpg', 'mov', 'flv'], WORD_TYPES: ['doc', 'docx'], }; + +export default Files; \ No newline at end of file diff --git a/src/constants/preferences.ts b/src/constants/preferences.ts index 90fcd08fc..63ae5fa16 100644 --- a/src/constants/preferences.ts +++ b/src/constants/preferences.ts @@ -1,6 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -export default { + +import {Dictionary} from 'types/utilities'; + +const Preferences: Dictionary = { CATEGORY_CHANNEL_OPEN_TIME: 'channel_open_time', CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME: 'channel_approximate_view_time', CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show', @@ -145,3 +148,5 @@ export default { }, }, }; + +export default Preferences; \ No newline at end of file diff --git a/src/reducers/entities/admin.ts b/src/reducers/entities/admin.ts index 14f9955cd..42cc49233 100644 --- a/src/reducers/entities/admin.ts +++ b/src/reducers/entities/admin.ts @@ -5,8 +5,9 @@ import {combineReducers} from 'redux'; import {AdminTypes, UserTypes} from 'action_types'; import {Stats, Plugins} from '../../constants'; import PluginState from '../../constants/plugins'; +import {GenericAction} from 'types/actions'; -function logs(state = [], action) { +function logs(state = [], action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_LOGS: { return action.data; @@ -19,7 +20,7 @@ function logs(state = [], action) { } } -function audits(state = {}, action) { +function audits(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_AUDITS: { const nextState = {...state}; @@ -36,7 +37,7 @@ function audits(state = {}, action) { } } -function config(state: any = {}, action) { +function config(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_CONFIG: { return action.data; @@ -63,7 +64,7 @@ function config(state: any = {}, action) { } } -function environmentConfig(state = {}, action) { +function environmentConfig(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_ENVIRONMENT_CONFIG: { return action.data; @@ -76,7 +77,7 @@ function environmentConfig(state = {}, action) { } } -function complianceReports(state = {}, action) { +function complianceReports(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_COMPLIANCE_REPORT: { const nextState = {...state}; @@ -98,7 +99,7 @@ function complianceReports(state = {}, action) { } } -function clusterInfo(state = [], action) { +function clusterInfo(state = [], action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_CLUSTER_STATUS: { return action.data; @@ -111,7 +112,7 @@ function clusterInfo(state = [], action) { } } -function samlCertStatus(state = {}, action) { +function samlCertStatus(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_SAML_CERT_STATUS: { return action.data; @@ -124,8 +125,8 @@ function samlCertStatus(state = {}, action) { } } -export function convertAnalyticsRowsToStats(data, name) { - const stats = {}; +export function convertAnalyticsRowsToStats(data: any, name: string) { + const stats: any = {}; const clonedData = [...data]; if (name === 'post_counts_day') { @@ -210,7 +211,7 @@ export function convertAnalyticsRowsToStats(data, name) { return stats; } -function analytics(state = {}, action) { +function analytics(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_SYSTEM_ANALYTICS: { const stats = convertAnalyticsRowsToStats(action.data, action.name); @@ -224,7 +225,7 @@ function analytics(state = {}, action) { } } -function teamAnalytics(state = {}, action) { +function teamAnalytics(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_TEAM_ANALYTICS: { const nextState = {...state}; @@ -241,13 +242,13 @@ function teamAnalytics(state = {}, action) { } } -function userAccessTokens(state = {}, action) { +function userAccessTokens(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_USER_ACCESS_TOKEN: { return {...state, [action.data.id]: action.data}; } case AdminTypes.RECEIVED_USER_ACCESS_TOKENS_FOR_USER: { - const nextState = {}; + const nextState: any = {}; for (const uat of action.data) { nextState[uat.id] = uat; @@ -256,7 +257,7 @@ function userAccessTokens(state = {}, action) { return {...state, ...nextState}; } case AdminTypes.RECEIVED_USER_ACCESS_TOKENS: { - const nextState = {}; + const nextState: any = {}; for (const uat of action.data) { nextState[uat.id] = uat; @@ -284,7 +285,7 @@ function userAccessTokens(state = {}, action) { } } -function userAccessTokensForUser(state = {}, action) { +function userAccessTokensForUser(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_USER_ACCESS_TOKEN: { const nextUserState = {...(state[action.data.user_id] || {})}; @@ -302,7 +303,7 @@ function userAccessTokensForUser(state = {}, action) { return {...state, [action.userId]: nextUserState}; } case AdminTypes.RECEIVED_USER_ACCESS_TOKENS: { - const nextUserState = {}; + const nextUserState: any = {}; for (const uat of action.data) { nextUserState[uat.user_id] = nextUserState[uat.user_id] || {}; @@ -360,7 +361,7 @@ function userAccessTokensForUser(state = {}, action) { } } -function plugins(state = {}, action) { +function plugins(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_PLUGINS: { const nextState = {...state}; @@ -406,10 +407,10 @@ function plugins(state = {}, action) { } } -function pluginStatuses(state = {}, action) { +function pluginStatuses(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_PLUGIN_STATUSES: { - const nextState = {}; + const nextState: any = {}; for (const plugin of (action.data || [])) { const id = plugin.plugin_id; @@ -492,7 +493,7 @@ function pluginStatuses(state = {}, action) { } } -function ldapGroupsCount(state = {}, action) { +function ldapGroupsCount(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_LDAP_GROUPS: return action.data.count; @@ -503,10 +504,10 @@ function ldapGroupsCount(state = {}, action) { } } -function ldapGroups(state = {}, action) { +function ldapGroups(state: any = {}, action: GenericAction) { switch (action.type) { case AdminTypes.RECEIVED_LDAP_GROUPS: { - const nextState = {}; + const nextState: any = {}; for (const group of action.data.groups) { nextState[group.primary_key] = group; } diff --git a/src/reducers/entities/bots.ts b/src/reducers/entities/bots.ts index 9f96f4312..837fb9586 100644 --- a/src/reducers/entities/bots.ts +++ b/src/reducers/entities/bots.ts @@ -2,8 +2,11 @@ // See LICENSE.txt for license information. import {combineReducers} from 'redux'; import {BotTypes, UserTypes} from 'action_types'; +import {GenericAction} from 'types/actions'; +import {Dictionary} from 'types/utilities'; +import {Bot} from 'types/bots'; -function accounts(state = {}, action) { +function accounts(state: Dictionary = {}, action: GenericAction) { switch (action.type) { case BotTypes.RECEIVED_BOT_ACCOUNTS: { const newBots = action.data; diff --git a/src/reducers/entities/channels.ts b/src/reducers/entities/channels.ts index 5f2c668b6..7556d4906 100644 --- a/src/reducers/entities/channels.ts +++ b/src/reducers/entities/channels.ts @@ -3,10 +3,14 @@ import {combineReducers} from 'redux'; import {ChannelTypes, UserTypes, SchemeTypes, GroupTypes} from 'action_types'; import {General} from '../../constants'; +import {GenericAction} from 'types/actions'; +import {Channel, ChannelMembership, ChannelStats} from 'types/channels'; +import {RelationOneToMany, RelationOneToOne, IDMappedObjects, UserIDMappedObjects} from 'types/utilities'; +import {Team} from 'types/teams'; -function channelListToSet(state, action) { +function channelListToSet(state: any, action: GenericAction) { const nextState = {...state}; - action.data.forEach((channel) => { + action.data.forEach((channel: Channel) => { const nextSet = new Set(nextState[channel.team_id]); nextSet.add(channel.id); nextState[channel.team_id] = nextSet; @@ -15,7 +19,7 @@ function channelListToSet(state, action) { return nextState; } -function removeChannelFromSet(state, action) { +function removeChannelFromSet(state: any, action: GenericAction) { const id = action.data.team_id; const nextSet = new Set(state[id]); nextSet.delete(action.data.id); @@ -25,7 +29,7 @@ function removeChannelFromSet(state, action) { }; } -function currentChannelId(state = '', action) { +function currentChannelId(state = '', action: GenericAction) { switch (action.type) { case ChannelTypes.SELECT_CHANNEL: return action.data; @@ -36,7 +40,7 @@ function currentChannelId(state = '', action) { } } -function channels(state = {}, action) { +function channels(state: IDMappedObjects = {}, action: GenericAction) { switch (action.type) { case ChannelTypes.RECEIVED_CHANNEL: if (state[action.data.id] && action.data.type === General.DM_CHANNEL) { @@ -145,7 +149,7 @@ function channels(state = {}, action) { } } -function channelsInTeam(state = {}, action) { +function channelsInTeam(state: RelationOneToMany = {}, action: GenericAction) { switch (action.type) { case ChannelTypes.RECEIVED_CHANNEL: { const nextSet = new Set(state[action.data.team_id]); @@ -171,7 +175,7 @@ function channelsInTeam(state = {}, action) { } } -function myMembers(state = {}, action) { +function myMembers(state: RelationOneToOne = {}, action: GenericAction) { switch (action.type) { case ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER: { const channelMember = action.data; @@ -181,10 +185,10 @@ function myMembers(state = {}, action) { }; } case ChannelTypes.RECEIVED_MY_CHANNEL_MEMBERS: { - const nextState = {...state}; - const remove = action.remove; + const nextState: any = {...state}; + const remove = action.remove as string[]; if (remove) { - remove.forEach((id) => { + remove.forEach((id: string) => { Reflect.deleteProperty(nextState, id); }); } @@ -310,7 +314,7 @@ function myMembers(state = {}, action) { } } -function membersInChannel(state = {}, action) { +function membersInChannel(state: RelationOneToOne> = {}, action: GenericAction) { switch (action.type) { case ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER: case ChannelTypes.RECEIVED_CHANNEL_MEMBER: { @@ -325,7 +329,7 @@ function membersInChannel(state = {}, action) { case ChannelTypes.RECEIVED_MY_CHANNEL_MEMBERS: case ChannelTypes.RECEIVED_CHANNEL_MEMBERS: { const nextState = {...state}; - const remove = action.remove; + const remove = action.remove as string[]; const currentUserId = action.currentUserId; if (remove && currentUserId) { remove.forEach((id) => { @@ -372,7 +376,7 @@ function membersInChannel(state = {}, action) { } } -function stats(state = {}, action) { +function stats(state: RelationOneToOne = {}, action: GenericAction) { switch (action.type) { case ChannelTypes.RECEIVED_CHANNEL_STATS: { const nextState = {...state}; @@ -422,7 +426,7 @@ function stats(state = {}, action) { } } -function groupsAssociatedToChannel(state = {}, action) { +function groupsAssociatedToChannel(state: any = {}, action: GenericAction) { switch (action.type) { case GroupTypes.RECEIVED_GROUPS_ASSOCIATED_TO_CHANNEL: { const {channelID, groups, totalGroupCount} = action.data; @@ -460,7 +464,7 @@ function groupsAssociatedToChannel(state = {}, action) { } } -function updateChannelMemberSchemeRoles(state, action) { +function updateChannelMemberSchemeRoles(state: any, action: GenericAction) { const {channelId, userId, isSchemeUser, isSchemeAdmin} = action.data; const channel = state[channelId]; if (channel) { @@ -482,7 +486,7 @@ function updateChannelMemberSchemeRoles(state, action) { return state; } -function totalCount(state = 0, action) { +function totalCount(state = 0, action: GenericAction) { switch (action.type) { case ChannelTypes.RECEIVED_TOTAL_CHANNEL_COUNT: { return action.data; diff --git a/src/reducers/entities/files.ts b/src/reducers/entities/files.ts index cdda1bc37..bda824d22 100644 --- a/src/reducers/entities/files.ts +++ b/src/reducers/entities/files.ts @@ -3,16 +3,20 @@ import {combineReducers} from 'redux'; import {FileTypes, PostTypes, UserTypes} from 'action_types'; +import {GenericAction} from 'types/actions'; +import {Post} from 'types/posts'; +import {FileInfo} from 'types/files'; +import {Dictionary} from 'types/utilities'; -export function files(state = {}, action) { +export function files(state: Dictionary = {}, action: GenericAction) { switch (action.type) { case FileTypes.RECEIVED_UPLOAD_FILES: case FileTypes.RECEIVED_FILES_FOR_POST: { - const filesById = action.data.reduce((filesMap, file) => { + const filesById = action.data.reduce((filesMap: any, file: any) => { return {...filesMap, [file.id]: file, }; - }, {}); + }, {} as any); return {...state, ...filesById, }; @@ -35,7 +39,7 @@ export function files(state = {}, action) { case PostTypes.POST_REMOVED: { if (action.data && action.data.file_ids && action.data.file_ids.length) { const nextState = {...state}; - const fileIds = action.data.file_ids; + const fileIds = action.data.file_ids as string[]; fileIds.forEach((id) => { Reflect.deleteProperty(nextState, id); }); @@ -53,7 +57,7 @@ export function files(state = {}, action) { } } -function storeFilesForPost(state, post) { +function storeFilesForPost(state: Dictionary, post: Post) { if (!post.metadata || !post.metadata.files) { return state; } @@ -71,13 +75,13 @@ function storeFilesForPost(state, post) { }, state); } -export function fileIdsByPostId(state = {}, action) { +export function fileIdsByPostId(state: Dictionary> = {}, action: GenericAction) { switch (action.type) { case FileTypes.RECEIVED_FILES_FOR_POST: { const {data, postId} = action; - const filesIdsForPost = data.map((file) => file.id); + const filesIdsForPost = data.map((file: FileInfo) => file.id); return {...state, - [postId]: filesIdsForPost, + [postId as string]: filesIdsForPost, }; } @@ -112,7 +116,7 @@ export function fileIdsByPostId(state = {}, action) { } } -function storeFilesIdsForPost(state, post) { +function storeFilesIdsForPost(state: Dictionary, post: Post) { if (!post.metadata || !post.metadata.files) { return state; } @@ -123,7 +127,7 @@ function storeFilesIdsForPost(state, post) { }; } -function filePublicLink(state = {}, action) { +function filePublicLink(state: string | null = null, action: GenericAction) { switch (action.type) { case FileTypes.RECEIVED_FILE_PUBLIC_LINK: { return action.data; diff --git a/src/reducers/entities/general.ts b/src/reducers/entities/general.ts index dbe026a77..1aacc73f1 100644 --- a/src/reducers/entities/general.ts +++ b/src/reducers/entities/general.ts @@ -3,8 +3,10 @@ import {combineReducers} from 'redux'; import {GeneralTypes, UserTypes} from 'action_types'; +import {GenericAction} from 'types/actions'; +import {Config} from 'types/config'; -function config(state = {}, action) { +function config(state: Partial = {}, action: GenericAction) { switch (action.type) { case GeneralTypes.CLIENT_CONFIG_RECEIVED: return Object.assign({}, state, action.data); @@ -16,7 +18,7 @@ function config(state = {}, action) { } } -function appState(state = false, action) { +function appState(state = false, action: GenericAction) { switch (action.type) { case GeneralTypes.RECEIVED_APP_STATE: return action.data; @@ -26,7 +28,7 @@ function appState(state = false, action) { } } -function credentials(state = {}, action) { +function credentials(state: any = {}, action: GenericAction) { switch (action.type) { case GeneralTypes.RECEIVED_APP_CREDENTIALS: return Object.assign({}, state, action.data); @@ -38,7 +40,7 @@ function credentials(state = {}, action) { } } -function dataRetentionPolicy(state = {}, action) { +function dataRetentionPolicy(state: any = {}, action: GenericAction) { switch (action.type) { case GeneralTypes.RECEIVED_DATA_RETENTION_POLICY: return action.data; @@ -49,7 +51,7 @@ function dataRetentionPolicy(state = {}, action) { } } -function deviceToken(state = '', action) { +function deviceToken(state = '', action: GenericAction) { switch (action.type) { case GeneralTypes.RECEIVED_APP_DEVICE_TOKEN: return action.data; @@ -58,7 +60,7 @@ function deviceToken(state = '', action) { } } -function license(state = {}, action) { +function license(state: any = {}, action: GenericAction) { switch (action.type) { case GeneralTypes.CLIENT_LICENSE_RECEIVED: return action.data; @@ -70,7 +72,7 @@ function license(state = {}, action) { } } -function timezones(state = [], action) { +function timezones(state: string[] = [], action: GenericAction) { switch (action.type) { case GeneralTypes.SUPPORTED_TIMEZONES_RECEIVED: return action.data; @@ -81,7 +83,7 @@ function timezones(state = [], action) { } } -function serverVersion(state = '', action) { +function serverVersion(state = '', action: GenericAction) { switch (action.type) { case GeneralTypes.RECEIVED_SERVER_VERSION: return action.data; diff --git a/src/reducers/entities/gifs.ts b/src/reducers/entities/gifs.ts index b4f3ea69a..67e34f67b 100644 --- a/src/reducers/entities/gifs.ts +++ b/src/reducers/entities/gifs.ts @@ -3,78 +3,80 @@ import {combineReducers} from 'redux'; import {GifTypes} from 'action_types'; +import {GenericAction} from 'types/actions'; +import {Dictionary} from 'types/utilities'; -const SEARCH_SELECTORS = { - [GifTypes.SELECT_SEARCH_TEXT]: (state, action) => ({ +const SEARCH_SELECTORS: Dictionary = { + [GifTypes.SELECT_SEARCH_TEXT]: (state: any, action: GenericAction) => ({ ...state, searchText: action.searchText, }), - [GifTypes.INVALIDATE_SEARCH_TEXT]: (state, action) => ({ + [GifTypes.INVALIDATE_SEARCH_TEXT]: (state: any, action: GenericAction) => ({ ...state, resultsByTerm: { ...state.resultsByTerm[action.searchText], didInvalidate: true, }, }), - [GifTypes.REQUEST_SEARCH]: (state, action) => ({ + [GifTypes.REQUEST_SEARCH]: (state: any, action: GenericAction) => ({ ...state, resultsByTerm: TERM_SELECTOR[action.type](state.resultsByTerm, action), }), - [GifTypes.RECEIVE_SEARCH]: (state, action) => ({ + [GifTypes.RECEIVE_SEARCH]: (state: any, action: GenericAction) => ({ ...state, searchText: action.searchText, resultsByTerm: TERM_SELECTOR[action.type](state.resultsByTerm, action), }), - [GifTypes.RECEIVE_SEARCH_END]: (state, action) => ({ + [GifTypes.RECEIVE_SEARCH_END]: (state: any, action: GenericAction) => ({ ...state, searchText: action.searchText, resultsByTerm: TERM_SELECTOR[action.type](state.resultsByTerm, action), }), - [GifTypes.RECEIVE_CATEGORY_SEARCH]: (state, action) => ({ + [GifTypes.RECEIVE_CATEGORY_SEARCH]: (state: any, action: GenericAction) => ({ ...state, searchText: action.searchText, resultsByTerm: TERM_SELECTOR[action.type](state.resultsByTerm, action), }), - [GifTypes.SEARCH_FAILURE]: (state, action) => ({ + [GifTypes.SEARCH_FAILURE]: (state: any, action: GenericAction) => ({ ...state, searchText: action.searchText, resultsByTerm: TERM_SELECTOR[action.type](state.resultsByTerm, action), }), - [GifTypes.CLEAR_SEARCH_RESULTS]: (state) => ({ + [GifTypes.CLEAR_SEARCH_RESULTS]: (state: any) => ({ ...state, searchText: '', resultsByTerm: {}, }), - [GifTypes.SAVE_SEARCH_SCROLL_POSITION]: (state, action) => ({ + [GifTypes.SAVE_SEARCH_SCROLL_POSITION]: (state: any, action: GenericAction) => ({ ...state, scrollPosition: action.scrollPosition, }), - [GifTypes.SAVE_SEARCH_PRIOR_LOCATION]: (state, action) => ({ + [GifTypes.SAVE_SEARCH_PRIOR_LOCATION]: (state: any, action: GenericAction) => ({ ...state, priorLocation: action.priorLocation, }), - [GifTypes.UPDATE_SEARCH_TEXT]: (state, action) => ({ + [GifTypes.UPDATE_SEARCH_TEXT]: (state: any, action: GenericAction) => ({ ...state, searchText: action.searchText, }), - [GifTypes.SAVE_SEARCH_BAR_TEXT]: (state, action) => ({ + [GifTypes.SAVE_SEARCH_BAR_TEXT]: (state: any, action: GenericAction) => ({ ...state, searchBarText: action.searchBarText, }), }; -const CATEGORIES_SELECTORS = { - [GifTypes.REQUEST_CATEGORIES_LIST]: (state) => ({ +const CATEGORIES_SELECTORS: Dictionary = { + [GifTypes.REQUEST_CATEGORIES_LIST]: (state: any) => ({ ...state, isFetching: true, }), - [GifTypes.CATEGORIES_LIST_RECEIVED]: (state, action) => { + [GifTypes.CATEGORIES_LIST_RECEIVED]: (state: any, action: GenericAction) => { const {cursor, tags} = action; const {tagsList: oldTagsList = []} = state; - const tagsDict = {}; - const newTagsList = tags.filter((item) => { + const tagsDict: any = {}; + const newTagsList = tags.filter((item: any) => { return Boolean(item && item.gfycats[0] && item.gfycats[0].width); - }).map((item) => { + }).map((item: any) => { tagsDict[item.tag] = true; return { tagName: item.tag, @@ -91,14 +93,14 @@ const CATEGORIES_SELECTORS = { tagsDict, }; }, - [GifTypes.CATEGORIES_LIST_FAILURE]: (state) => ({ + [GifTypes.CATEGORIES_LIST_FAILURE]: (state: any) => ({ ...state, isFetching: false, }), }; -const TERM_SELECTOR = { - [GifTypes.REQUEST_SEARCH]: (state, action) => ({ +const TERM_SELECTOR: Dictionary = { + [GifTypes.REQUEST_SEARCH]: (state: any, action: GenericAction) => ({ ...state, [action.searchText]: { ...state[action.searchText], @@ -107,11 +109,11 @@ const TERM_SELECTOR = { pages: PAGE_SELECTOR[action.type](state[action.searchText], action), }, }), - [GifTypes.RECEIVE_SEARCH]: (state, action) => { - const gfycats = action.gfycats.filter((item) => { + [GifTypes.RECEIVE_SEARCH]: (state: any, action: GenericAction) => { + const gfycats = action.gfycats.filter((item: any) => { return Boolean(item.gfyId && item.width && item.height); }); - const newItems = gfycats.map((gfycat) => gfycat.gfyId); + const newItems = gfycats.map((gfycat: any) => gfycat.gfyId); return { ...state, [action.searchText]: { @@ -138,11 +140,11 @@ const TERM_SELECTOR = { }, }; }, - [GifTypes.RECEIVE_CATEGORY_SEARCH]: (state, action) => { - const gfycats = action.gfycats.filter((item) => { + [GifTypes.RECEIVE_CATEGORY_SEARCH]: (state: any, action: GenericAction) => { + const gfycats = action.gfycats.filter((item: any) => { return Boolean(item.gfyId && item.width && item.height); }); - const newItems = gfycats.map((gfycat) => gfycat.gfyId); + const newItems = gfycats.map((gfycat: any) => gfycat.gfyId); return { ...state, [action.searchText]: { @@ -157,7 +159,7 @@ const TERM_SELECTOR = { }, }; }, - [GifTypes.RECEIVE_SEARCH_END]: (state, action) => ({ + [GifTypes.RECEIVE_SEARCH_END]: (state: any, action: GenericAction) => ({ ...state, [action.searchText]: { ...state[action.searchText], @@ -165,7 +167,7 @@ const TERM_SELECTOR = { moreRemaining: false, }, }), - [GifTypes.SEARCH_FAILURE]: (state, action) => ({ + [GifTypes.SEARCH_FAILURE]: (state: any, action: GenericAction) => ({ ...state, [action.searchText]: { ...state[action.searchText], @@ -179,42 +181,42 @@ const TERM_SELECTOR = { }, }), }; -const PAGE_SELECTOR = { - [GifTypes.REQUEST_SEARCH]: (state: {pages?} = {}) => { +const PAGE_SELECTOR: Dictionary = { + [GifTypes.REQUEST_SEARCH]: (state: {pages?: any} = {}) => { if (typeof state.pages == 'undefined') { return {}; } return {...state.pages}; }, - [GifTypes.RECEIVE_SEARCH]: (state, action) => ({ + [GifTypes.RECEIVE_SEARCH]: (state: any, action: GenericAction) => ({ ...state.pages, - [action.currentPage]: action.gfycats.map((gfycat) => gfycat.gfyId), + [action.currentPage]: action.gfycats.map((gfycat: any) => gfycat.gfyId), }), }; -const CACHE_SELECTORS = { - [GifTypes.CACHE_GIFS]: (state, action) => ({ +const CACHE_SELECTORS: Dictionary = { + [GifTypes.CACHE_GIFS]: (state: any, action: GenericAction) => ({ ...state, gifs: CACHE_GIF_SELECTOR[action.type](state.gifs, action), updating: false, }), - [GifTypes.CACHE_REQUEST]: (state, action) => ({ + [GifTypes.CACHE_REQUEST]: (state: any, action: GenericAction) => ({ ...state, ...action.payload, }), }; -const CACHE_GIF_SELECTOR = { - [GifTypes.CACHE_GIFS]: (state, action) => ({ +const CACHE_GIF_SELECTOR: Dictionary = { + [GifTypes.CACHE_GIFS]: (state: any, action: GenericAction) => ({ ...state, - ...action.gifs.reduce((map, obj) => { + ...action.gifs.reduce((map: any, obj: any) => { map[obj.gfyId] = obj; return map; }, {}), }), }; -function appReducer(state = {}, action) { +function appReducer(state: any = {}, action: GenericAction) { const nextState = {...state}; switch (action.type) { case GifTypes.SAVE_APP_PROPS: @@ -224,17 +226,17 @@ function appReducer(state = {}, action) { } } -function categoriesReducer(state = {}, action) { +function categoriesReducer(state: any = {}, action: GenericAction) { const selector = CATEGORIES_SELECTORS[action.type]; return selector ? selector(state, action) : state; } -function searchReducer(state = {}, action) { +function searchReducer(state: any = {}, action: GenericAction) { const selector = SEARCH_SELECTORS[action.type]; return selector ? selector(state, action) : state; } -function cacheReducer(state = {}, action) { +function cacheReducer(state: any = {}, action: GenericAction) { const selector = CACHE_SELECTORS[action.type]; return selector ? selector(state, action) : state; } diff --git a/src/reducers/entities/groups.ts b/src/reducers/entities/groups.ts index f83664bfb..ae073ba88 100644 --- a/src/reducers/entities/groups.ts +++ b/src/reducers/entities/groups.ts @@ -2,9 +2,13 @@ // See LICENSE.txt for license information. import {combineReducers} from 'redux'; import {GroupTypes} from 'action_types'; -import {GroupChannel, GroupSyncables, GroupTeam} from 'types/groups'; +import {GroupChannel, GroupSyncables, GroupTeam, Group} from 'types/groups'; +import {GenericAction} from 'types/actions'; +import {Team, TeamMembership} from 'types/teams'; +import {ChannelMembership} from 'types/channels'; +import {Dictionary} from 'types/utilities'; -function syncables(state = {}, action) { +function syncables(state: Dictionary = {}, action: GenericAction) { switch (action.type) { case GroupTypes.RECEIVED_GROUP_TEAMS: { return { @@ -117,7 +121,7 @@ function syncables(state = {}, action) { } } -function members(state = {}, action) { +function members(state: any = {}, action: GenericAction) { switch (action.type) { case GroupTypes.RECEIVED_GROUP_MEMBERS: { return { @@ -133,7 +137,7 @@ function members(state = {}, action) { } } -function groups(state = {}, action) { +function groups(state: Dictionary = {}, action: GenericAction) { switch (action.type) { case GroupTypes.RECEIVED_GROUP: { return { diff --git a/src/reducers/entities/integrations.ts b/src/reducers/entities/integrations.ts index cf017ac72..8dff4d445 100644 --- a/src/reducers/entities/integrations.ts +++ b/src/reducers/entities/integrations.ts @@ -3,8 +3,11 @@ import {combineReducers} from 'redux'; import {IntegrationTypes, UserTypes, ChannelTypes} from 'action_types'; +import {GenericAction} from 'types/actions'; +import {Command, IncomingWebhook, OutgoingWebhook, OAuthApp} from 'types/integrations'; +import {Dictionary, IDMappedObjects} from 'types/utilities'; -function incomingHooks(state = {}, action) { +function incomingHooks(state: IDMappedObjects = {}, action: GenericAction) { switch (action.type) { case IntegrationTypes.RECEIVED_INCOMING_HOOK: { const nextState = {...state}; @@ -47,7 +50,7 @@ function incomingHooks(state = {}, action) { } } -function outgoingHooks(state = {}, action) { +function outgoingHooks(state: IDMappedObjects = {}, action: GenericAction) { switch (action.type) { case IntegrationTypes.RECEIVED_OUTGOING_HOOK: { const nextState = {...state}; @@ -90,7 +93,7 @@ function outgoingHooks(state = {}, action) { } } -function commands(state = {}, action) { +function commands(state: IDMappedObjects = {}, action: GenericAction) { switch (action.type) { case IntegrationTypes.RECEIVED_COMMANDS: case IntegrationTypes.RECEIVED_CUSTOM_TEAM_COMMANDS: { @@ -136,10 +139,10 @@ function commands(state = {}, action) { } } -function systemCommands(state = {}, action) { +function systemCommands(state: IDMappedObjects = {}, action: GenericAction) { switch (action.type) { case IntegrationTypes.RECEIVED_COMMANDS: { - const nextCommands = {}; + const nextCommands: Dictionary = {}; for (const command of action.data) { if (!command.id) { nextCommands[command.trigger] = command; @@ -164,7 +167,7 @@ function systemCommands(state = {}, action) { } } -function oauthApps(state = {}, action) { +function oauthApps(state: IDMappedObjects = {}, action: GenericAction) { switch (action.type) { case IntegrationTypes.RECEIVED_OAUTH_APPS: { const nextState = {...state}; @@ -191,7 +194,7 @@ function oauthApps(state = {}, action) { } } -function dialogTriggerId(state = '', action) { +function dialogTriggerId(state = '', action: GenericAction) { switch (action.type) { case IntegrationTypes.RECEIVED_DIALOG_TRIGGER_ID: return action.data; @@ -200,7 +203,7 @@ function dialogTriggerId(state = '', action) { } } -function dialog(state = '', action) { +function dialog(state = '', action: GenericAction) { switch (action.type) { case IntegrationTypes.RECEIVED_DIALOG: return action.data; diff --git a/src/reducers/entities/jobs.ts b/src/reducers/entities/jobs.ts index 3bb8415cb..5357d2bf1 100644 --- a/src/reducers/entities/jobs.ts +++ b/src/reducers/entities/jobs.ts @@ -30,7 +30,7 @@ function jobsByTypeList(state: JobsByType = {}, action: GenericAction): JobsByTy case JobTypes.RECEIVED_JOBS_BY_TYPE: { const nextState = {...state}; if (action.data && action.data.length && action.data.length > 0) { - nextState[action.data[0].type] = action.data; + nextState[action.data[0].type as JobType] = action.data; } return nextState; } diff --git a/src/reducers/entities/plugins.ts b/src/reducers/entities/plugins.ts index 69d2834b5..2875b7bd8 100644 --- a/src/reducers/entities/plugins.ts +++ b/src/reducers/entities/plugins.ts @@ -3,8 +3,9 @@ import {combineReducers} from 'redux'; import {PluginTypes} from 'action_types'; +import {GenericAction} from 'types/actions'; -function marketplacePlugins(state = [], action) { +function marketplacePlugins(state = [], action: GenericAction) { switch (action.type) { case PluginTypes.RECEIVED_MARKETPLACE_PLUGINS: { return action.data ? action.data : []; diff --git a/src/reducers/entities/posts.ts b/src/reducers/entities/posts.ts index 107af1099..f5c0d8142 100644 --- a/src/reducers/entities/posts.ts +++ b/src/reducers/entities/posts.ts @@ -3,10 +3,12 @@ import {ChannelTypes, GeneralTypes, PostTypes, UserTypes} from 'action_types'; import {Posts} from '../../constants'; import {comparePosts} from 'utils/post_utils'; -import {Post, PostsState} from 'types/posts'; -import {RelationOneToOne, Dictionary} from 'types/utilities'; +import {Post, PostsState, PostOrderBlock, MessageHistory} from 'types/posts'; +import {RelationOneToOne, Dictionary, IDMappedObjects, RelationOneToMany} from 'types/utilities'; +import {GenericAction} from 'types/actions'; +import {Reaction} from 'types/reactions'; -export function removeUnneededMetadata(post) { +export function removeUnneededMetadata(post: Post) { if (!post.metadata) { return post; } @@ -63,7 +65,7 @@ export function removeUnneededMetadata(post) { }; } -export function handlePosts(state: RelationOneToOne = {}, action) { +export function handlePosts(state: RelationOneToOne = {}, action: GenericAction) { switch (action.type) { case PostTypes.RECEIVED_POST: case PostTypes.RECEIVED_NEW_POST: { @@ -80,7 +82,7 @@ export function handlePosts(state: RelationOneToOne = {}, action) { } case PostTypes.RECEIVED_POSTS: { - const posts = Object.values(action.data.posts); + const posts = Object.values(action.data.posts) as Post[]; if (posts.length === 0) { return state; @@ -184,7 +186,7 @@ export function handlePosts(state: RelationOneToOne = {}, action) { } } -function handlePostReceived(nextState, post) { +function handlePostReceived(nextState: any, post: Post) { if (nextState[post.id] && nextState[post.id].update_at >= post.update_at) { // The stored post is newer than the one we've received return nextState; @@ -212,7 +214,7 @@ function handlePostReceived(nextState, post) { return nextState; } -export function handlePendingPosts(state: Array = [], action) { +export function handlePendingPosts(state: Array = [], action: GenericAction) { switch (action.type) { case PostTypes.RECEIVED_NEW_POST: { const post = action.data; @@ -278,10 +280,10 @@ export function handlePendingPosts(state: Array = [], action) { } } -export function postsInChannel(state = {}, action, prevPosts, nextPosts) { +export function postsInChannel(state: Dictionary> = {}, action: GenericAction, prevPosts: IDMappedObjects, nextPosts: Dictionary) { switch (action.type) { case PostTypes.RECEIVED_NEW_POST: { - const post = action.data; + const post = action.data as Post; const postsForChannel = state[post.channel_id]; if (!postsForChannel) { @@ -289,9 +291,9 @@ export function postsInChannel(state = {}, action, prevPosts, nextPosts) { return state; } - const recentBlockIndex = postsForChannel.findIndex((block) => block.recent); + const recentBlockIndex = postsForChannel.findIndex((block: PostOrderBlock) => block.recent); - let nextRecentBlock; + let nextRecentBlock: PostOrderBlock; if (recentBlockIndex === -1) { nextRecentBlock = { order: [], @@ -358,7 +360,7 @@ export function postsInChannel(state = {}, action, prevPosts, nextPosts) { const postsForChannel = state[post.channel_id] || []; - const recentBlockIndex = postsForChannel.findIndex((block) => block.recent); + const recentBlockIndex = postsForChannel.findIndex((block: PostOrderBlock) => block.recent); if (recentBlockIndex === -1) { // Nothing to do since there's no recent block and only the recent block should contain pending posts return state; @@ -403,7 +405,7 @@ export function postsInChannel(state = {}, action, prevPosts, nextPosts) { if (recent) { // The newly received block is now the most recent, so unmark the current most recent block - const recentBlockIndex = postsForChannel.findIndex((block) => block.recent); + const recentBlockIndex = postsForChannel.findIndex((block: PostOrderBlock) => block.recent); if (recentBlockIndex !== -1) { const recentBlock = postsForChannel[recentBlockIndex]; @@ -503,7 +505,7 @@ export function postsInChannel(state = {}, action, prevPosts, nextPosts) { const postsForChannel = state[action.channelId] || []; - const recentBlockIndex = postsForChannel.findIndex((block) => block.recent); + const recentBlockIndex = postsForChannel.findIndex((block: PostOrderBlock) => block.recent); if (recentBlockIndex === -1) { // Nothing to do since this shouldn't be dispatched if we haven't loaded the most recent posts yet return state; @@ -513,7 +515,7 @@ export function postsInChannel(state = {}, action, prevPosts, nextPosts) { const mostOldestCreateAt = nextPosts[recentBlock.order[recentBlock.order.length - 1]].create_at; - const nextRecentBlock = { + const nextRecentBlock: PostOrderBlock = { ...recentBlock, order: [...recentBlock.order], }; @@ -576,7 +578,7 @@ export function postsInChannel(state = {}, action, prevPosts, nextPosts) { const block = nextPostsForChannel[i]; // Remove any comments for this post - const nextOrder = block.order.filter((postId) => prevPosts[postId].root_id !== post.id); + const nextOrder = block.order.filter((postId: string) => prevPosts[postId].root_id !== post.id); if (nextOrder.length !== block.order.length) { nextPostsForChannel[i] = { @@ -618,7 +620,7 @@ export function postsInChannel(state = {}, action, prevPosts, nextPosts) { for (let i = 0; i < nextPostsForChannel.length; i++) { const block = nextPostsForChannel[i]; - const nextOrder = block.order.filter((postId) => postId !== post.id && prevPosts[postId].root_id !== post.id); + const nextOrder = block.order.filter((postId: string) => postId !== post.id && prevPosts[postId].root_id !== post.id); if (nextOrder.length !== block.order.length) { nextPostsForChannel[i] = { @@ -672,11 +674,11 @@ export function postsInChannel(state = {}, action, prevPosts, nextPosts) { } } -export function removeNonRecentEmptyPostBlocks(blocks) { - return blocks.filter((block) => block.order.length !== 0 || block.recent); +export function removeNonRecentEmptyPostBlocks(blocks: PostOrderBlock[]) { + return blocks.filter((block: PostOrderBlock) => block.order.length !== 0 || block.recent); } -export function mergePostBlocks(blocks, posts) { +export function mergePostBlocks(blocks: PostOrderBlock[], posts: Dictionary) { let nextBlocks = [...blocks]; // Remove any blocks that may have become empty by removing posts @@ -733,7 +735,7 @@ export function mergePostBlocks(blocks, posts) { return nextBlocks; } -export function mergePostOrder(left, right, posts) { +export function mergePostOrder(left: string[], right: string[], posts: Dictionary) { const result = [...left]; // Add without duplicates @@ -757,7 +759,7 @@ export function mergePostOrder(left, right, posts) { return result; } -export function postsInThread(state: Dictionary = {}, action, prevPosts) { +export function postsInThread(state: RelationOneToMany = {}, action: GenericAction, prevPosts: Dictionary) { switch (action.type) { case PostTypes.RECEIVED_NEW_POST: case PostTypes.RECEIVED_POST: { @@ -956,7 +958,7 @@ export function postsInThread(state: Dictionary = {}, action, prevPost } } -function selectedPostId(state = '', action) { +function selectedPostId(state = '', action: GenericAction) { switch (action.type) { case PostTypes.RECEIVED_POST_SELECTED: return action.data; @@ -967,7 +969,7 @@ function selectedPostId(state = '', action) { } } -function currentFocusedPostId(state = '', action) { +function currentFocusedPostId(state = '', action: GenericAction) { switch (action.type) { case PostTypes.RECEIVED_FOCUSED_POST: return action.data; @@ -978,22 +980,22 @@ function currentFocusedPostId(state = '', action) { } } -export function reactions(state = {}, action) { +export function reactions(state: RelationOneToOne> = {}, action: GenericAction) { switch (action.type) { case PostTypes.RECEIVED_REACTIONS: { const reactionsList = action.data; - const nextReactions = {}; - reactionsList.forEach((reaction) => { + const nextReactions: Dictionary = {}; + reactionsList.forEach((reaction: Reaction) => { nextReactions[reaction.user_id + '-' + reaction.emoji_name] = reaction; }); return { ...state, - [action.postId]: nextReactions, + [action.postId!]: nextReactions, }; } case PostTypes.RECEIVED_REACTION: { - const reaction = action.data; + const reaction = action.data as Reaction; const nextReactions = {...(state[reaction.post_id] || {})}; nextReactions[reaction.user_id + '-' + reaction.emoji_name] = reaction; @@ -1051,12 +1053,12 @@ export function reactions(state = {}, action) { } } -function storeReactionsForPost(state, post) { +function storeReactionsForPost(state: any, post: Post) { if (!post.metadata || !post.metadata.reactions || post.delete_at > 0) { return state; } - const reactionsForPost = {}; + const reactionsForPost: Dictionary = {}; if (post.metadata.reactions && post.metadata.reactions.length > 0) { for (const reaction of post.metadata.reactions) { reactionsForPost[reaction.user_id + '-' + reaction.emoji_name] = reaction; @@ -1069,7 +1071,7 @@ function storeReactionsForPost(state, post) { }; } -export function openGraph(state = {}, action) { +export function openGraph(state: RelationOneToOne = {}, action: GenericAction) { switch (action.type) { case PostTypes.RECEIVED_OPEN_GRAPH_METADATA: { const nextState = {...state}; @@ -1097,7 +1099,7 @@ export function openGraph(state = {}, action) { } } -function storeOpenGraphForPost(state, post) { +function storeOpenGraphForPost(state: any, post: Post) { if (!post.metadata || !post.metadata.embeds) { return state; } @@ -1115,10 +1117,10 @@ function storeOpenGraphForPost(state, post) { }, state); } -function messagesHistory(state: {messages?;index?} = {}, action) { +function messagesHistory(state: Partial = {}, action: GenericAction) { switch (action.type) { case PostTypes.ADD_MESSAGE_INTO_HISTORY: { - const nextIndex = {}; + const nextIndex: Dictionary = {}; let nextMessages = state.messages ? [...state.messages] : []; nextMessages.push(action.data); nextIndex[Posts.MESSAGE_TYPES.POST] = nextMessages.length; @@ -1134,7 +1136,7 @@ function messagesHistory(state: {messages?;index?} = {}, action) { }; } case PostTypes.RESET_HISTORY_INDEX: { - const index = {}; + const index: Dictionary = {}; index[Posts.MESSAGE_TYPES.POST] = -1; index[Posts.MESSAGE_TYPES.COMMENT] = -1; @@ -1147,7 +1149,7 @@ function messagesHistory(state: {messages?;index?} = {}, action) { }; } case PostTypes.MOVE_HISTORY_INDEX_BACK: { - const index = {}; + const index: Dictionary = {}; index[Posts.MESSAGE_TYPES.POST] = -1; index[Posts.MESSAGE_TYPES.COMMENT] = -1; @@ -1161,7 +1163,7 @@ function messagesHistory(state: {messages?;index?} = {}, action) { }; } case PostTypes.MOVE_HISTORY_INDEX_FORWARD: { - const index = {}; + const index: Dictionary = {}; index[Posts.MESSAGE_TYPES.POST] = -1; index[Posts.MESSAGE_TYPES.COMMENT] = -1; @@ -1176,7 +1178,7 @@ function messagesHistory(state: {messages?;index?} = {}, action) { }; } case UserTypes.LOGOUT_SUCCESS: { - const index = {}; + const index: Dictionary = {}; index[Posts.MESSAGE_TYPES.POST] = -1; index[Posts.MESSAGE_TYPES.COMMENT] = -1; @@ -1190,7 +1192,7 @@ function messagesHistory(state: {messages?;index?} = {}, action) { } } -export function expandedURLs(state = {}, action) { +export function expandedURLs(state: Dictionary = {}, action: GenericAction) { switch (action.type) { case GeneralTypes.REDIRECT_LOCATION_SUCCESS: return { @@ -1207,9 +1209,9 @@ export function expandedURLs(state = {}, action) { } } -export default function(state: Partial = {}, action) { +export default function(state: Partial = {}, action: GenericAction) { const nextPosts = handlePosts(state.posts, action); - const nextPostsInChannel = postsInChannel(state.postsInChannel, action, state.posts, nextPosts); + const nextPostsInChannel = postsInChannel(state.postsInChannel, action, state.posts!, nextPosts); const nextState = { @@ -1224,7 +1226,7 @@ export default function(state: Partial = {}, action) { // Object mapping post root ids to an array of posts ids of comments (but not the root post) in that thread // with no guaranteed order - postsInThread: postsInThread(state.postsInThread, action, state.posts), + postsInThread: postsInThread(state.postsInThread, action, state.posts!), // The current selected post selectedPostId: selectedPostId(state.selectedPostId, action), diff --git a/src/reducers/entities/preferences.ts b/src/reducers/entities/preferences.ts index f773e18a2..25c044494 100644 --- a/src/reducers/entities/preferences.ts +++ b/src/reducers/entities/preferences.ts @@ -3,15 +3,18 @@ import {combineReducers} from 'redux'; import {PreferenceTypes, UserTypes} from 'action_types'; +import {GenericAction} from 'types/actions'; +import {PreferenceType} from 'types/preferences'; +import {Dictionary} from 'types/utilities'; -function getKey(preference) { +function getKey(preference: PreferenceType) { return `${preference.category}--${preference.name}`; } -function myPreferences(state = {}, action) { +function myPreferences(state: Dictionary = {}, action: GenericAction) { switch (action.type) { case PreferenceTypes.RECEIVED_ALL_PREFERENCES: { - const nextState = {}; + const nextState: any = {}; if (action.data) { for (const preference of action.data) { diff --git a/src/reducers/entities/roles.ts b/src/reducers/entities/roles.ts index 669872355..66e544d67 100644 --- a/src/reducers/entities/roles.ts +++ b/src/reducers/entities/roles.ts @@ -3,8 +3,11 @@ import {combineReducers} from 'redux'; import {RoleTypes, UserTypes} from 'action_types'; +import {GenericAction} from 'types/actions'; +import {Dictionary} from 'types/utilities'; +import {Role} from 'types/roles'; -function pending(state = new Set(), action) { +function pending(state: Set = new Set(), action: GenericAction) { switch (action.type) { case RoleTypes.SET_PENDING_ROLES: return action.data; @@ -15,7 +18,7 @@ function pending(state = new Set(), action) { } } -function roles(state = {}, action) { +function roles(state: Dictionary = {}, action: GenericAction) { switch (action.type) { case RoleTypes.RECEIVED_ROLES: { if (action.data) { diff --git a/src/reducers/entities/search.ts b/src/reducers/entities/search.ts index b6b4394a7..7ae93090b 100644 --- a/src/reducers/entities/search.ts +++ b/src/reducers/entities/search.ts @@ -4,8 +4,12 @@ import {combineReducers} from 'redux'; import {PostTypes, PreferenceTypes, SearchTypes, UserTypes} from 'action_types'; import {Preferences} from '../../constants'; import {PreferenceType} from 'types/preferences'; +import {GenericAction} from 'types/actions'; +import {Post} from 'types/posts'; +import {Dictionary} from 'types/utilities'; +import {Search} from 'types/search'; -function results(state: Array = [], action) { +function results(state: Array = [], action: GenericAction) { switch (action.type) { case SearchTypes.RECEIVED_SEARCH_POSTS: { if (action.isGettingMore) { @@ -32,7 +36,7 @@ function results(state: Array = [], action) { } } -function matches(state = {}, action) { +function matches(state: Dictionary> = {}, action: GenericAction) { switch (action.type) { case SearchTypes.RECEIVED_SEARCH_POSTS: if (action.isGettingMore) { @@ -57,7 +61,7 @@ function matches(state = {}, action) { } } -function flagged(state: Array = [], action) { +function flagged(state: Array = [], action: GenericAction) { switch (action.type) { case SearchTypes.RECEIVED_SEARCH_FLAGGED_POSTS: { return action.data.order; @@ -76,7 +80,7 @@ function flagged(state: Array = [], action) { if (action.data) { const nextState = [...state]; let hasNewFlaggedPosts = false; - action.data.forEach((pref) => { + action.data.forEach((pref: PreferenceType) => { if (pref.category === Preferences.CATEGORY_FLAGGED_POST) { const exists = nextState.find((p) => p === pref.name); if (!exists) { @@ -119,7 +123,7 @@ function flagged(state: Array = [], action) { } } -function removePinnedPost(state, post) { +function removePinnedPost(state: Dictionary>, post: Post) { if (post && state[post.channel_id]) { const postId = post.id; const channelId = post.channel_id; @@ -138,7 +142,7 @@ function removePinnedPost(state, post) { return state; } -function pinned(state = {}, action) { +function pinned(state: Dictionary> = {}, action: GenericAction) { switch (action.type) { case SearchTypes.RECEIVED_SEARCH_PINNED_POSTS: { const {channelId, pinned: posts} = action.data; @@ -191,7 +195,7 @@ function pinned(state = {}, action) { } } -function recent(state = {}, action) { +function recent(state: Dictionary> = {}, action: GenericAction) { const {data, type} = action; switch (type) { @@ -236,7 +240,7 @@ function recent(state = {}, action) { } } -function current(state = {}, action) { +function current(state: any = {}, action: GenericAction) { const {data, type} = action; switch (type) { case SearchTypes.RECEIVED_SEARCH_TERM: { @@ -258,7 +262,7 @@ function current(state = {}, action) { } } -function isSearchingTerm(state = false, action) { +function isSearchingTerm(state = false, action: GenericAction) { switch (action.type) { case SearchTypes.SEARCH_POSTS_REQUEST: return !action.isGettingMore; @@ -270,7 +274,7 @@ function isSearchingTerm(state = false, action) { } } -function isSearchGettingMore(state = false, action) { +function isSearchGettingMore(state = false, action: GenericAction) { switch (action.type) { case SearchTypes.SEARCH_POSTS_REQUEST: return action.isGettingMore; diff --git a/src/reducers/entities/teams.ts b/src/reducers/entities/teams.ts index fee222b26..eb45547c9 100644 --- a/src/reducers/entities/teams.ts +++ b/src/reducers/entities/teams.ts @@ -297,7 +297,7 @@ function membersInTeam(state: RelationOneToOne, action: GenericAction) { const id = action.id; const nextSet = new Set(state[id]); Object.keys(action.data).forEach((key) => { @@ -18,11 +23,11 @@ function profilesToSet(state, action) { }; } -function profileListToSet(state, action, replace = false) { +function profileListToSet(state: RelationOneToMany, action: GenericAction, replace = false) { const id = action.id; const nextSet = replace ? new Set() : new Set(state[id]); if (action.data) { - action.data.forEach((profile) => { + action.data.forEach((profile: UserProfile) => { nextSet.add(profile.id); }); @@ -35,11 +40,11 @@ function profileListToSet(state, action, replace = false) { return state; } -function removeProfileListFromSet(state, action) { +function removeProfileListFromSet(state: RelationOneToMany, action: GenericAction) { const id = action.id; const nextSet = new Set(state[id]); if (action.data) { - action.data.forEach((profile) => { + action.data.forEach((profile: UserProfile) => { nextSet.delete(profile.id); }); @@ -52,7 +57,7 @@ function removeProfileListFromSet(state, action) { return state; } -function addProfileToSet(state, action) { +function addProfileToSet(state: RelationOneToMany, action: GenericAction) { const {id, user_id: userId} = action.data; const nextSet = new Set(state[id]); nextSet.add(userId); @@ -62,7 +67,7 @@ function addProfileToSet(state, action) { }; } -function removeProfileFromSet(state, action) { +function removeProfileFromSet(state: RelationOneToMany, action: GenericAction) { const {id, user_id: userId} = action.data; const nextSet = new Set(state[id]); nextSet.delete(userId); @@ -72,7 +77,7 @@ function removeProfileFromSet(state, action) { }; } -function currentUserId(state = '', action) { +function currentUserId(state = '', action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_ME: { const data = action.data || action.payload; @@ -87,7 +92,7 @@ function currentUserId(state = '', action) { return state; } -function mySessions(state: Array<{id}> = [], action) { +function mySessions(state: Array<{id: string}> = [], action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_SESSIONS: return [...action.data]; @@ -125,7 +130,7 @@ function mySessions(state: Array<{id}> = [], action) { } } -function myAudits(state = [], action) { +function myAudits(state = [], action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_AUDITS: return [...action.data]; @@ -138,7 +143,7 @@ function myAudits(state = [], action) { } } -function profiles(state = {}, action) { +function profiles(state: IDMappedObjects = {}, action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_ME: case UserTypes.RECEIVED_PROFILE: { @@ -179,7 +184,7 @@ function profiles(state = {}, action) { } } -function profilesInTeam(state = {}, action) { +function profilesInTeam(state: RelationOneToMany = {}, action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILE_IN_TEAM: return addProfileToSet(state, action); @@ -204,7 +209,7 @@ function profilesInTeam(state = {}, action) { } } -function profilesNotInTeam(state = {}, action) { +function profilesNotInTeam(state: RelationOneToMany = {}, action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILE_NOT_IN_TEAM: return addProfileToSet(state, action); @@ -229,16 +234,16 @@ function profilesNotInTeam(state = {}, action) { } } -function profilesWithoutTeam(state = new Set(), action) { +function profilesWithoutTeam(state: Set = new Set(), action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILE_WITHOUT_TEAM: { const nextSet = new Set(state); - Object.values(action.data).forEach((id) => nextSet.add(id)); + Object.values(action.data).forEach((id: string) => nextSet.add(id)); return nextSet; } case UserTypes.RECEIVED_PROFILES_LIST_WITHOUT_TEAM: { const nextSet = new Set(state); - action.data.forEach((user) => nextSet.add(user.id)); + action.data.forEach((user: UserProfile) => nextSet.add(user.id)); return nextSet; } case UserTypes.RECEIVED_PROFILE_IN_TEAM: { @@ -254,7 +259,7 @@ function profilesWithoutTeam(state = new Set(), action) { } } -function profilesInChannel(state = {}, action) { +function profilesInChannel(state: RelationOneToMany = {}, action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILE_IN_CHANNEL: return addProfileToSet(state, action); @@ -269,10 +274,12 @@ function profilesInChannel(state = {}, action) { return removeProfileFromSet(state, action); case ChannelTypes.CHANNEL_MEMBER_REMOVED: - return removeProfileFromSet(state, {data: { - id: action.data.channel_id, - user_id: action.data.user_id, - }}); + return removeProfileFromSet(state, { + type: '', + data: { + id: action.data.channel_id, + user_id: action.data.user_id, + }}); case UserTypes.LOGOUT_SUCCESS: return {}; @@ -282,7 +289,7 @@ function profilesInChannel(state = {}, action) { } } -function profilesNotInChannel(state = {}, action) { +function profilesNotInChannel(state: RelationOneToMany = {}, action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_PROFILE_NOT_IN_CHANNEL: return addProfileToSet(state, action); @@ -300,10 +307,12 @@ function profilesNotInChannel(state = {}, action) { return removeProfileFromSet(state, action); case ChannelTypes.CHANNEL_MEMBER_ADDED: - return removeProfileFromSet(state, {data: { - id: action.data.channel_id, - user_id: action.data.user_id, - }}); + return removeProfileFromSet(state, { + type: '', + data: { + id: action.data.channel_id, + user_id: action.data.user_id, + }}); case UserTypes.LOGOUT_SUCCESS: return {}; @@ -313,7 +322,7 @@ function profilesNotInChannel(state = {}, action) { } } -function statuses(state = {}, action) { +function statuses(state: RelationOneToOne = {}, action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_STATUS: { const nextState = Object.assign({}, state); @@ -338,7 +347,7 @@ function statuses(state = {}, action) { } } -function myUserAccessTokens(state = {}, action) { +function myUserAccessTokens(state: any = {}, action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_MY_USER_ACCESS_TOKEN: { const nextState = {...state}; @@ -389,7 +398,7 @@ function myUserAccessTokens(state = {}, action) { } } -function stats(state = {}, action) { +function stats(state = {}, action: GenericAction) { switch (action.type) { case UserTypes.RECEIVED_USER_STATS: { const stat = action.data; diff --git a/src/reducers/errors/index.ts b/src/reducers/errors/index.ts index ba47c9a6e..20cd493ae 100644 --- a/src/reducers/errors/index.ts +++ b/src/reducers/errors/index.ts @@ -1,11 +1,12 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import {ErrorTypes} from 'action_types'; -export default ((state: Array<{error;displayable;date}> = [], action) => { +import {GenericAction} from 'types/actions'; +export default ((state: Array<{error: any;displayable?: boolean;date: string}> = [], action: GenericAction) => { switch (action.type) { case ErrorTypes.DISMISS_ERROR: { const nextState = [...state]; - nextState.splice(action.index, 1); + nextState.splice(action.index!, 1); return nextState; } diff --git a/src/reducers/websocket.ts b/src/reducers/websocket.ts index 96d39ab76..89eaba5a3 100644 --- a/src/reducers/websocket.ts +++ b/src/reducers/websocket.ts @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import {GeneralTypes, UserTypes} from 'action_types'; +import {GenericAction} from 'types/actions'; function getInitialState() { return { @@ -11,7 +12,7 @@ function getInitialState() { }; } -export default function(state = getInitialState(), action) { +export default function(state = getInitialState(), action: GenericAction) { if (!state.connected && action.type === GeneralTypes.WEBSOCKET_SUCCESS) { return { ...state, diff --git a/src/selectors/entities/admin.ts b/src/selectors/entities/admin.ts index 27130d876..1df751d81 100644 --- a/src/selectors/entities/admin.ts +++ b/src/selectors/entities/admin.ts @@ -1,38 +1,40 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -export function getLogs(state) { +import {GlobalState} from 'types/store'; + +export function getLogs(state: GlobalState) { return state.entities.admin.logs; } -export function getAudits(state) { +export function getAudits(state: GlobalState) { return state.entities.admin.audits; } -export function getConfig(state) { +export function getConfig(state: GlobalState) { return state.entities.admin.config; } -export function getLdapGroups(state) { +export function getLdapGroups(state: GlobalState) { return state.entities.admin.ldapGroups; } -export function getLdapGroupsCount(state) { +export function getLdapGroupsCount(state: GlobalState) { return state.entities.admin.ldapGroupsCount; } -export function getEnvironmentConfig(state) { +export function getEnvironmentConfig(state: GlobalState) { return state.entities.admin.environmentConfig; } -export function getComplianceReports(state) { +export function getComplianceReports(state: GlobalState) { return state.entities.admin.complianceReports; } -export function getClusterInfo(state) { +export function getClusterInfo(state: GlobalState) { return state.entities.admin.clusterInfo; } -export function getUserAccessTokens(state) { +export function getUserAccessTokens(state: GlobalState) { return state.entities.admin.userAccessTokens; } diff --git a/src/selectors/entities/bots.ts b/src/selectors/entities/bots.ts index cdff854c4..40fcd6913 100644 --- a/src/selectors/entities/bots.ts +++ b/src/selectors/entities/bots.ts @@ -1,6 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -export function getBotAccounts(state) { +import {GlobalState} from 'types/store'; + +export function getBotAccounts(state: GlobalState) { return state.entities.bots.accounts; } diff --git a/src/selectors/entities/channels.ts b/src/selectors/entities/channels.ts index 7007282e5..005dbb6be 100644 --- a/src/selectors/entities/channels.ts +++ b/src/selectors/entities/channels.ts @@ -22,7 +22,9 @@ import {TeamMembership, Team} from 'types/teams'; import {NameMappedObjects, UserIDMappedObjects, IDMappedObjects, RelationOneToOne, RelationOneToMany} from 'types/utilities'; import {getUserIdsInChannels} from './users'; +import {Config} from 'types/config'; type SortingType = 'recent' | 'alpha'; + export function getAllChannels(state: GlobalState): IDMappedObjects { return state.entities.channels.channels; } @@ -42,7 +44,7 @@ export function getChannelMembersInChannels(state: GlobalState): RelationOneToOn return state.entities.channels.membersInChannel; } -function sortChannelsByRecencyOrAlpha(locale, lastPosts, sorting: SortingType, a, b) { +function sortChannelsByRecencyOrAlpha(locale: string, lastPosts: RelationOneToOne, sorting: SortingType, a: Channel, b: Channel) { if (sorting === 'recent') { return sortChannelsByRecency(lastPosts, a, b); } @@ -110,11 +112,11 @@ export function filterChannels(unreadIds: Array, favoriteIds: Array Channel { - return createSelector(getAllChannels, (state: GlobalState, props) => props.id, (state: GlobalState) => state.entities.users, getTeammateNameDisplaySetting, (allChannels, channelId, users, teammateNameDisplay) => { + return createSelector(getAllChannels, (state: GlobalState, props: {id: string}) => props.id, (state: GlobalState) => state.entities.users, getTeammateNameDisplaySetting, (allChannels, channelId, users, teammateNameDisplay) => { const channel = allChannels[channelId]; if (channel) { - return completeDirectChannelInfo(users, teammateNameDisplay, channel); + return completeDirectChannelInfo(users, teammateNameDisplay!, channel); } return channel; @@ -203,32 +205,36 @@ export const getChannelsInCurrentTeam: (a: GlobalState) => Array = crea return sortAndInjectChannels(channels, currentTeamChannelSet, locale); }); + export const getChannelsNameMapInTeam: (b: GlobalState, a: string) => NameMappedObjects = createSelector(getAllChannels, getChannelsInTeam, (state: GlobalState, teamId: string): string => teamId, (channels: IDMappedObjects, channelsInTeams: RelationOneToMany, teamId: string): NameMappedObjects => { const channelsInTeam = channelsInTeams[teamId] || []; - const channelMap = {}; + const channelMap: NameMappedObjects = {}; channelsInTeam.forEach((id) => { const channel = channels[id]; channelMap[channel.name] = channel; }); return channelMap; }); + export const getChannelsNameMapInCurrentTeam: (a: GlobalState) => NameMappedObjects = createSelector(getAllChannels, getChannelSetInCurrentTeam, (channels: IDMappedObjects, currentTeamChannelSet: Array): NameMappedObjects => { - const channelMap = {}; + const channelMap: NameMappedObjects = {}; currentTeamChannelSet.forEach((id) => { const channel = channels[id]; channelMap[channel.name] = channel; }); return channelMap; -}); // Returns both DMs and GMs +}); +// Returns both DMs and GMs export const getAllDirectChannels: (a: GlobalState) => Array = createSelector(getAllChannels, getDirectChannelsSet, (state: GlobalState): UsersState => state.entities.users, getTeammateNameDisplaySetting, (channels: IDMappedObjects, channelSet: Set, users: UsersState, teammateNameDisplay: string): Array => { const dmChannels: Channel[] = []; channelSet.forEach((c) => { dmChannels.push(completeDirectChannelInfo(users, teammateNameDisplay, channels[c])); }); return dmChannels; -}); // Returns only GMs +}); +// Returns only GMs export const getGroupChannels: (a: GlobalState) => Array = createSelector(getAllChannels, getDirectChannelsSet, (state: GlobalState): UsersState => state.entities.users, getTeammateNameDisplaySetting, (channels: IDMappedObjects, channelSet: Set, users: UsersState, teammateNameDisplay: string): Array => { const gmChannels: Channel[] = []; channelSet.forEach((id) => { @@ -240,21 +246,25 @@ export const getGroupChannels: (a: GlobalState) => Array = createSelect }); return gmChannels; }); + export const getMyChannels: (a: GlobalState) => Array = createSelector(getChannelsInCurrentTeam, getAllDirectChannels, getMyChannelMemberships, (channels: Array, directChannels: Array, myMembers: RelationOneToOne): Array => { return [...channels, ...directChannels].filter((c) => myMembers.hasOwnProperty(c.id)); }); + export const getOtherChannels: (b: GlobalState, a?: boolean | null) => Array = createSelector(getChannelsInCurrentTeam, getMyChannelMemberships, (state: GlobalState, archived: boolean | undefined | null = true) => archived, (channels: Array, myMembers: RelationOneToOne, archived?: boolean | null): Array => { return channels.filter((c) => !myMembers.hasOwnProperty(c.id) && c.type === General.OPEN_CHANNEL && (archived ? true : c.delete_at === 0)); }); + export const getArchivedChannels: (a: GlobalState) => Array = createSelector(getChannelsInCurrentTeam, getMyChannelMemberships, (channels: Array, myMembers: RelationOneToOne): Array => { return channels.filter((c) => myMembers.hasOwnProperty(c.id) && c.delete_at !== 0); }); + export const getChannelsByCategory: (a: GlobalState) => { favoriteChannels: Array; publicChannels: Array; privateChannels: Array; directAndGroupChannels: Array; -} = createSelector(getCurrentChannelId, getMyChannels, getMyChannelMemberships, getConfig, getMyPreferences, getTeammateNameDisplaySetting, (state: GlobalState): UsersState => state.entities.users, getLastPostPerChannel, (currentChannelId: string, channels: Array, myMembers: RelationOneToOne, config: any, myPreferences: { +} = createSelector(getCurrentChannelId, getMyChannels, getMyChannelMemberships, getConfig, getMyPreferences, getTeammateNameDisplaySetting, (state: GlobalState): UsersState => state.entities.users, getLastPostPerChannel, (currentChannelId: string, channels: Array, myMembers: RelationOneToOne, config: Config, myPreferences: { [x: string]: PreferenceType; }, teammateNameDisplay: string, usersState: UsersState, lastPosts: RelationOneToOne) => { const allChannels = channels.map((c) => { @@ -265,13 +275,14 @@ export const getChannelsByCategory: (a: GlobalState) => { }); return buildDisplayableChannelList(usersState, allChannels, myMembers, config, myPreferences, teammateNameDisplay, lastPosts); }); + export const getChannelsWithUnreadSection: (a: GlobalState) => { unreadChannels: Array; favoriteChannels: Array; publicChannels: Array; privateChannels: Array; directAndGroupChannels: Array; -} = createSelector(getCurrentChannelId, getMyChannels, getMyChannelMemberships, getConfig, getMyPreferences, getTeammateNameDisplaySetting, (state: GlobalState): UsersState => state.entities.users, getLastPostPerChannel, (currentChannelId: string, channels: Array, myMembers: RelationOneToOne, config: any, myPreferences: { +} = createSelector(getCurrentChannelId, getMyChannels, getMyChannelMemberships, getConfig, getMyPreferences, getTeammateNameDisplaySetting, (state: GlobalState): UsersState => state.entities.users, getLastPostPerChannel, (currentChannelId: string, channels: Array, myMembers: RelationOneToOne, config: Config, myPreferences: { [x: string]: PreferenceType; }, teammateNameDisplay: string, usersState: UsersState, lastPosts: RelationOneToOne) => { const allChannels = channels.map((c) => { @@ -282,12 +293,15 @@ export const getChannelsWithUnreadSection: (a: GlobalState) => { }); return buildDisplayableChannelListWithUnreadSection(usersState, allChannels, myMembers, config, myPreferences, teammateNameDisplay, lastPosts); }); + export const getDefaultChannel: (a: GlobalState) => Channel | undefined | null = createSelector(getAllChannels, getCurrentTeamId, (channels: IDMappedObjects, teamId: string): Channel | undefined | null => { return Object.keys(channels).map((key) => channels[key]).find((c) => c && c.team_id === teamId && c.name === General.DEFAULT_CHANNEL); }); + export const getMembersInCurrentChannel: (a: GlobalState) => UserIDMappedObjects = createSelector(getCurrentChannelId, getChannelMembersInChannels, (currentChannelId: string, members: RelationOneToOne>): UserIDMappedObjects => { return members[currentChannelId]; }); + export const getUnreads: (a: GlobalState) => { messageCount: number; mentionCount: number; @@ -392,7 +406,7 @@ export const canManageChannelMembers: (a: GlobalState) => boolean = createSelect permission: Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS, }), (state: GlobalState): boolean => haveICurrentChannelPermission(state, { permission: Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS, -}), (channel: Channel, user: UserProfile, teamMembership: TeamMembership, channelMembership: ChannelMembership | undefined | null, config: any, license: any, newPermissions: boolean, managePrivateMembers: boolean, managePublicMembers: boolean): boolean => { +}), (channel: Channel, user: UserProfile, teamMembership: TeamMembership, channelMembership: ChannelMembership | undefined | null, config: Config, license: any, newPermissions: boolean, managePrivateMembers: boolean, managePublicMembers: boolean): boolean => { if (!channel) { return false; } @@ -490,21 +504,21 @@ export const getUnreadChannels: (b: GlobalState, a?: Channel | null) => Array Array = createIdsSelector(getUnreadChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state, lastUnreadChannel, sorting: SortingType = 'alpha') => sorting, (channels, currentUser, myMembers, lastPosts: RelationOneToOne, sorting: SortingType) => { +export const getMapAndSortedUnreadChannelIds: (c: GlobalState, b: Channel, a: SortingType) => Array = createIdsSelector(getUnreadChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state: GlobalState, lastUnreadChannel: Channel, sorting: SortingType = 'alpha') => sorting, (channels, currentUser, myMembers, lastPosts: RelationOneToOne, sorting: SortingType) => { return mapAndSortChannelIds(channels, currentUser, myMembers, lastPosts, sorting, true); }); -export const getSortedUnreadChannelIds: (e: GlobalState, d: Channel|null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting: SortingType = 'alpha') => { +export const getSortedUnreadChannelIds: (e: GlobalState, d: Channel|null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, (state: GlobalState, lastUnreadChannel: Channel, unreadsAtTop: boolean, favoritesAtTop: boolean, sorting: SortingType = 'alpha') => { return getMapAndSortedUnreadChannelIds(state, lastUnreadChannel, sorting); }, (unreadChannelIds, mappedAndSortedUnreadChannelIds) => mappedAndSortedUnreadChannelIds); // Favorites -export const getFavoriteChannels: (a: GlobalState) => Array = createIdsSelector(getCurrentUser, getUsers, getUserIdsInChannels, getAllChannels, getMyChannelMemberships, getFavoritesPreferences, getChannelIdsForCurrentTeam, getTeammateNameDisplaySetting, getConfig, getMyPreferences, getCurrentChannelId, (currentUser: UserProfile, profiles: IDMappedObjects, userIdsInChannels: any, channels: IDMappedObjects, myMembers: RelationOneToOne, favoriteIds: Array, teamChannelIds: Array, settings: string, config: any, prefs: { +export const getFavoriteChannels: (a: GlobalState) => Array = createIdsSelector(getCurrentUser, getUsers, getUserIdsInChannels, getAllChannels, getMyChannelMemberships, getFavoritesPreferences, getChannelIdsForCurrentTeam, getTeammateNameDisplaySetting, getConfig, getMyPreferences, getCurrentChannelId, (currentUser: UserProfile, profiles: IDMappedObjects, userIdsInChannels: any, channels: IDMappedObjects, myMembers: RelationOneToOne, favoriteIds: Array, teamChannelIds: Array, settings: string, config: Config, prefs: { [x: string]: PreferenceType; }, currentChannelId: string): Array => { if (!currentUser) { @@ -547,11 +561,14 @@ export const getFavoriteChannels: (a: GlobalState) => Array = createIds }); return favoriteChannel; }); -export const getFavoriteChannelIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getFavoriteChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting: SortingType = 'alpha') => sorting, mapAndSortChannelIds); -export const getSortedFavoriteChannelIds: (e: GlobalState, d: Channel|null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting: SortingType) => getFavoriteChannelIds(state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting), (state, lastUnreadChannel, unreadsAtTop = true) => unreadsAtTop, (unreadChannelIds, favoritePreferences, favoriteChannelIds, unreadsAtTop) => { + +export const getFavoriteChannelIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getFavoriteChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state: GlobalState, lastUnreadChannel: Channel, unreadsAtTop: boolean, favoritesAtTop: boolean, sorting: SortingType = 'alpha') => sorting, mapAndSortChannelIds); + +export const getSortedFavoriteChannelIds: (e: GlobalState, d: Channel | null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, (state: GlobalState, lastUnreadChannel: Channel, unreadsAtTop: boolean, favoritesAtTop: boolean, sorting: SortingType) => getFavoriteChannelIds(state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting), (state, lastUnreadChannel, unreadsAtTop = true) => unreadsAtTop, (unreadChannelIds, favoritePreferences, favoriteChannelIds, unreadsAtTop) => { return filterChannels(unreadChannelIds, favoritePreferences, favoriteChannelIds, unreadsAtTop, false); -}); // Public Channels +}); +// Public Channels export const getPublicChannels: (a: GlobalState) => Array = createSelector(getCurrentUser, getAllChannels, getMyChannelMemberships, getChannelIdsForCurrentTeam, (currentUser, channels, myMembers, teamChannelIds) => { if (!currentUser) { return []; @@ -567,8 +584,10 @@ export const getPublicChannels: (a: GlobalState) => Array = createSelec }).map((id) => channels[id]); return publicChannels; }); -export const getPublicChannelIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getPublicChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting: SortingType = 'alpha') => sorting, mapAndSortChannelIds); -export const getSortedPublicChannelIds: (e: GlobalState, d: Channel|null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting: SortingType = 'alpha') => getPublicChannelIds(state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting), (state, lastUnreadChannel, unreadsAtTop = true) => unreadsAtTop, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop = true) => favoritesAtTop, filterChannels); // Private Channels + +export const getPublicChannelIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getPublicChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state: GlobalState, lastUnreadChannel: Channel, unreadsAtTop: boolean, favoritesAtTop: boolean, sorting: SortingType = 'alpha') => sorting, mapAndSortChannelIds); + +export const getSortedPublicChannelIds: (e: GlobalState, d: Channel | null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, (state: GlobalState, lastUnreadChannel: Channel, unreadsAtTop: boolean, favoritesAtTop: boolean, sorting: SortingType = 'alpha') => getPublicChannelIds(state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting), (state, lastUnreadChannel, unreadsAtTop = true) => unreadsAtTop, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop = true) => favoritesAtTop, filterChannels); // Private Channels export const getPrivateChannels: (a: GlobalState) => Array = createSelector(getCurrentUser, getAllChannels, getMyChannelMemberships, getChannelIdsForCurrentTeam, (currentUser, channels, myMembers, teamChannelIds) => { if (!currentUser) { @@ -585,10 +604,12 @@ export const getPrivateChannels: (a: GlobalState) => Array = createSele }).map((id) => channels[id]); return privateChannels; }); -export const getPrivateChannelIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getPrivateChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting: SortingType = 'alpha') => sorting, mapAndSortChannelIds); -export const getSortedPrivateChannelIds: (e: GlobalState, d: Channel|null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting: SortingType = 'alpha') => getPrivateChannelIds(state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting), (state, lastUnreadChannel, unreadsAtTop = true) => unreadsAtTop, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop = true) => favoritesAtTop, filterChannels); // Direct Messages -export const getDirectChannels: (a: GlobalState) => Array = createSelector(getCurrentUser, getUsers, getUserIdsInChannels, getAllChannels, getVisibleTeammate, getVisibleGroupIds, getTeammateNameDisplaySetting, getConfig, getMyPreferences, getLastPostPerChannel, getCurrentChannelId, (currentUser: UserProfile, profiles: IDMappedObjects, userIdsInChannels: any, channels: IDMappedObjects, teammates: Array, groupIds: Array, settings: any, config: any, preferences: { +export const getPrivateChannelIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getPrivateChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state: GlobalState, lastUnreadChannel: Channel, unreadsAtTop: boolean, favoritesAtTop: boolean, sorting: SortingType = 'alpha') => sorting, mapAndSortChannelIds); + +export const getSortedPrivateChannelIds: (e: GlobalState, d: Channel | null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, (state: GlobalState, lastUnreadChannel: Channel, unreadsAtTop: boolean, favoritesAtTop: boolean, sorting: SortingType = 'alpha') => getPrivateChannelIds(state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting), (state, lastUnreadChannel, unreadsAtTop = true) => unreadsAtTop, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop = true) => favoritesAtTop, filterChannels); // Direct Messages + +export const getDirectChannels: (a: GlobalState) => Array = createSelector(getCurrentUser, getUsers, getUserIdsInChannels, getAllChannels, getVisibleTeammate, getVisibleGroupIds, getTeammateNameDisplaySetting, getConfig, getMyPreferences, getLastPostPerChannel, getCurrentChannelId, (currentUser: UserProfile, profiles: IDMappedObjects, userIdsInChannels: any, channels: IDMappedObjects, teammates: Array, groupIds: Array, settings, config, preferences: { [x: string]: PreferenceType; }, lastPosts: RelationOneToOne, currentChannelId: string): Array => { if (!currentUser) { @@ -623,7 +644,7 @@ export const getDirectChannels: (a: GlobalState) => Array = createSelec return false; }).concat(directChannelsIds).map((id) => { const channel = channels[id]; - return completeDirectChannelDisplayName(currentUser.id, profiles, userIdsInChannels[id], settings, channel); + return completeDirectChannelDisplayName(currentUser.id, profiles, userIdsInChannels[id], settings!, channel); }); return directChannels; }); // getDirectAndGroupChannels returns all direct and group channels, even if they have been manually @@ -639,10 +660,13 @@ export const getDirectAndGroupChannels: (a: GlobalState) => Array = cre return []; } - return Object.keys(channels).map((key) => channels[key]).filter((channel: Channel): boolean => Boolean(channel)).filter((channel: Channel): boolean => channel.type === General.DM_CHANNEL || channel.type === General.GM_CHANNEL).map((channel: Channel): Channel => completeDirectChannelDisplayName(currentUser.id, profiles, userIdsInChannels[channel.id], settings, channel)); + return Object.keys(channels).map((key) => channels[key]).filter((channel: Channel): boolean => Boolean(channel)).filter((channel: Channel): boolean => channel.type === General.DM_CHANNEL || channel.type === General.GM_CHANNEL).map((channel: Channel): Channel => completeDirectChannelDisplayName(currentUser.id, profiles, userIdsInChannels[channel.id], settings!, channel)); }); -export const getDirectChannelIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getDirectChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting: SortingType = 'alpha') => sorting, mapAndSortChannelIds); -export const getSortedDirectChannelIds: (e: GlobalState, d: Channel|null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting: SortingType = 'alpha') => getDirectChannelIds(state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting), (state, lastUnreadChannel, unreadsAtTop = true) => unreadsAtTop, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop = true) => favoritesAtTop, filterChannels); + +export const getDirectChannelIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getDirectChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state: GlobalState, lastUnreadChannel: Channel, unreadsAtTop: boolean, favoritesAtTop: boolean, sorting: SortingType = 'alpha') => sorting, mapAndSortChannelIds); + +export const getSortedDirectChannelIds: (e: GlobalState, d: Channel | null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, (state: GlobalState, lastUnreadChannel: Channel, unreadsAtTop: boolean, favoritesAtTop: boolean, sorting: SortingType = 'alpha') => getDirectChannelIds(state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting), (state, lastUnreadChannel, unreadsAtTop = true) => unreadsAtTop, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop = true) => favoritesAtTop, filterChannels); + export function getGroupOrDirectChannelVisibility(state: GlobalState, channelId: string): boolean { return isGroupOrDirectChannelVisible(getChannel(state, channelId), getMyChannelMemberships(state), getConfig(state), getMyPreferences(state), getCurrentUser(state).id, getUsers(state), getLastPostPerChannel(state)); } @@ -695,11 +719,12 @@ const getAllActiveChannels = createSelector(getPublicChannels, getPrivateChannel const allChannels = [...publicChannels, ...privateChannels, ...directChannels]; return allChannels; }); -export const getAllChannelIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getAllActiveChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting: SortingType = 'alpha') => sorting, mapAndSortChannelIds); -export const getAllSortedChannelIds: (e: GlobalState, d: Channel| null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting: SortingType = 'alpha') => getAllChannelIds(state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting), (state, lastUnreadChannel, unreadsAtTop = true) => unreadsAtTop, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop = true) => favoritesAtTop, filterChannels); -let lastChannels; +export const getAllChannelIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getAllActiveChannels, getCurrentUser, getMyChannelMemberships, getLastPostPerChannel, (state: GlobalState, lastUnreadChannel: Channel, unreadsAtTop: boolean, favoritesAtTop: boolean, sorting: SortingType = 'alpha') => sorting, mapAndSortChannelIds); -const hasChannelsChanged = (channels) => { +export const getAllSortedChannelIds: (e: GlobalState, d: Channel | null, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, (state: GlobalState, lastUnreadChannel: Channel, unreadsAtTop: boolean, favoritesAtTop: boolean, sorting: SortingType = 'alpha') => getAllChannelIds(state, lastUnreadChannel, unreadsAtTop, favoritesAtTop, sorting), (state, lastUnreadChannel, unreadsAtTop = true) => unreadsAtTop, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop = true) => favoritesAtTop, filterChannels); +let lastChannels: {type: string;name: string;items: string[]}[]; + +const hasChannelsChanged = (channels: {type: string;name: string;items: string[]}[]) => { if (!lastChannels || lastChannels.length !== channels.length) { return true; } @@ -714,7 +739,7 @@ const hasChannelsChanged = (channels) => { }; export const getOrderedChannelIds = (state: GlobalState, lastUnreadChannel: Channel|null, grouping: 'by_type' | 'none', sorting: SortingType, unreadsAtTop: boolean, favoritesAtTop: boolean) => { - const channels: {type;name;items}[] = []; + const channels: {type: string;name: string;items: string[]}[] = []; if (grouping === 'by_type') { channels.push({ @@ -770,21 +795,27 @@ export const getOrderedChannelIds = (state: GlobalState, lastUnreadChannel: Chan } return lastChannels; -}; // Added for backwards compatibility +}; + +// Added for backwards compatibility // Can be removed once webapp includes new sidebar preferences export const getSortedPublicChannelWithUnreadsIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, getPublicChannelIds, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop = true) => favoritesAtTop, (unreadChannelIds, favoritePreferences, publicChannelIds, favoritesAtTop) => { return filterChannels(unreadChannelIds, favoritePreferences, publicChannelIds, false, favoritesAtTop); }); + export const getSortedPrivateChannelWithUnreadsIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, getPrivateChannelIds, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop = true) => favoritesAtTop, (unreadChannelIds, favoritePreferences, privateChannelId, favoritesAtTop) => { return filterChannels(unreadChannelIds, favoritePreferences, privateChannelId, false, favoritesAtTop); }); + export const getSortedFavoriteChannelWithUnreadsIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoriteChannelIds, (unreadChannelIds, favoriteChannelIds) => favoriteChannelIds); + export const getSortedDirectChannelWithUnreadsIds: (e: GlobalState, d: Channel, c: boolean, b: boolean, a: SortingType) => Array = createIdsSelector(getUnreadChannelIds, getFavoritesPreferences, getDirectChannelIds, (state, lastUnreadChannel, unreadsAtTop, favoritesAtTop = true) => favoritesAtTop, (unreadChannelIds, favoritePreferences, directChannelIds, favoritesAtTop) => { return filterChannels(unreadChannelIds, favoritePreferences, directChannelIds, false, favoritesAtTop); }); + export const getDefaultChannelForTeams: (a: GlobalState) => RelationOneToOne = createSelector(getAllChannels, (channels: IDMappedObjects): RelationOneToOne => { - const result = {}; + const result: RelationOneToOne = {}; for (const channel of Object.keys(channels).map((key) => channels[key])) { if (channel && channel.name === General.DEFAULT_CHANNEL) { @@ -794,13 +825,13 @@ export const getDefaultChannelForTeams: (a: GlobalState) => RelationOneToOne RelationOneToOne = createSelector(getAllChannels, getMyChannelMemberships, getMyTeams, getCurrentUser, (allChannels: IDMappedObjects, myChannelMemberships: RelationOneToOne, myTeams: Array, currentUser: UserProfile): RelationOneToOne => { const locale = currentUser.locale || General.DEFAULT_LOCALE; - const result = {}; + const result: RelationOneToOne = {}; for (const team of myTeams) { // Get a sorted array of all channels in the team that the current user is a member of - // $FlowFixMe const teamChannels = Object.values(allChannels).filter((channel: Channel) => channel && channel.team_id === team.id && Boolean(myChannelMemberships[channel.id])).sort(sortChannelsByDisplayName.bind(null, locale)); if (teamChannels.length === 0) { diff --git a/src/selectors/entities/files.ts b/src/selectors/entities/files.ts index 2bba7cee1..07f6647fd 100644 --- a/src/selectors/entities/files.ts +++ b/src/selectors/entities/files.ts @@ -8,6 +8,7 @@ import {getCurrentUserLocale} from 'selectors/entities/i18n'; import {sortFileInfos} from 'utils/file_utils'; import * as types from 'types'; +import {GlobalState} from 'types/store'; function getAllFiles(state: types.store.GlobalState) { return state.entities.files.files; @@ -21,7 +22,7 @@ function getFilesIdsForPost(state: types.store.GlobalState, postId: string) { return []; } -export function getFilePublicLink(state) { +export function getFilePublicLink(state: GlobalState) { return state.entities.files.filePublicLink; } diff --git a/src/selectors/entities/general.ts b/src/selectors/entities/general.ts index 4692b5ec3..0217c84cb 100644 --- a/src/selectors/entities/general.ts +++ b/src/selectors/entities/general.ts @@ -4,7 +4,9 @@ import {createSelector} from 'reselect'; import {isMinimumServerVersion} from 'utils/helpers'; import {General} from '../../constants'; import {GlobalState} from 'types/store'; -export function getConfig(state: GlobalState): any { +import {Config} from 'types/config'; + +export function getConfig(state: GlobalState): Partial { return state.entities.general.config; } @@ -39,7 +41,7 @@ export function hasNewPermissions(state: GlobalState): boolean { export const canUploadFilesOnMobile: (a: GlobalState) => boolean = createSelector( getConfig, getLicense, - (config: any, license: any): boolean => { + (config: Config, license: any): boolean => { // Defaults to true if either setting doesn't exist return config.EnableFileAttachments !== 'false' && (license.IsLicensed === 'false' || license.Compliance === 'false' || config.EnableMobileFileUpload !== 'false'); @@ -49,7 +51,7 @@ export const canUploadFilesOnMobile: (a: GlobalState) => boolean = createSelecto export const canDownloadFilesOnMobile: (a: GlobalState) => boolean = createSelector( getConfig, getLicense, - (config: any, license: any): boolean => { + (config: Config, license: any): boolean => { // Defaults to true if the setting doesn't exist return license.IsLicensed === 'false' || license.Compliance === 'false' || config.EnableMobileFileDownload !== 'false'; } @@ -57,7 +59,7 @@ export const canDownloadFilesOnMobile: (a: GlobalState) => boolean = createSelec export const getAutolinkedUrlSchemes: (a: GlobalState) => string[] = createSelector( getConfig, - (config: any): string[] => { + (config: Config): string[] => { if (!config.CustomUrlSchemes) { return General.DEFAULT_AUTOLINKED_URL_SCHEMES; } diff --git a/src/selectors/entities/groups.ts b/src/selectors/entities/groups.ts index d93647e03..13078ee84 100644 --- a/src/selectors/entities/groups.ts +++ b/src/selectors/entities/groups.ts @@ -2,21 +2,21 @@ // See LICENSE.txt for license information. import * as reselect from 'reselect'; import {GlobalState} from 'types/store'; -const emptyList = []; +const emptyList: any[] = []; const emptySyncables = { teams: [], channels: [], }; -export function getAllGroups(state) { +export function getAllGroups(state: GlobalState) { return state.entities.groups.groups; } -export function getGroup(state, id) { +export function getGroup(state: GlobalState, id: string) { return getAllGroups(state)[id]; } -export function getGroupMemberCount(state, id) { +export function getGroupMemberCount(state: GlobalState, id: string) { const memberData = state.entities.groups.members; const groupMemberData = memberData[id]; if (!groupMemberData) { @@ -25,19 +25,19 @@ export function getGroupMemberCount(state, id) { return memberData[id].totalMemberCount; } -function getGroupSyncables(state, id) { +function getGroupSyncables(state: GlobalState, id: string) { return state.entities.groups.syncables[id] || emptySyncables; } -export function getGroupTeams(state, id) { +export function getGroupTeams(state: GlobalState, id: string) { return getGroupSyncables(state, id).teams; } -export function getGroupChannels(state, id) { +export function getGroupChannels(state: GlobalState, id: string) { return getGroupSyncables(state, id).channels; } -export function getGroupMembers(state, id) { +export function getGroupMembers(state: GlobalState, id: string) { const groupMemberData = state.entities.groups.members[id]; if (!groupMemberData) { return emptyList; @@ -45,9 +45,9 @@ export function getGroupMembers(state, id) { return groupMemberData.members; } -const teamGroupIDs = (state: GlobalState, teamID) => (state.entities.teams.groupsAssociatedToTeam[teamID] == null ? undefined : state.entities.teams.groupsAssociatedToTeam[teamID].ids == null ? undefined : state.entities.teams.groupsAssociatedToTeam[teamID].ids) || []; +const teamGroupIDs = (state: GlobalState, teamID: string) => (state.entities.teams.groupsAssociatedToTeam[teamID] == null ? undefined : state.entities.teams.groupsAssociatedToTeam[teamID].ids == null ? undefined : state.entities.teams.groupsAssociatedToTeam[teamID].ids) || []; -const channelGroupIDs = (state: GlobalState, channelID) => (state.entities.channels.groupsAssociatedToChannel[channelID] == null ? undefined : state.entities.channels.groupsAssociatedToChannel[channelID].ids == null ? undefined : state.entities.channels.groupsAssociatedToChannel[channelID].ids) || []; +const channelGroupIDs = (state: GlobalState, channelID: string) => (state.entities.channels.groupsAssociatedToChannel[channelID] == null ? undefined : state.entities.channels.groupsAssociatedToChannel[channelID].ids == null ? undefined : state.entities.channels.groupsAssociatedToChannel[channelID].ids) || []; const getTeamGroupIDSet = reselect.createSelector( teamGroupIDs, @@ -61,7 +61,7 @@ const getChannelGroupIDSet = reselect.createSelector( export const getGroupsNotAssociatedToTeam = reselect.createSelector( getAllGroups, - (state, teamID) => getTeamGroupIDSet(state, teamID), + (state: GlobalState, teamID: string) => getTeamGroupIDSet(state, teamID), (allGroups, teamGroupIDSet) => { return Object.entries(allGroups).filter(([groupID]) => !teamGroupIDSet.has(groupID)).map((entry) => entry[1]); } @@ -69,7 +69,7 @@ export const getGroupsNotAssociatedToTeam = reselect.createSelector( export const getGroupsAssociatedToTeam = reselect.createSelector( getAllGroups, - (state, teamID) => getTeamGroupIDSet(state, teamID), + (state: GlobalState, teamID: string) => getTeamGroupIDSet(state, teamID), (allGroups, teamGroupIDSet) => { return Object.entries(allGroups).filter(([groupID]) => teamGroupIDSet.has(groupID)).map((entry) => entry[1]); } @@ -77,7 +77,7 @@ export const getGroupsAssociatedToTeam = reselect.createSelector( export const getGroupsNotAssociatedToChannel = reselect.createSelector( getAllGroups, - (state, channelID) => getChannelGroupIDSet(state, channelID), + (state: GlobalState, channelID: string) => getChannelGroupIDSet(state, channelID), (allGroups, channelGroupIDSet) => { return Object.entries(allGroups).filter(([groupID]) => !channelGroupIDSet.has(groupID)).map((entry) => entry[1]); } @@ -85,7 +85,7 @@ export const getGroupsNotAssociatedToChannel = reselect.createSelector( export const getGroupsAssociatedToChannel = reselect.createSelector( getAllGroups, - (state, channelID) => getChannelGroupIDSet(state, channelID), + (state: GlobalState, channelID: string) => getChannelGroupIDSet(state, channelID), (allGroups, channelGroupIDSet) => { return Object.entries(allGroups).filter(([groupID]) => channelGroupIDSet.has(groupID)).map((entry) => entry[1]); } diff --git a/src/selectors/entities/integrations.test.js b/src/selectors/entities/integrations.test.js index 348913d25..6a0091256 100644 --- a/src/selectors/entities/integrations.test.js +++ b/src/selectors/entities/integrations.test.js @@ -5,7 +5,7 @@ import assert from 'assert'; import TestHelper from 'test/test_helper'; import deepFreezeAndThrowOnMutation from 'utils/deep_freeze'; -import {getAllCommands, getAutocompleteCommandsList, getOutgoingHooksInCurrentTeam} from 'selectors/entities/integrations'; +import {getAllCommands, getAutocompleteCommandsList, getOutgoingHooksInCurrentTeam} from './integrations'; describe('Selectors.Integrations', () => { TestHelper.initBasic(); diff --git a/src/selectors/entities/integrations.ts b/src/selectors/entities/integrations.ts index 8a0c872b7..8d5176e54 100644 --- a/src/selectors/entities/integrations.ts +++ b/src/selectors/entities/integrations.ts @@ -33,7 +33,7 @@ export const getOutgoingHooksInCurrentTeam = reselect.createSelector( getCurrentTeamId, getOutgoingHooks, (teamId, hooks) => { - return Object.values(hooks).filter((o) => o.teamId === teamId); + return Object.values(hooks).filter((o) => o.team_id === teamId); } ); diff --git a/src/selectors/entities/plugins.ts b/src/selectors/entities/plugins.ts index bedd4c93c..4035c505b 100644 --- a/src/selectors/entities/plugins.ts +++ b/src/selectors/entities/plugins.ts @@ -2,14 +2,15 @@ // See LICENSE.txt for license information. import * as reselect from 'reselect'; +import {GlobalState} from 'types/store'; -export function getMarketplacePlugins(state) { +export function getMarketplacePlugins(state: GlobalState) { return state.entities.plugins.marketplacePlugins; } export const getMarketplaceInstalledPlugins = reselect.createSelector( getMarketplacePlugins, (plugins) => { - return Object.values(plugins).filter((p: any) => p.installed_version !== ''); + return Object.values(plugins).filter((p) => p.installed_version !== ''); } ); diff --git a/src/selectors/entities/posts.ts b/src/selectors/entities/posts.ts index d75e24b58..9f16489ef 100644 --- a/src/selectors/entities/posts.ts +++ b/src/selectors/entities/posts.ts @@ -8,11 +8,11 @@ import {Posts, Preferences} from '../../constants'; import {isPostEphemeral, isSystemMessage, shouldFilterJoinLeavePost, comparePosts, isPostPendingOrFailed, isPostCommentMention} from 'utils/post_utils'; import {getPreferenceKey} from 'utils/preference_utils'; import {GlobalState} from 'types/store'; -import {Post, PostWithFormatData} from 'types/posts'; +import {Post, PostWithFormatData, MessageHistory, PostOrderBlock} from 'types/posts'; import {Reaction} from 'types/reactions'; import {UserProfile} from 'types/users'; import {Channel} from 'types/channels'; -import {$ID, IDMappedObjects, RelationOneToOne, RelationOneToMany} from 'types/utilities'; +import {$ID, IDMappedObjects, RelationOneToOne, RelationOneToMany, Dictionary} from 'types/utilities'; export function getAllPosts(state: GlobalState) { return state.entities.posts.posts; } @@ -34,7 +34,7 @@ export function getReactionsForPosts(state: GlobalState): RelationOneToOne) => { [x: string]: Reaction; } | undefined | null { - return createSelector(getReactionsForPosts, (state: GlobalState, postId) => postId, (reactions, postId) => { + return createSelector(getReactionsForPosts, (state: GlobalState, postId: string) => postId, (reactions, postId) => { if (reactions[postId]) { return reactions[postId]; } @@ -68,7 +68,7 @@ export const getPostsInCurrentChannel: (a: GlobalState) => Array) => Array<$ID> { return createIdsSelector( getAllPosts, - (state: GlobalState, rootId) => state.entities.posts.postsInThread[rootId] || [], + (state: GlobalState, rootId: string) => state.entities.posts.postsInThread[rootId] || [], (state: GlobalState, rootId) => state.entities.posts.posts[rootId], (posts, postsForThread, rootPost) => { const thread: Post[] = []; @@ -91,9 +91,9 @@ export function makeGetPostIdsForThread(): (b: GlobalState, a: $ID) => Arr ); } -export function makeGetPostsChunkAroundPost(): (c: GlobalState, b: $ID, a: $ID) => any { +export function makeGetPostsChunkAroundPost(): (c: GlobalState, b: $ID, a: $ID) => PostOrderBlock| null | undefined { return createIdsSelector( - (state: GlobalState, postId, channelId) => state.entities.posts.postsInChannel[channelId], + (state: GlobalState, postId: string, channelId: string) => state.entities.posts.postsInChannel[channelId], (state: GlobalState, postId) => postId, (postsForChannel, postId) => { if (!postsForChannel) { @@ -123,7 +123,7 @@ export function makeGetPostIdsAroundPost(): (d: GlobalState, c: $ID, b: $I }) => Array<$ID> | undefined | null { const getPostsChunkAroundPost = makeGetPostsChunkAroundPost(); return createIdsSelector( - (state: GlobalState, postId, channelId) => getPostsChunkAroundPost(state, postId, channelId), + (state: GlobalState, postId: string, channelId: string) => getPostsChunkAroundPost(state, postId, channelId), (state: GlobalState, postId) => postId, (state: GlobalState, postId, channelId, options) => options && options.postsBeforeCount, (state: GlobalState, postId, channelId, options) => options && options.postsAfterCount, @@ -148,7 +148,7 @@ function formatPostInChannel(post: Post, previousPost: Post | undefined | null, let isFirstReply = false; let isLastReply = false; let highlight = false; - let commentedOnPost; + let commentedOnPost: Post| undefined; if (post.id === focusedPostId) { highlight = true; @@ -276,7 +276,7 @@ export function makeGetPostsAroundPost(): (c: GlobalState, b: $ID, a: $ID< }; return createSelector( - (state: GlobalState, focusedPostId, channelId) => getPostIdsAroundPost(state, focusedPostId, channelId, options), + (state: GlobalState, focusedPostId: string, channelId: string) => getPostIdsAroundPost(state, focusedPostId, channelId, options), getAllPosts, getPostsInThread, (state: GlobalState, focusedPostId) => focusedPostId, @@ -318,7 +318,7 @@ export function makeGetPostsForThread(): (b: GlobalState, a: { }) => Array { return createSelector(getAllPosts, (state: GlobalState, { rootId, - }) => state.entities.posts.postsInThread[rootId] || [], (state: GlobalState, { + }: {rootId: string}) => state.entities.posts.postsInThread[rootId] || [], (state: GlobalState, { rootId, }) => state.entities.posts.posts[rootId], (posts, postsForThread, rootPost) => { const thread: Post[] = []; @@ -344,7 +344,7 @@ export function makeGetCommentCountForPost(): (b: GlobalState, a: { }) => number { return createSelector( getAllPosts, - (state, {post}) => state.entities.posts.postsInThread[post ? post.id : ''] || [], + (state: GlobalState, {post}: {post: Post}) => state.entities.posts.postsInThread[post ? post.id : ''] || [], (state, props) => props, (posts, postsForThread, {post: currentPost}) => { if (!currentPost) { @@ -385,10 +385,10 @@ export function getSearchMatches(state: GlobalState): { return state.entities.search.matches; } -export function makeGetMessageInHistoryItem(type: string): (a: GlobalState) => string { +export function makeGetMessageInHistoryItem(type: 'post'|'comment'): (a: GlobalState) => string { return createSelector( (state: GlobalState) => state.entities.posts.messagesHistory, - (messagesHistory) => { + (messagesHistory: MessageHistory) => { const idx = messagesHistory.index[type]; const messages = messagesHistory.messages; if (idx >= 0 && messages && messages.length > idx) { @@ -402,7 +402,7 @@ export function makeGetMessageInHistoryItem(type: string): (a: GlobalState) => s export function makeGetPostsForIds(): (b: GlobalState, a: Array<$ID>) => Array { return createIdsSelector( getAllPosts, - (state: GlobalState, postIds) => postIds, + (state: GlobalState, postIds: Array<$ID>) => postIds, (allPosts, postIds) => { if (!postIds) { return []; @@ -417,10 +417,10 @@ export const getLastPostPerChannel: (a: GlobalState) => RelationOneToOne state.entities.posts.postsInChannel, (allPosts, postsInChannel) => { - const ret = {}; + const ret: Dictionary = {}; for (const [channelId, postsForChannel] of Object.entries(postsInChannel)) { - const recentBlock = (postsForChannel as any).find((block) => block.recent); + const recentBlock = (postsForChannel).find((block) => block.recent); if (!recentBlock) { continue; } @@ -434,7 +434,7 @@ export const getLastPostPerChannel: (a: GlobalState) => RelationOneToOne) => $ID | undefined | null = createSelector(getAllPosts, (state: GlobalState, channelId) => getPostIdsInChannel(state, channelId), getMyPreferences, (posts, postIdsInChannel, preferences) => { +export const getMostRecentPostIdInChannel: (b: GlobalState, a: $ID) => $ID | undefined | null = createSelector(getAllPosts, (state: GlobalState, channelId: string) => getPostIdsInChannel(state, channelId), getMyPreferences, (posts, postIdsInChannel, preferences) => { if (!postIdsInChannel) { return ''; } @@ -474,7 +474,8 @@ export const getLatestReplyablePostId: (a: GlobalState) => $ID = createSel return latestReplyablePost.id; } ); -export const getCurrentUsersLatestPost: (b: GlobalState, a: $ID) => PostWithFormatData | undefined | null = createSelector(getPostsInCurrentChannel, getCurrentUser, (_, rootId) => rootId, (posts, currentUser, rootId) => { + +export const getCurrentUsersLatestPost: (b: GlobalState, a: $ID) => PostWithFormatData | undefined | null = createSelector(getPostsInCurrentChannel, getCurrentUser, (_: any, rootId: string) => rootId, (posts, currentUser, rootId) => { if (!posts) { return null; } @@ -493,7 +494,8 @@ export const getCurrentUsersLatestPost: (b: GlobalState, a: $ID) => PostWi }); return lastPost; }); -export function getRecentPostsChunkInChannel(state: GlobalState, channelId: $ID): any { + +export function getRecentPostsChunkInChannel(state: GlobalState, channelId: $ID): PostOrderBlock|null|undefined { const postsForChannel = state.entities.posts.postsInChannel[channelId]; if (!postsForChannel) { @@ -503,7 +505,7 @@ export function getRecentPostsChunkInChannel(state: GlobalState, channelId: $ID< return postsForChannel.find((block) => block.recent); } -export function getOldestPostsChunkInChannel(state: GlobalState, channelId: $ID): any { +export function getOldestPostsChunkInChannel(state: GlobalState, channelId: $ID): PostOrderBlock|null|undefined { const postsForChannel = state.entities.posts.postsInChannel[channelId]; if (!postsForChannel) { diff --git a/src/selectors/entities/preferences.ts b/src/selectors/entities/preferences.ts index e8746ec63..085bffeb8 100644 --- a/src/selectors/entities/preferences.ts +++ b/src/selectors/entities/preferences.ts @@ -8,11 +8,12 @@ import {createShallowSelector} from 'utils/helpers'; import {getPreferenceKey} from 'utils/preference_utils'; import {GlobalState} from 'types/store'; import {PreferencesType, PreferenceType} from 'types/preferences'; -export function getMyPreferences(state) { + +export function getMyPreferences(state: GlobalState) { return state.entities.preferences.myPreferences; } -export function get(state: GlobalState, category, name, defaultValue: any = '') { +export function get(state: GlobalState, category: string, name: string, defaultValue: any = '') { const key = getPreferenceKey(category, name); const prefs = getMyPreferences(state); @@ -23,12 +24,12 @@ export function get(state: GlobalState, category, name, defaultValue: any = '') return prefs[key].value; } -export function getBool(state, category, name, defaultValue = false) { +export function getBool(state: GlobalState, category: string, name: string, defaultValue = false) { const value = get(state, category, name, String(defaultValue)); return value !== 'false'; } -export function getInt(state, category, name, defaultValue = 0) { +export function getInt(state: GlobalState, category: string, name: string, defaultValue = 0) { const value = get(state, category, name, defaultValue); return parseInt(value, 10); } @@ -36,7 +37,7 @@ export function getInt(state, category, name, defaultValue = 0) { export function makeGetCategory() { return reselect.createSelector( getMyPreferences, - (state, category) => category, + (state: GlobalState, category: string) => category, (preferences, category) => { const prefix = category + '--'; const prefsInCategory: PreferenceType[] = []; @@ -54,19 +55,19 @@ export function makeGetCategory() { const getDirectShowCategory = makeGetCategory(); -export function getDirectShowPreferences(state) { +export function getDirectShowPreferences(state: GlobalState) { return getDirectShowCategory(state, Preferences.CATEGORY_DIRECT_CHANNEL_SHOW); } const getGroupShowCategory = makeGetCategory(); -export function getGroupShowPreferences(state) { +export function getGroupShowPreferences(state: GlobalState) { return getGroupShowCategory(state, Preferences.CATEGORY_GROUP_CHANNEL_SHOW); } const getFavoritesCategory = makeGetCategory(); -export function getFavoritesPreferences(state) { +export function getFavoritesPreferences(state: GlobalState) { const favorites = getFavoritesCategory(state, Preferences.CATEGORY_FAVORITE_CHANNEL); return favorites.filter((f) => f.value === 'true').map((f) => f.name); } @@ -134,7 +135,7 @@ export const getTheme = createShallowSelector( getThemePreference, getDefaultTheme, (themePreference, defaultTheme) => { - let theme; + let theme: any; if (themePreference) { theme = themePreference.value; } else { @@ -150,7 +151,7 @@ export const getTheme = createShallowSelector( // If this is a system theme, find it in case the user's theme is missing any fields if (theme.type && theme.type !== 'custom') { - const match = Object.values(Preferences.THEMES).find((v) => v.type === theme.type); + const match = Object.values(Preferences.THEMES).find((v: any) => v.type === theme.type) as any; if (match) { if (!match.mentionBg) { match.mentionBg = match.mentionBj; @@ -179,7 +180,7 @@ export const getTheme = createShallowSelector( export function makeGetStyleFromTheme() { return reselect.createSelector( getTheme, - (state, getStyleFromTheme) => getStyleFromTheme, + (state: GlobalState, getStyleFromTheme: Function) => getStyleFromTheme, (theme, getStyleFromTheme) => { return getStyleFromTheme(theme); } diff --git a/src/selectors/entities/roles.ts b/src/selectors/entities/roles.ts index 7a9587267..3e10d70ec 100644 --- a/src/selectors/entities/roles.ts +++ b/src/selectors/entities/roles.ts @@ -4,14 +4,19 @@ import * as reselect from 'reselect'; import {getCurrentUser, getCurrentChannelId} from 'selectors/entities/common'; import {getTeamMemberships, getCurrentTeamId} from './teams'; import * as types from 'types'; -import {getMySystemPermissions, getMySystemRoles, getRoles} from 'selectors/entities/roles_helpers'; +import {getMySystemPermissions, getMySystemRoles, getRoles, PermissionsOptions} from 'selectors/entities/roles_helpers'; +import {GlobalState} from 'types/store'; +import {Dictionary} from 'types/utilities'; +import {Role} from 'types/roles'; +import {Channel} from 'types/channels'; +import {Team} from 'types/teams'; export {getMySystemPermissions, getMySystemRoles, getRoles}; export const getMyTeamRoles = reselect.createSelector( getTeamMemberships, (teamsMemberships) => { - const roles = {}; + const roles: Dictionary> = {}; if (teamsMemberships) { for (const key in teamsMemberships) { if (teamsMemberships.hasOwnProperty(key) && teamsMemberships[key].roles) { @@ -26,7 +31,7 @@ export const getMyTeamRoles = reselect.createSelector( export const getMyChannelRoles = reselect.createSelector( (state: types.store.GlobalState) => state.entities.channels.myMembers, (channelsMemberships) => { - const roles = {}; + const roles: Dictionary> = {}; if (channelsMemberships) { for (const key in channelsMemberships) { if (channelsMemberships.hasOwnProperty(key) && channelsMemberships[key].roles) { @@ -54,7 +59,7 @@ export const getMyRoles = reselect.createSelector( export const getRolesById = reselect.createSelector( getRoles, (rolesByName) => { - const rolesById = {}; + const rolesById: Dictionary = {}; for (const role of Object.values(rolesByName)) { rolesById[role.id] = role; } @@ -112,11 +117,11 @@ export const getMyTeamPermissions = reselect.createSelector( getMyTeamRoles, getRoles, getMySystemPermissions, - (state, options) => options.team, + (state: GlobalState, options: PermissionsOptions) => options.team, (myTeamRoles, roles, systemPermissions, teamId) => { const permissions = new Set(); - if (myTeamRoles[teamId]) { - for (const roleName of myTeamRoles[teamId]) { + if (myTeamRoles[teamId!]) { + for (const roleName of myTeamRoles[teamId!]) { if (roles[roleName]) { for (const permission of roles[roleName].permissions) { permissions.add(permission); @@ -135,11 +140,11 @@ export const getMyChannelPermissions = reselect.createSelector( getMyChannelRoles, getRoles, getMyTeamPermissions, - (state, options) => options.channel, + (state, options: PermissionsOptions) => options.channel, (myChannelRoles, roles, teamPermissions, channelId) => { const permissions = new Set(); - if (myChannelRoles[channelId]) { - for (const roleName of myChannelRoles[channelId]) { + if (myChannelRoles[channelId!]) { + for (const roleName of myChannelRoles[channelId!]) { if (roles[roleName]) { for (const permission of roles[roleName].permissions) { permissions.add(permission); @@ -156,7 +161,7 @@ export const getMyChannelPermissions = reselect.createSelector( export const haveISystemPermission = reselect.createSelector( getMySystemPermissions, - (state, options) => options.permission, + (state: GlobalState, options: PermissionsOptions) => options.permission, (permissions, permission) => { return permissions.has(permission); } @@ -180,7 +185,7 @@ export const haveIChannelPermission = reselect.createSelector( export const haveICurrentTeamPermission = reselect.createSelector( getMyCurrentTeamPermissions, - (state, options) => options.permission, + (state: GlobalState, options: PermissionsOptions) => options.permission, (permissions, permission) => { return permissions.has(permission); } @@ -188,7 +193,7 @@ export const haveICurrentTeamPermission = reselect.createSelector( export const haveICurrentChannelPermission = reselect.createSelector( getMyCurrentChannelPermissions, - (state, options) => options.permission, + (state: GlobalState, options: PermissionsOptions) => options.permission, (permissions, permission) => { return permissions.has(permission); } diff --git a/src/selectors/entities/roles_helpers.ts b/src/selectors/entities/roles_helpers.ts index cc4f2e3b1..6cab7b8b5 100644 --- a/src/selectors/entities/roles_helpers.ts +++ b/src/selectors/entities/roles_helpers.ts @@ -5,6 +5,12 @@ import {GlobalState} from 'types/store'; import {getCurrentUser} from 'selectors/entities/common'; import * as types from 'types'; +export type PermissionsOptions = { + channel?: string; + team?: string; + permission: string; +}; + export function getRoles(state: GlobalState) { return state.entities.roles.roles; } @@ -31,6 +37,6 @@ export const getMySystemPermissions = reselect.createSelector(getMySystemRoles, return permissions; }); -export const haveISystemPermission = reselect.createSelector(getMySystemPermissions, (state, options) => options.permission, (permissions, permission) => { +export const haveISystemPermission = reselect.createSelector(getMySystemPermissions, (state: GlobalState, options: PermissionsOptions) => options.permission, (permissions, permission) => { return permissions.has(permission); }); diff --git a/src/selectors/entities/schemes.ts b/src/selectors/entities/schemes.ts index 4c30ac997..818de62a4 100644 --- a/src/selectors/entities/schemes.ts +++ b/src/selectors/entities/schemes.ts @@ -9,6 +9,7 @@ import {GlobalState} from 'types/store'; import {Scheme} from 'types/schemes'; import {Channel} from 'types/channels'; import {Team} from 'types/teams'; + export function getSchemes(state: GlobalState): { [x: string]: Scheme; } { @@ -23,7 +24,7 @@ export function getScheme(state: GlobalState, id: string): Scheme { export function makeGetSchemeChannels() { return (createSelector( getAllChannels, - (state, props: {schemeId: string}) => getScheme(state, props.schemeId), + (state: GlobalState, props: {schemeId: string}) => getScheme(state, props.schemeId), (allChannels, scheme) => { if (!scheme) { return []; @@ -53,7 +54,7 @@ export function makeGetSchemeChannels() { export function makeGetSchemeTeams() { return (createSelector( getTeams, - (state, props: {schemeId: string}) => getScheme(state, props.schemeId), + (state: GlobalState, props: {schemeId: string}) => getScheme(state, props.schemeId), (allTeams, scheme) => { if (!scheme) { return []; diff --git a/src/selectors/entities/timezone.ts b/src/selectors/entities/timezone.ts index eb7718c0f..189c73e34 100644 --- a/src/selectors/entities/timezone.ts +++ b/src/selectors/entities/timezone.ts @@ -1,7 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -export function getUserTimezone(state, id) { +import {GlobalState} from 'types/store'; + +export function getUserTimezone(state: GlobalState, id: string) { const profile = state.entities.users.profiles[id]; if (profile && profile.timezone) { @@ -18,7 +20,7 @@ export function getUserTimezone(state, id) { }; } -export function isTimezoneEnabled(state) { +export function isTimezoneEnabled(state: GlobalState) { const {config} = state.entities.general; return config.ExperimentalTimezone === 'true'; } diff --git a/src/selectors/entities/users.ts b/src/selectors/entities/users.ts index fd02216f0..12c6925ae 100644 --- a/src/selectors/entities/users.ts +++ b/src/selectors/entities/users.ts @@ -11,7 +11,7 @@ import {UserProfile} from 'types/users'; import {Reaction} from 'types/reactions'; import {Team} from 'types/teams'; import {Channel} from 'types/channels'; -import {RelationOneToOne, RelationOneToMany, IDMappedObjects, UsernameMappedObjects, EmailMappedObjects, $ID, $Username, $Email} from 'types/utilities'; +import {RelationOneToOne, RelationOneToMany, IDMappedObjects, UsernameMappedObjects, EmailMappedObjects, $ID, $Username, $Email, Dictionary} from 'types/utilities'; type Filters = { role?: string; inactive?: boolean; @@ -52,10 +52,10 @@ export function getUser(state: GlobalState, id: $ID): UserProfile { return state.entities.users.profiles[id]; } -export const getUsersByUsername: (GlobalState) => UsernameMappedObjects = createSelector( +export const getUsersByUsername: (a: GlobalState) => UsernameMappedObjects = createSelector( getUsers, (users) => { - const usersByUsername = {}; + const usersByUsername: Dictionary = {}; for (const id in users) { if (users.hasOwnProperty(id)) { @@ -72,10 +72,10 @@ export function getUserByUsername(state: GlobalState, username: $Username EmailMappedObjects = createSelector( +export const getUsersByEmail: (a: GlobalState) => EmailMappedObjects = createSelector( getUsers, (users) => { - const usersByEmail = {}; + const usersByEmail: Dictionary = {}; for (const user of Object.keys(users).map((key) => users[key])) { usersByEmail[user.email] = user; @@ -89,7 +89,7 @@ export function getUserByEmail(state: GlobalState, email: $Email): return getUsersByEmail(state)[email]; } -export const isCurrentUserSystemAdmin: (GlobalState) => boolean = createSelector( +export const isCurrentUserSystemAdmin: (a: GlobalState) => boolean = createSelector( getCurrentUser, (user) => { const roles = user.roles || ''; @@ -113,11 +113,14 @@ export const getCurrentUserRoles: (a: GlobalState) => UserProfile['roles'] = cre return roles.trim(); } ); -export const getCurrentUserMentionKeys: (a: GlobalState) => Array<{ + +export type UserMentionKey= { key: string; caseSensitive?: boolean; -}> = createSelector(getCurrentUser, (user: UserProfile) => { - let keys: {key;caseSensitive?}[] = []; +} + +export const getCurrentUserMentionKeys: (a: GlobalState) => Array = createSelector(getCurrentUser, (user: UserProfile) => { + let keys: UserMentionKey[] = []; if (!user || !user.notify_props) { return keys; @@ -203,7 +206,7 @@ function sortAndInjectProfiles(profiles: IDMappedObjects, profileSe export const getProfiles: (a: GlobalState, b: Filters) => Array = createSelector( getUsers, - (state, filters) => filters, + (state: GlobalState, filters: Filters) => filters, (profiles, filters) => { return sortAndInjectProfiles(filterProfiles(profiles, filters), PROFILE_SET_ALL); } @@ -227,7 +230,7 @@ function filterProfiles(profiles: IDMappedObjects, filters?: Filter return users.reduce((acc, user) => { acc[user.id] = user; return acc; - }, {}); + }, {} as IDMappedObjects); } export const getProfilesInCurrentChannel: (a: GlobalState) => Array = createSelector( @@ -257,8 +260,8 @@ export const getProfilesInCurrentTeam: (a: GlobalState) => Array = export const getProfilesInTeam: (a: GlobalState, b: $ID) => Array = createSelector( getUsers, getUserIdsInTeams, - (state, teamId) => teamId, - (state, teamId, filters) => filters, + (state: GlobalState, teamId: string) => teamId, + (state: GlobalState, teamId, filters) => filters, (profiles, usersInTeams, teamId, filters) => { return sortAndInjectProfiles(filterProfiles(profiles, filters), usersInTeams[teamId] || new Set()); } @@ -275,7 +278,7 @@ export const getProfilesNotInCurrentTeam: (a: GlobalState) => Array export const getProfilesWithoutTeam: (a: GlobalState, filters?: Filters) => Array = createSelector( getUsers, getUserIdsWithoutTeam, - (state, filters) => filters, + (state: GlobalState, filters: Filters) => filters, (profiles, withoutTeamProfileSet, filters) => { return sortAndInjectProfiles(filterProfiles(profiles, filters), withoutTeamProfileSet); } @@ -375,7 +378,7 @@ export const shouldShowTermsOfService: (a: GlobalState) => boolean = createSelec const acceptedAt = user ? user.terms_of_service_create_at : 0; const featureEnabled = license.IsLicensed === 'true' && config.EnableCustomTermsOfService === 'true'; - const reacceptanceTime = config.CustomTermsOfServiceReAcceptancePeriod * 1000 * 60 * 60 * 24; + const reacceptanceTime = parseInt(config.CustomTermsOfServiceReAcceptancePeriod!, 10) * 1000 * 60 * 60 * 24; const timeElapsed = new Date().getTime() - acceptedAt; return Boolean(user && featureEnabled && (config.CustomTermsOfServiceId !== acceptedTermsId || timeElapsed > reacceptanceTime)); } @@ -398,7 +401,7 @@ export const getUsersInVisibleDMs: (a: GlobalState) => Array = crea export function makeGetProfilesForReactions(): (a: GlobalState, b: Array) => Array { return createSelector( getUsers, - (state, reactions) => reactions, + (state: GlobalState, reactions: Array) => reactions, (users, reactions) => { const profiles: UserProfile[] = []; reactions.forEach((r) => { @@ -415,7 +418,7 @@ export function makeGetProfilesInChannel(): (a: GlobalState, b: $ID, c: return createSelector( getUsers, getUserIdsInChannels, - (state, channelId) => channelId, + (state: GlobalState, channelId: string) => channelId, (state, channelId, skipInactive) => skipInactive, (users, userIds, channelId, skipInactive = false) => { const userIdsInChannel = userIds[channelId]; @@ -433,7 +436,7 @@ export function makeGetProfilesNotInChannel(): (a: GlobalState, b: $ID, return createSelector( getUsers, getUserIdsNotInChannels, - (state, channelId) => channelId, + (state: GlobalState, channelId: string) => channelId, (state, channelId, skipInactive) => skipInactive, (users, userIds, channelId, skipInactive = false) => { const userIdsInChannel = userIds[channelId]; @@ -451,9 +454,9 @@ export function makeGetProfilesByIdsAndUsernames(): (a: GlobalState, b: {allUser return createSelector( getUsers, getUsersByUsername, - (state, props) => props.allUserIds, + (state: GlobalState, props: {allUserIds: Array<$ID>; allUsernames: Array<$Username>}) => props.allUserIds, (state, props) => props.allUsernames, - (allProfilesById, allProfilesByUsername, allUserIds, allUsernames) => { + (allProfilesById: Dictionary, allProfilesByUsername: Dictionary, allUserIds: Array, allUsernames: Array) => { const userProfiles: UserProfile[] = []; if (allUserIds && allUserIds.length > 0) { @@ -483,11 +486,11 @@ export function makeGetProfilesByIdsAndUsernames(): (a: GlobalState, b: {allUser export function makeGetDisplayName(): (a: GlobalState, b: $ID, c: boolean) => string { return createSelector( - (state, userId) => getUser(state, userId), + (state: GlobalState, userId: string) => getUser(state, userId), getTeammateNameDisplaySetting, (state, _, useFallbackUsername = true) => useFallbackUsername, (user, teammateNameDisplaySetting, useFallbackUsername) => { - return displayUsername(user, teammateNameDisplaySetting, useFallbackUsername); + return displayUsername(user, teammateNameDisplaySetting!, useFallbackUsername); } ); } diff --git a/src/selectors/errors.ts b/src/selectors/errors.ts index 718a21b81..cb95ce77a 100644 --- a/src/selectors/errors.ts +++ b/src/selectors/errors.ts @@ -1,6 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -export function getDisplayableErrors(state) { +import {GlobalState} from 'types/store'; + +export function getDisplayableErrors(state: GlobalState) { return state.errors.filter((error) => error.displayable); } diff --git a/src/store/configureStore.dev.ts b/src/store/configureStore.dev.ts index 30367ad34..2cbd7793a 100644 --- a/src/store/configureStore.dev.ts +++ b/src/store/configureStore.dev.ts @@ -3,10 +3,10 @@ /* eslint-disable no-undefined */ import * as redux from 'redux'; -import devTools from 'remote-redux-devtools'; import {createOfflineReducer, networkStatusChangedAction, offlineCompose} from 'redux-offline'; import defaultOfflineConfig from 'redux-offline/lib/defaults'; import reducerRegistry from './reducer_registry'; +import devTools from 'remote-redux-devtools'; const windowAny = window as any; @@ -25,6 +25,8 @@ import deepFreezeAndThrowOnMutation from 'utils/deep_freeze'; import initialState from './initial_state'; import {offlineConfig, createReducer} from './helpers'; import {createMiddleware} from './middleware'; +import {Reducer, Action} from 'types/actions'; +import {GlobalState} from 'types/store'; /** * Configures and constructs the redux store. Accepts the following parameters: @@ -37,7 +39,7 @@ import {createMiddleware} from './middleware'; * enableBuffer - bool - default = true - If true, the store will buffer all actions until offline state rehydration occurs. * enableThunk - bool - default = true - If true, include the thunk middleware automatically. If false, thunk must be provided as part of additionalMiddleware. */ -export default function configureServiceStore(preloadedState, appReducer, userOfflineConfig, getAppReducer, clientOptions) { +export default function configureServiceStore(preloadedState: any, appReducer: any, userOfflineConfig: any, getAppReducer: any, clientOptions: any) { const baseOfflineConfig = Object.assign({}, defaultOfflineConfig, offlineConfig, userOfflineConfig); const baseState = Object.assign({}, initialState, preloadedState); @@ -52,7 +54,7 @@ export default function configureServiceStore(preloadedState, appReducer, userOf ) ); - reducerRegistry.setChangeListener((reducers) => { + reducerRegistry.setChangeListener((reducers: any) => { store.replaceReducer(createOfflineReducer(createDevReducer(baseState, reducers))); }); @@ -62,7 +64,7 @@ export default function configureServiceStore(preloadedState, appReducer, userOf } if (baseOfflineConfig.detectNetwork) { - baseOfflineConfig.detectNetwork((online) => { + baseOfflineConfig.detectNetwork((online: boolean) => { store.dispatch(networkStatusChangedAction(online)); }); } @@ -82,12 +84,12 @@ export default function configureServiceStore(preloadedState, appReducer, userOf return store; } -function createDevReducer(baseState, ...reducers) { +function createDevReducer(baseState: any, ...reducers: any) { return enableFreezing(createReducer(baseState, ...reducers)); } -function enableFreezing(reducer) { - return (state, action) => { +function enableFreezing(reducer: Reducer) { + return (state: GlobalState, action: Action) => { const nextState = reducer(state, action); if (nextState !== state) { diff --git a/src/store/configureStore.prod.ts b/src/store/configureStore.prod.ts index 4318c3fcf..f1d456386 100644 --- a/src/store/configureStore.prod.ts +++ b/src/store/configureStore.prod.ts @@ -2,8 +2,6 @@ // See LICENSE.txt for license information. import * as redux from 'redux'; -import {createOfflineReducer, networkStatusChangedAction, offlineCompose} from 'redux-offline'; -import defaultOfflineConfig from 'redux-offline/lib/defaults'; import reducerRegistry from './reducer_registry'; import serviceReducer from '../reducers'; @@ -12,6 +10,9 @@ import {offlineConfig, createReducer} from './helpers'; import initialState from './initial_state'; import {createMiddleware} from './middleware'; +import {createOfflineReducer, networkStatusChangedAction, offlineCompose} from 'redux-offline'; +import defaultOfflineConfig from 'redux-offline/lib/defaults'; + /** * Configures and constructs the redux store. Accepts the following parameters: * preloadedState - Any preloaded state to be applied to the store after it is initially configured. @@ -23,13 +24,13 @@ import {createMiddleware} from './middleware'; * enableBuffer - bool - default = true - If true, the store will buffer all actions until offline state rehydration occurs. * enableThunk - bool - default = true - If true, include the thunk middleware automatically. If false, thunk must be provided as part of additionalMiddleware. */ -export default function configureOfflineServiceStore(preloadedState, appReducer, userOfflineConfig, getAppReducer, clientOptions = {}) { +export default function configureOfflineServiceStore(preloadedState: any, appReducer: any, userOfflineConfig: any, getAppReducer: any, clientOptions = {}) { const baseState = Object.assign({}, initialState, preloadedState); const baseOfflineConfig = Object.assign({}, defaultOfflineConfig, offlineConfig, userOfflineConfig); const store = redux.createStore( - createOfflineReducer(createReducer(baseState, serviceReducer, appReducer)), + createOfflineReducer(createReducer(baseState, serviceReducer as any, appReducer)), baseState, offlineCompose(baseOfflineConfig)( createMiddleware(clientOptions), @@ -37,7 +38,7 @@ export default function configureOfflineServiceStore(preloadedState, appReducer, ) ); - reducerRegistry.setChangeListener((reducers) => { + reducerRegistry.setChangeListener((reducers: any) => { store.replaceReducer(createOfflineReducer(createReducer(baseState, reducers))); }); @@ -47,7 +48,7 @@ export default function configureOfflineServiceStore(preloadedState, appReducer, } if (baseOfflineConfig.detectNetwork) { - baseOfflineConfig.detectNetwork((online) => { + baseOfflineConfig.detectNetwork((online: boolean) => { store.dispatch(networkStatusChangedAction(online)); }); } diff --git a/src/store/helpers.ts b/src/store/helpers.ts index 8cb1b1aa5..3ee39ed6e 100644 --- a/src/store/helpers.ts +++ b/src/store/helpers.ts @@ -3,19 +3,19 @@ import {combineReducers} from 'redux'; import {General} from '../constants'; import reducerRegistry from './reducer_registry'; -import {enableBatching} from 'types/actions'; +import {enableBatching, Action, Reducer} from 'types/actions'; export const offlineConfig = { - effect: (effect, action) => { + effect: (effect: Function, action: Action) => { if (typeof effect !== 'function') { throw new Error('Offline Action: effect must be a function.'); - } else if (!action.meta.offline.commit) { + } else if (!('meta' in action && action.meta && action.meta.offline.commit)) { throw new Error('Offline Action: commit action must be present.'); } return effect(); }, - discard: (error, action, retries) => { - if (action.meta && action.meta.offline.hasOwnProperty('maxRetry')) { + discard: (error: Error, action: Action, retries: number) => { + if ('meta' in action && action.meta && action.meta.offline.hasOwnProperty('maxRetry')) { return retries >= action.meta.offline.maxRetry; } @@ -23,19 +23,19 @@ export const offlineConfig = { }, }; -export function createReducer(baseState, ...reducers) { +export function createReducer(baseState: any, ...reducers: Reducer[]) { reducerRegistry.setReducers(Object.assign({}, ...reducers)); const baseReducer = combineReducers(reducerRegistry.getReducers()); // Root reducer wrapper that listens for reset events. // Returns whatever is passed for the data property // as the new state. - function offlineReducer(state = {}, action) { - if (action.type === General.OFFLINE_STORE_RESET) { + function offlineReducer(state = {}, action: Action) { + if ('type' in action && 'data' in action && action.type === General.OFFLINE_STORE_RESET) { return baseReducer(action.data || baseState, action); } - return baseReducer(state, action); + return baseReducer(state, action as any); } return enableBatching(offlineReducer); diff --git a/src/store/initial_state.ts b/src/store/initial_state.ts index d3ffea14c..47d582a69 100644 --- a/src/store/initial_state.ts +++ b/src/store/initial_state.ts @@ -66,6 +66,9 @@ const state: GlobalState = { preferences: { myPreferences: {}, }, + bots: { + accounts: {}, + }, admin: { logs: [], audits: {}, @@ -74,6 +77,8 @@ const state: GlobalState = { complianceReports: {}, ldapGroups: {}, ldapGroupsCount: 0, + userAccessTokens: [], + clusterInfo: {}, }, jobs: { jobs: {}, @@ -89,6 +94,9 @@ const state: GlobalState = { systemCommands: {}, commands: {}, }, + plugins: { + marketplacePlugins: [], + }, files: { files: {}, fileIdsByPostId: {}, diff --git a/src/store/middleware.ts b/src/store/middleware.ts index c07d75255..88a1ca420 100644 --- a/src/store/middleware.ts +++ b/src/store/middleware.ts @@ -1,14 +1,16 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import thunk, {ThunkMiddleware} from 'redux-thunk'; + import createActionBuffer from 'redux-action-buffer'; import {REHYDRATE} from 'redux-persist/constants'; -import thunk, {ThunkMiddleware} from 'redux-thunk'; + const defaultOptions = { additionalMiddleware: [], enableBuffer: true, enableThunk: true, }; -export function createMiddleware(clientOptions): ThunkMiddleware[] { +export function createMiddleware(clientOptions: any): ThunkMiddleware[] { const options = Object.assign({}, defaultOptions, clientOptions); const { additionalMiddleware, diff --git a/src/store/reducer_registry.ts b/src/store/reducer_registry.ts index 057a7620d..0b8ccde87 100644 --- a/src/store/reducer_registry.ts +++ b/src/store/reducer_registry.ts @@ -1,12 +1,15 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {Reducer} from 'types/actions'; +import {Dictionary} from 'types/utilities'; + // Based on http://nicolasgallagher.com/redux-modules-and-code-splitting/ export class ReducerRegistry { emitChange?: Function; - reducers = {}; + reducers: Dictionary = {}; - setReducers = (reducers) => { + setReducers = (reducers: Dictionary) => { this.reducers = reducers; } @@ -14,14 +17,14 @@ export class ReducerRegistry { return {...this.reducers}; } - register = (name, reducer) => { + register = (name: string, reducer: Reducer) => { this.reducers = {...this.reducers, [name]: reducer}; if (this.emitChange) { this.emitChange(this.getReducers()); } } - setChangeListener = (listener) => { + setChangeListener = (listener: Function) => { this.emitChange = listener; } } diff --git a/src/types/actions.ts b/src/types/actions.ts index 90af666bd..8a2507dd9 100644 --- a/src/types/actions.ts +++ b/src/types/actions.ts @@ -17,7 +17,7 @@ export type GenericAction = { timestamp?: number; [extraProps: string]: any; }; -type Thunk = (b: DispatchFunc, a: GetStateFunc) => Promise | ActionResult; +export type Thunk = (b: DispatchFunc, a: GetStateFunc) => Promise | ActionResult; type BatchAction = { type: 'BATCHING_REDUCER.BATCH'; @@ -44,32 +44,16 @@ export function batchActions(actions: Action[], type = BATCH) { return {type, meta: {batch: true}, payload: actions}; } -export function enableBatching(reduce) { +export type Reducer = ( + state: S | undefined, + action: A + ) => S + +export function enableBatching(reduce: Reducer): Reducer { return function batchingReducer(state, action) { - if (action && action.meta && action.meta.batch) { + if (action && 'meta' in action && action.meta.batch) { return action.payload.reduce(batchingReducer, state); } return reduce(state, action); }; } - -export function batchDispatchMiddleware(store_) { - function dispatchChildActions(store, action) { - if (action.meta && action.meta.batch) { - action.payload.forEach((childAction) => { - dispatchChildActions(store, childAction); - }); - } else { - store.dispatch(action); - } - } - - return (next) => { - return (action) => { - if (action && action.meta && action.meta.batch) { - dispatchChildActions(store_, action); - } - return next(action); - }; - }; -} diff --git a/src/types/admin.ts b/src/types/admin.ts index 5d4f8c7e7..e808dad57 100644 --- a/src/types/admin.ts +++ b/src/types/admin.ts @@ -8,4 +8,6 @@ export type AdminState = { complianceReports: any; ldapGroups: any; ldapGroupsCount: number; + userAccessTokens: any[]; + clusterInfo: any; }; diff --git a/src/types/bots.ts b/src/types/bots.ts new file mode 100644 index 000000000..2a3e17c2a --- /dev/null +++ b/src/types/bots.ts @@ -0,0 +1,20 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +export type Bot = { + user_id: string ; + username: string ; + display_name?: string ; + description?: string ; + owner_id: string ; + create_at: number ; + update_at: number ; + delete_at: number ; +} + +// BotPatch is a description of what fields to update on an existing bot. +export type BotPatch = { + username: string; + display_name: string; + description: string; +} \ No newline at end of file diff --git a/src/types/client4.ts b/src/types/client4.ts index d33e8fa94..fcee74987 100644 --- a/src/types/client4.ts +++ b/src/types/client4.ts @@ -24,4 +24,10 @@ export type ErrorApi = { url: string; }; export type Client4Error = ErrorOffline | ErrorInvalidResponse | ErrorApi; -export type Options = { headers: {[x: string]: string}; method?: string;url?: string; credentials?: 'omit' | 'same-origin' | 'include'}; +export type Options = { + headers?: { [x: string]: string }; + method?: string; + url?: string; + credentials?: 'omit' | 'same-origin' | 'include'; + body?: any; +}; diff --git a/src/types/config.ts b/src/types/config.ts new file mode 100644 index 000000000..2bf279345 --- /dev/null +++ b/src/types/config.ts @@ -0,0 +1,162 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +export type Config = { + AboutLink: string; + AllowBannerDismissal: string; + AllowCustomThemes: string; + AllowedThemes: string; + AndroidAppDownloadLink: string; + AndroidLatestVersion: string; + AndroidMinVersion: string; + AppDownloadLink: string; + AvailableLocales: string; + BannerColor: string; + BannerText: string; + BannerTextColor: string; + BuildDate: string; + BuildEnterpriseReady: string; + BuildHash: string; + BuildHashEnterprise: string; + BuildNumber: string; + CloseUnusedDirectMessages: string; + CustomBrandText: string; + CustomDescriptionText: string; + CustomTermsOfServiceId: string; + CustomTermsOfServiceReAcceptancePeriod: string; + CustomUrlSchemes: string; + DataRetentionEnableFileDeletion: string; + DataRetentionEnableMessageDeletion: string; + DataRetentionFileRetentionDays: string; + DataRetentionMessageRetentionDays: string; + DefaultClientLocale: string; + DefaultTheme: string; + DesktopLatestVersion: string; + DesktopMinVersion: string; + DiagnosticId: string; + DiagnosticsEnabled: string; + EmailLoginButtonBorderColor: string; + EmailLoginButtonColor: string; + EmailLoginButtonTextColor: string; + EmailNotificationContentsType: string; + EnableBanner: string; + EnableBotAccountCreation: string; + EnableChannelViewedMessages: string; + EnableCluster: string; + EnableCommands: string; + EnableCompliance: string; + EnableConfirmNotificationsToChannel: string; + EnableCustomBrand: string; + EnableCustomEmoji: string; + EnableCustomTermsOfService: string; + EnableDeveloper: string; + EnableDiagnostics: string; + EnableEmailBatching: string; + EnableEmailInvitations: string; + EnableEmojiPicker: string; + EnableFileAttachments: string; + EnableGifPicker: string; + EnableGuestAccounts: string; + EnableIncomingWebhooks: string; + EnableLatex: string; + EnableLdap: string; + EnableLinkPreviews: string; + EnableMarketplace: string; + EnableMetrics: string; + EnableMobileFileDownload: string; + EnableMobileFileUpload: string; + EnableMultifactorAuthentication: string; + EnableOAuthServiceProvider: string; + EnableOpenServer: string; + EnableOutgoingWebhooks: string; + EnablePostIconOverride: string; + EnablePostUsernameOverride: string; + EnablePreviewFeatures: string; + EnablePreviewModeBanner: string; + EnablePublicLink: string; + EnableSaml: string; + EnableSignInWithEmail: string; + EnableSignInWithUsername: string; + EnableSignUpWithEmail: string; + EnableSignUpWithGitLab: string; + EnableSignUpWithGoogle: string; + EnableSignUpWithOffice365: string; + EnableSVGs: string; + EnableTesting: string; + EnableThemeSelection: string; + EnableTutorial: string; + EnableUserAccessTokens: string; + EnableUserCreation: string; + EnableUserDeactivation: string; + EnableUserTypingMessages: string; + EnableXToLeaveChannelsFromLHS: string; + EnforceMultifactorAuthentication: string; + ExperimentalChannelOrganization: string; + ExperimentalClientSideCertCheck: string; + ExperimentalClientSideCertEnable: string; + ExperimentalEnableAuthenticationTransfer: string; + ExperimentalEnableAutomaticReplies: string; + ExperimentalEnableClickToReply: string; + ExperimentalEnableDefaultChannelLeaveJoinMessages: string; + ExperimentalEnablePostMetadata: string; + ExperimentalGroupUnreadChannels: string; + ExperimentalHideTownSquareinLHS: string; + ExperimentalPrimaryTeam: string; + ExperimentalTimezone: string; + ExperimentalTownSquareIsReadOnly: string; + ExperimentalViewArchivedChannels: string; + GfycatApiKey: string; + GfycatApiSecret: string; + GoogleDeveloperKey: string; + GuestAccountsEnforceMultifactorAuthentication: string; + HasImageProxy: string; + HelpLink: string; + IosAppDownloadLink: string; + IosLatestVersion: string; + IosMinVersion: string; + LdapFirstNameAttributeSet: string; + LdapLastNameAttributeSet: string; + LdapLoginButtonBorderColor: string; + LdapLoginButtonColor: string; + LdapLoginButtonTextColor: string; + LdapLoginFieldName: string; + LdapNicknameAttributeSet: string; + LdapPositionAttributeSet: string; + MaxFileSize: string; + MaxNotificationsPerChannel: string; + MinimumHashtagLength: string; + PasswordMinimumLength: string; + PasswordRequireLowercase: string; + PasswordRequireNumber: string; + PasswordRequireSymbol: string; + PasswordRequireUppercase: string; + PluginsEnabled: string; + PostEditTimeLimit: string; + PrivacyPolicyLink: string; + ReportAProblemLink: string; + RequireEmailVerification: string; + RestrictDirectMessage: string; + RunJobs: string; + SamlFirstNameAttributeSet: string; + SamlLastNameAttributeSet: string; + SamlLoginButtonBorderColor: string; + SamlLoginButtonColor: string; + SamlLoginButtonText: string; + SamlLoginButtonTextColor: string; + SamlNicknameAttributeSet: string; + SamlPositionAttributeSet: string; + SendEmailNotifications: string; + SendPushNotifications: string; + ShowEmailAddress: string; + SiteName: string; + SiteURL: string; + SQLDriverName: string; + SupportEmail: string; + TeammateNameDisplay: string; + TermsOfServiceLink: string; + TimeBetweenUserTypingUpdatesMilliseconds: string; + Version: string; + WebsocketPort: string; + WebsocketSecurePort: string; + WebsocketURL: string; +}; diff --git a/src/types/files.ts b/src/types/files.ts index 7e5d13001..38cf1a639 100644 --- a/src/types/files.ts +++ b/src/types/files.ts @@ -1,5 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. + +import {Dictionary} from './utilities'; + export type FileInfo = { id: string; user_id: string; @@ -16,10 +19,12 @@ export type FileInfo = { clientId: string; }; export type FilesState = { - files: { - [x: string]: FileInfo; - }; - fileIdsByPostId: { - [x: string]: Array; - }; + files: Dictionary; + fileIdsByPostId: Dictionary>; + filePublicLink?: string; }; + +export type FileUploadResponse = { + file_infos: FileInfo[]; + client_ids: string[]; +} \ No newline at end of file diff --git a/src/types/general.ts b/src/types/general.ts index c76f3d0b1..c51e0b7de 100644 --- a/src/types/general.ts +++ b/src/types/general.ts @@ -1,9 +1,12 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. + +import {Config} from './config'; + export type GeneralState = { appState: boolean; credentials: any; - config: any; + config: Partial; dataRetentionPolicy: any; deviceToken: string; license: any; diff --git a/src/types/index.ts b/src/types/index.ts index 42fd8fabf..1ecf0d089 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -25,11 +25,17 @@ import * as teams from './teams'; import * as typing from './typing'; import * as utilities from './utilities'; import * as users from './users'; +import * as bots from './bots'; +import * as plugins from './plugins'; +import * as config from './config'; export { admin, + config, actions, alerts, + bots, + plugins, store, channels, errors, diff --git a/src/types/integrations.ts b/src/types/integrations.ts index cf819ac8f..92dd8755e 100644 --- a/src/types/integrations.ts +++ b/src/types/integrations.ts @@ -23,7 +23,7 @@ export type OutgoingWebhook = { delete_at: number; creator_id: string; channel_id: string; - teamId: string; + team_id: string; trigger_words: Array; trigger_when: number; callback_urls: Array; diff --git a/src/types/module.d.ts b/src/types/module.d.ts new file mode 100644 index 000000000..3dd9c11aa --- /dev/null +++ b/src/types/module.d.ts @@ -0,0 +1,9 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +declare module 'gfycat-sdk'; +declare module 'redux-offline'; +declare module 'redux-offline/lib/defaults'; +declare module 'remote-redux-devtools'; +declare module 'redux-persist'; +declare module 'redux-persist/constants'; +declare module 'redux-action-buffer'; \ No newline at end of file diff --git a/src/types/plugins.ts b/src/types/plugins.ts new file mode 100644 index 000000000..052ac0d7b --- /dev/null +++ b/src/types/plugins.ts @@ -0,0 +1,15 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +export type MarketplacePlugin = { + homepage_url: string; + download_url: string; + manifest: { + id: string; + name: string; + description: string; + version: string; + minServerVersion: string; + }; + installed_version: string; +} diff --git a/src/types/posts.ts b/src/types/posts.ts index 4a0b4c7a4..8b806097c 100644 --- a/src/types/posts.ts +++ b/src/types/posts.ts @@ -77,7 +77,7 @@ export type PostWithFormatData = Post & { isFirstReply: boolean; isLastReply: boolean; previousPostIsComment: boolean; - commentedOnPost: Post; + commentedOnPost?: Post; consecutivePostByUser: boolean; replyCount: number; isCommentMention: boolean; @@ -86,32 +86,27 @@ export type PostWithFormatData = Post & { export type PostOrderBlock = { order: Array; - recent: boolean; - oldest: boolean; + recent?: boolean; + oldest?: boolean; +}; + +export type MessageHistory = { + messages: Array; + index: { + post: number; + comment: number; + }; }; export type PostsState = { posts: IDMappedObjects; - postsInChannel: { - [x: string]: Array; - }; + postsInChannel: Dictionary>; postsInThread: RelationOneToMany; - reactions: RelationOneToOne< - Post, - { - [x: string]: Reaction; - } - >; + reactions: RelationOneToOne>; openGraph: RelationOneToOne; pendingPostIds: Array; selectedPostId: string; currentFocusedPostId: string; - messagesHistory: { - messages: Array; - index: { - post: number; - comment: number; - }; - }; - expandedURLs: { [x: string]: string}; + messagesHistory: MessageHistory; + expandedURLs: Dictionary; }; diff --git a/src/types/search.ts b/src/types/search.ts index 97ba00d91..8b9888591 100644 --- a/src/types/search.ts +++ b/src/types/search.ts @@ -1,9 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. + export type Search = { terms: string; isOrSearch: boolean; }; + export type SearchState = { current: any; results: Array; @@ -14,3 +16,12 @@ export type SearchState = { [x: string]: Array; }; }; + +export type SearchParameter = { + terms: string; + is_or_search: boolean; + time_zone_offset?: number; + page: number; + per_page: number; + include_deleted_channels: boolean; +} \ No newline at end of file diff --git a/src/types/store.ts b/src/types/store.ts index f510a51df..266e7fb9c 100644 --- a/src/types/store.ts +++ b/src/types/store.ts @@ -18,6 +18,10 @@ import {GroupsState} from './groups'; import {ChannelsRequestsStatuses, GeneralRequestsStatuses, PostsRequestsStatuses, TeamsRequestsStatuses, UsersRequestsStatuses, PreferencesRequestsStatuses, AdminRequestsStatuses, FilesRequestsStatuses, IntegrationsRequestsStatuses, RolesRequestsStatuses, SchemesRequestsStatuses, GroupsRequestsStatuses, JobsRequestsStatuses, SearchRequestsStatuses} from './requests'; import {Role} from './roles'; import {PreferenceType} from './preferences'; +import {MarketplacePlugin} from './plugins'; +import {Bot} from './bots'; +import {Dictionary} from './utilities'; + export type GlobalState = { entities: { general: GeneralState; @@ -25,6 +29,9 @@ export type GlobalState = { teams: TeamsState; channels: ChannelsState; posts: PostsState; + bots: { + accounts: Dictionary; + }; preferences: { myPreferences: { [x: string]: PreferenceType; @@ -48,6 +55,9 @@ export type GlobalState = { }; schemes: SchemesState; gifs: any; + plugins: { + marketplacePlugins: Array; + }; groups: GroupsState; }; errors: Array; diff --git a/src/types/teams.ts b/src/types/teams.ts index dbc536cd2..38a2eb5a1 100644 --- a/src/types/teams.ts +++ b/src/types/teams.ts @@ -1,5 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. + +import {Dictionary} from './utilities'; + export type TeamMembership = { mention_count: number; msg_count: number; @@ -10,7 +13,9 @@ export type TeamMembership = { scheme_user: boolean; scheme_admin: boolean; }; + export type TeamType = 'O' | 'I'; + export type Team = { id: string; create_at: number; @@ -28,14 +33,11 @@ export type Team = { scheme_id: string; group_constrained: boolean; }; + export type TeamsState = { currentTeamId: string; - teams: { - [x: string]: Team; - }; - myMembers: { - [x: string]: TeamMembership; - }; + teams: Dictionary; + myMembers: Dictionary; membersInTeam: any; stats: any; groupsAssociatedToTeam: any; diff --git a/src/types/users.ts b/src/types/users.ts index f613ff85e..9633406a5 100644 --- a/src/types/users.ts +++ b/src/types/users.ts @@ -44,7 +44,7 @@ export type UsersState = { profiles: IDMappedObjects; profilesInTeam: RelationOneToMany; profilesNotInTeam: RelationOneToMany; - profilesWithoutTeam: Set; + profilesWithoutTeam: Set; profilesInChannel: RelationOneToMany; profilesNotInChannel: RelationOneToMany; statuses: RelationOneToOne; @@ -63,3 +63,11 @@ export type UserActivity = { } | Array<$ID>; }; }; + +export type UserStatus = { + user_id: string; + status: string; + manual: boolean; + last_activity_at: number; + active_channel?: string; +} \ No newline at end of file diff --git a/src/types/utilities.ts b/src/types/utilities.ts index 2c0bf357c..99cc1abd4 100644 --- a/src/types/utilities.ts +++ b/src/types/utilities.ts @@ -1,27 +1,27 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -export type $ID = E['id']; -export type $UserID = E['user_id']; -export type $Name = E['name']; -export type $Username = E['username']; -export type $Email = E['email']; -export type RelationOneToOne = { +export type $ID = E['id']; +export type $UserID = E['user_id']; +export type $Name = E['name']; +export type $Username = E['username']; +export type $Email = E['email']; +export type RelationOneToOne = { [x in $ID]: T; }; -export type RelationOneToMany = { +export type RelationOneToMany = { [x in $ID]: Array<$ID>; }; -export type IDMappedObjects = RelationOneToOne; -export type UserIDMappedObjects = { +export type IDMappedObjects = RelationOneToOne; +export type UserIDMappedObjects = { [x in $UserID]: E; }; -export type NameMappedObjects = { +export type NameMappedObjects = { [x in $Name]: E; }; -export type UsernameMappedObjects = { +export type UsernameMappedObjects = { [x in $Username]: E; }; -export type EmailMappedObjects = { +export type EmailMappedObjects = { [x in $Email]: E; }; diff --git a/src/utils/channel_utils.ts b/src/utils/channel_utils.ts index 11f5e1721..e65fddc49 100644 --- a/src/utils/channel_utils.ts +++ b/src/utils/channel_utils.ts @@ -396,7 +396,7 @@ export function getGroupDisplayNameFromUserIds(userIds: Array, profiles: } }); - function sortUsernames(a, b) { + function sortUsernames(a: string, b: string) { const locale = getUserLocale(currentUserId, profiles); return a.localeCompare(b, locale, {numeric: true}); } @@ -456,7 +456,7 @@ function createMissingDirectChannels(currentUserId: string, allChannels: Array, channel: Channel) { const member = members[channel.id]; if (member) { return member.mention_count > 0; @@ -637,17 +637,17 @@ function buildDirectAndGroupChannels(channels: Array, memberships: Rela }); } -function buildChannelsWithMentions(channels, members, locale) { +function buildChannelsWithMentions(channels: Array, members: RelationOneToOne, locale: string) { return channels.filter(channelHasMentions.bind(null, members)). sort(sortChannelsByDisplayName.bind(null, locale)); } -function buildUnreadChannels(channels, members, locale) { +function buildUnreadChannels(channels: Array, members: RelationOneToOne, locale: string) { return channels.filter(channelHasUnreadMessages.bind(null, members)). sort(sortChannelsByDisplayName.bind(null, locale)); } -function getUserLocale(userId, profiles) { +function getUserLocale(userId: string, profiles: IDMappedObjects) { let locale = General.DEFAULT_LOCALE; if (profiles && profiles[userId] && profiles[userId].locale) { locale = profiles[userId].locale; diff --git a/src/utils/file_utils.ts b/src/utils/file_utils.ts index ba74131b5..2dec8f754 100644 --- a/src/utils/file_utils.ts +++ b/src/utils/file_utils.ts @@ -2,8 +2,11 @@ // See LICENSE.txt for license information. import {Files, General} from '../constants'; import {Client4} from 'client'; -import mimeDB from 'mime-db'; import {FileInfo} from 'types/files'; +import {Dictionary} from 'types/utilities'; + +const mimeDB = require('mime-db'); + export function getFormattedFileSize(file: FileInfo): string { const bytes = file.size; const fileSizes = [ @@ -48,13 +51,13 @@ export function getFileType(file: FileInfo): string { }) || 'other'; } -let extToMime; +let extToMime: Dictionary; function buildExtToMime() { extToMime = {}; Object.keys(mimeDB).forEach((key) => { const mime = mimeDB[key]; if (mime.extensions) { - mime.extensions.forEach((ext) => { + mime.extensions.forEach((ext: string) => { extToMime[ext] = key; }); } diff --git a/src/utils/gfycat_sdk.ts b/src/utils/gfycat_sdk.ts index 64e9ff7a2..fe3ce2433 100644 --- a/src/utils/gfycat_sdk.ts +++ b/src/utils/gfycat_sdk.ts @@ -4,9 +4,10 @@ import Gfycat from 'gfycat-sdk'; const defaultKey = '2_KtH_W5'; const defaultSecret = '3wLVZPiswc3DnaiaFoLkDvB4X0IV6CpMkj4tf2inJRsBY6-FnkT08zGmppWFgeof'; let activeKey: string|null = null; -let activeSecret: string|null = null; -let instance: Gfycat|null = null; -export default function(key: string, secret: string): Gfycat { +let activeSecret: string | null = null; + +let instance: any = null; +export default function(key: string, secret: string): any { if (instance && activeKey === key && activeSecret === secret) { return instance; } diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 6c83bea71..61772a712 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -2,11 +2,12 @@ // See LICENSE.txt for license information. import * as reselect from 'reselect'; import shallowEqual from 'shallow-equals'; +import {Dictionary} from 'types/utilities'; export function memoizeResult(func: F): any { let lastArgs: IArguments|null = null; - let lastResult = null; // we reference arguments instead of spreading them for performance reasons + let lastResult: any = null; // we reference arguments instead of spreading them for performance reasons - return function shallowCompare(...args) { + return function shallowCompare(...args: any[]) { if (!shallowEqual(lastArgs, args)) { //eslint-disable-line prefer-rest-params // apply arguments instead of spreading for performance. @@ -26,7 +27,7 @@ export function memoizeResult(func: F): any { export const createIdsSelector = reselect.createSelectorCreator(memoizeResult); // Use this selector when you want a shallow comparison of the arguments and you don't need to memoize the result -export const createShallowSelector = reselect.createSelectorCreator(reselect.defaultMemoize, shallowEqual); +export const createShallowSelector = reselect.createSelectorCreator(reselect.defaultMemoize, shallowEqual as any); // isMinimumServerVersion will return true if currentVersion is equal to higher or than the // the provided minimum version. A non-equal major version will ignore minor and dot @@ -101,7 +102,7 @@ export function isEmail(email: string): boolean { return (/^[^ ,@]+@[^ ,@]+$/).test(email); } -export function buildQueryString(parameters: {}): string { +export function buildQueryString(parameters: Dictionary): string { const keys = Object.keys(parameters); if (keys.length === 0) { return ''; diff --git a/src/utils/team_utils.ts b/src/utils/team_utils.ts index f3b031584..cde1fcc42 100644 --- a/src/utils/team_utils.ts +++ b/src/utils/team_utils.ts @@ -1,10 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import {Team} from 'types/teams'; -import {IDMappedObjects} from 'types/utilities'; +import {IDMappedObjects, Dictionary} from 'types/utilities'; import {General} from '../constants'; + export function teamListToMap(teamList: Array): IDMappedObjects { - const teams = {}; + const teams: Dictionary = {}; for (let i = 0; i < teamList.length; i++) { teams[teamList[i].id] = teamList[i]; } diff --git a/src/utils/theme_utils.ts b/src/utils/theme_utils.ts index 4d84f5171..0b0255e1e 100644 --- a/src/utils/theme_utils.ts +++ b/src/utils/theme_utils.ts @@ -1,8 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. + export function makeStyleFromTheme(getStyleFromTheme: (a: any) => any): (a: any) => any { - let lastTheme; - let style; + let lastTheme: any; + let style: any; return (theme: any) => { if (!style || theme !== lastTheme) { style = getStyleFromTheme(theme); diff --git a/src/utils/user_utils.ts b/src/utils/user_utils.ts index df8bafa1d..d461ffe4e 100644 --- a/src/utils/user_utils.ts +++ b/src/utils/user_utils.ts @@ -3,7 +3,7 @@ import {General, Preferences} from '../constants'; import {localizeMessage} from 'utils/i18n_utils'; import {UserProfile} from 'types/users'; -import {IDMappedObjects, $ID} from 'types/utilities'; +import {IDMappedObjects, $ID, Dictionary} from 'types/utilities'; export function getFullName(user: UserProfile): string { if (user.first_name && user.last_name) { return user.first_name + ' ' + user.last_name; @@ -74,7 +74,7 @@ export function hasPostAllPublicRole(roles: string): boolean { } export function profileListToMap(profileList: Array): IDMappedObjects { - const profiles = {}; + const profiles: Dictionary = {}; for (let i = 0; i < profileList.length; i++) { profiles[profileList[i].id] = profileList[i]; } diff --git a/test/test_helper.js b/test/test_helper.js index e0fb042f6..52896e837 100644 --- a/test/test_helper.js +++ b/test/test_helper.js @@ -120,7 +120,7 @@ class TestHelper { fakeOutgoingHook = (teamId) => { return { - teamId, + team_id: teamId, }; }; diff --git a/tsconfig.json b/tsconfig.json index 400c2ea5a..1b1ccd915 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,21 @@ { - "include": ["src"], - "exclude": ["node_modules", "build"], + "include": [ + "src" + ], + "exclude": [ + "node_modules", + "build" + ], "typeAcquisition": { "enable": true }, "compilerOptions": { "target": "es5", "module": "commonjs", - "lib": ["dom", "esnext"], + "lib": [ + "dom", + "esnext" + ], "allowJs": false, "importHelpers": true, "declaration": true, @@ -16,7 +24,7 @@ "outDir": "./", "strict": false, "noEmit": false, - "noImplicitAny": false, + "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": false, "strictPropertyInitialization": true, @@ -30,24 +38,47 @@ "moduleResolution": "node", "baseUrl": "./", "paths": { - "utils/*": ["src/utils/*"], - "types/*": ["src/types/*"], - "constants/*": ["src/constants/*"], - "client": ["src/client"], - "selectors/*": ["src/selectors/*"], - "store/*": ["src/store/*"], - "action_types": ["src/action_types"], - "actions/*": ["src/action_types/*"], - "test/*": ["test/*"], - "*": ["src/*","node_modules/*"] + "utils/*": [ + "src/utils/*" + ], + "types/*": [ + "src/types/*" + ], + "constants/*": [ + "src/constants/*" + ], + "client": [ + "src/client" + ], + "selectors/*": [ + "src/selectors/*" + ], + "store/*": [ + "src/store/*" + ], + "action_types": [ + "src/action_types" + ], + "actions/*": [ + "src/action_types/*" + ], + "test/*": [ + "test/*" + ], + "*": [ + "src/*", + "node_modules/*" + ] }, "plugins": [ { "transform": "@zerollup/ts-transform-paths", - "exclude": ["*"] + "exclude": [ + "*" + ] } ], "jsx": "react", "esModuleInterop": true } -} +} \ No newline at end of file