diff --git a/keyserver/src/responders/website-responders.js b/keyserver/src/responders/website-responders.js --- a/keyserver/src/responders/website-responders.js +++ b/keyserver/src/responders/website-responders.js @@ -28,7 +28,10 @@ import { defaultWebEnabledApps } from 'lib/types/enabled-apps.js'; import { entryStoreValidator } from 'lib/types/entry-types.js'; import { defaultCalendarFilters } from 'lib/types/filter-types.js'; -import { keyserverStoreValidator } from 'lib/types/keyserver-types.js'; +import { + keyserverStoreValidator, + webKeyserverInfoValidator, +} from 'lib/types/keyserver-types.js'; import { inviteLinksStoreValidator } from 'lib/types/link-types.js'; import { defaultNumberPerThread, @@ -48,7 +51,13 @@ import { promiseAll } from 'lib/utils/promises.js'; import { defaultNotifPermissionAlertInfo } from 'lib/utils/push-alerts.js'; import { infoFromURL } from 'lib/utils/url-utils.js'; -import { tBool, tNumber, tShape, tString } from 'lib/utils/validation-utils.js'; +import { + tBool, + tNumber, + tShape, + tString, + ashoatKeyserverID, +} from 'lib/utils/validation-utils.js'; import getTitle from 'web/title/getTitle.js'; import { navInfoValidator } from 'web/types/nav-types.js'; import { navInfoFromURL } from 'web/url-utils.js'; @@ -253,7 +262,7 @@ 'default lastCommunicatedPlatformDetails', _isEqual({}), ), - keyserverStore: keyserverStoreValidator, + keyserverStore: keyserverStoreValidator(webKeyserverInfoValidator), }); async function websiteResponder( @@ -498,6 +507,15 @@ }; })(); + const keyserverStorePromise = (async () => { + const sessionID = await sessionIDPromise; + return { + keyserverInfos: { + [ashoatKeyserverID]: { cookie: undefined, sessionID }, + }, + }; + })(); + const { jsURL, fontsURL, @@ -600,7 +618,7 @@ commServicesAccessToken: null, inviteLinksStore: inviteLinksStorePromise, lastCommunicatedPlatformDetails: {}, - keyserverStore: { keyserverInfos: {} }, + keyserverStore: keyserverStorePromise, }); const validatedInitialReduxState = validateOutput( viewer.platformDetails, diff --git a/lib/reducers/keyserver-reducer.js b/lib/reducers/keyserver-reducer.js --- a/lib/reducers/keyserver-reducer.js +++ b/lib/reducers/keyserver-reducer.js @@ -7,9 +7,9 @@ import { ashoatKeyserverID } from '../utils/validation-utils.js'; export default function reduceKeyserverStore( - state: KeyserverStore, + state: KeyserverStore<*>, action: BaseAction, -): KeyserverStore { +): KeyserverStore<*> { // this action is only dispatched on native if (action.type === resetUserStateActionType) { const stateCookie = state.keyserverInfos[ashoatKeyserverID]?.cookie; diff --git a/lib/reducers/master-reducer.js b/lib/reducers/master-reducer.js --- a/lib/reducers/master-reducer.js +++ b/lib/reducers/master-reducer.js @@ -28,6 +28,7 @@ registerActionTypes, logInActionTypes, } from '../actions/user-actions.js'; +import type { KeyserverInfo } from '../types/keyserver-types.js'; import type { BaseNavInfo } from '../types/nav-types.js'; import type { BaseAppState, BaseAction } from '../types/redux-types.js'; import { @@ -36,7 +37,11 @@ } from '../types/socket-types.js'; import type { StoreOperations } from '../types/store-ops-types.js'; -export default function baseReducer>( +export default function baseReducer< + N: BaseNavInfo, + K: KeyserverInfo, + T: BaseAppState, +>( state: T, action: BaseAction, ): { state: T, storeOperations: StoreOperations } { diff --git a/lib/selectors/calendar-filter-selectors.js b/lib/selectors/calendar-filter-selectors.js --- a/lib/selectors/calendar-filter-selectors.js +++ b/lib/selectors/calendar-filter-selectors.js @@ -27,9 +27,9 @@ } const filteredThreadIDsSelector: ( - state: BaseAppState<*>, + state: BaseAppState<*, *>, ) => ?$ReadOnlySet = createSelector( - (state: BaseAppState<*>) => state.calendarFilters, + (state: BaseAppState<*, *>) => state.calendarFilters, filteredThreadIDs, ); @@ -53,9 +53,9 @@ } const nonThreadCalendarFiltersSelector: ( - state: BaseAppState<*>, + state: BaseAppState<*, *>, ) => $ReadOnlyArray = createSelector( - (state: BaseAppState<*>) => state.calendarFilters, + (state: BaseAppState<*, *>) => state.calendarFilters, nonThreadCalendarFilters, ); @@ -66,9 +66,9 @@ } const nonExcludeDeletedCalendarFiltersSelector: ( - state: BaseAppState<*>, + state: BaseAppState<*, *>, ) => $ReadOnlyArray = createSelector( - (state: BaseAppState<*>) => state.calendarFilters, + (state: BaseAppState<*, *>) => state.calendarFilters, nonExcludeDeletedCalendarFilters, ); @@ -84,9 +84,9 @@ return false; } -const includeDeletedSelector: (state: BaseAppState<*>) => boolean = +const includeDeletedSelector: (state: BaseAppState<*, *>) => boolean = createSelector( - (state: BaseAppState<*>) => state.calendarFilters, + (state: BaseAppState<*, *>) => state.calendarFilters, (calendarFilters: $ReadOnlyArray) => !filterExists(calendarFilters, calendarThreadFilterTypes.NOT_DELETED), ); diff --git a/lib/selectors/chat-selectors.js b/lib/selectors/chat-selectors.js --- a/lib/selectors/chat-selectors.js +++ b/lib/selectors/chat-selectors.js @@ -76,12 +76,13 @@ +pendingPersonalThreadUserInfo?: UserInfo, }; -const messageInfoSelector: (state: BaseAppState<*>) => { +const messageInfoSelector: (state: BaseAppState<*, *>) => { +[id: string]: ?MessageInfo, } = createObjectSelector( - (state: BaseAppState<*>) => state.messageStore.messages, - (state: BaseAppState<*>) => state.currentUserInfo && state.currentUserInfo.id, - (state: BaseAppState<*>) => state.userStore.userInfos, + (state: BaseAppState<*, *>) => state.messageStore.messages, + (state: BaseAppState<*, *>) => + state.currentUserInfo && state.currentUserInfo.id, + (state: BaseAppState<*, *>) => state.userStore.userInfos, threadInfoSelector, createMessageInfo, ); @@ -201,26 +202,27 @@ }; } -const chatListData: (state: BaseAppState<*>) => $ReadOnlyArray = - createSelector( - threadInfoSelector, - (state: BaseAppState<*>) => state.messageStore, - messageInfoSelector, - sidebarInfoSelector, - ( - threadInfos: { +[id: string]: ThreadInfo }, - messageStore: MessageStore, - messageInfos: { +[id: string]: ?MessageInfo }, - sidebarInfos: { +[id: string]: $ReadOnlyArray }, - ): $ReadOnlyArray => - getChatThreadItems( - threadInfos, - messageStore, - messageInfos, - sidebarInfos, - threadIsTopLevel, - ), - ); +const chatListData: ( + state: BaseAppState<*, *>, +) => $ReadOnlyArray = createSelector( + threadInfoSelector, + (state: BaseAppState<*, *>) => state.messageStore, + messageInfoSelector, + sidebarInfoSelector, + ( + threadInfos: { +[id: string]: ThreadInfo }, + messageStore: MessageStore, + messageInfos: { +[id: string]: ?MessageInfo }, + sidebarInfos: { +[id: string]: $ReadOnlyArray }, + ): $ReadOnlyArray => + getChatThreadItems( + threadInfos, + messageStore, + messageInfos, + sidebarInfos, + threadIsTopLevel, + ), +); function useFlattenedChatListData(): $ReadOnlyArray { return useFilteredChatListData(threadInChatList); @@ -565,11 +567,11 @@ additionalMessages: $ReadOnlyArray, ) => createSelector( - (state: BaseAppState<*>) => state.messageStore, + (state: BaseAppState<*, *>) => state.messageStore, messageInfoSelector, threadInfoSelector, threadInfoFromSourceMessageIDSelector, - (state: BaseAppState<*>) => + (state: BaseAppState<*, *>) => state.currentUserInfo && state.currentUserInfo.id, ( messageStore: MessageStore, @@ -596,7 +598,7 @@ const messageListData: ( threadID: ?string, additionalMessages: $ReadOnlyArray, -) => (state: BaseAppState<*>) => MessageListData = +) => (state: BaseAppState<*, *>) => MessageListData = memoize2(baseMessageListData); export type UseMessageListDataArgs = { diff --git a/lib/selectors/loading-selectors.js b/lib/selectors/loading-selectors.js --- a/lib/selectors/loading-selectors.js +++ b/lib/selectors/loading-selectors.js @@ -43,12 +43,12 @@ const baseCreateLoadingStatusSelector = ( actionTypes: ActionTypes<*, *, *>, overrideKey?: string, -): ((state: BaseAppState<*>) => LoadingStatus) => { +): ((state: BaseAppState<*, *>) => LoadingStatus) => { // This makes sure that reduceLoadingStatuses tracks this action registerFetchKey(actionTypes); const trackingKey = getTrackingKey(actionTypes, overrideKey); return createSelector( - (state: BaseAppState<*>) => state.loadingStatuses[trackingKey], + (state: BaseAppState<*, *>) => state.loadingStatuses[trackingKey], (loadingStatusInfo: { [idx: number]: LoadingStatus }) => loadingStatusFromInfo(loadingStatusInfo), ); @@ -57,7 +57,7 @@ const createLoadingStatusSelector: ( actionTypes: ActionTypes<*, *, *>, overrideKey?: string, -) => (state: BaseAppState<*>) => LoadingStatus = _memoize( +) => (state: BaseAppState<*, *>) => LoadingStatus = _memoize( baseCreateLoadingStatusSelector, getTrackingKey, ); @@ -77,19 +77,18 @@ return errorExists ? 'error' : 'inactive'; } -const globalLoadingStatusSelector: (state: BaseAppState<*>) => LoadingStatus = - createSelector( - (state: BaseAppState<*>) => state.loadingStatuses, - (loadingStatusInfos: { - [key: string]: { [idx: number]: LoadingStatus }, - }): LoadingStatus => { - const loadingStatusInfoValues = values(loadingStatusInfos); - const loadingStatuses = loadingStatusInfoValues.map( - loadingStatusFromInfo, - ); - return combineLoadingStatuses(...loadingStatuses); - }, - ); +const globalLoadingStatusSelector: ( + state: BaseAppState<*, *>, +) => LoadingStatus = createSelector( + (state: BaseAppState<*, *>) => state.loadingStatuses, + (loadingStatusInfos: { + [key: string]: { [idx: number]: LoadingStatus }, + }): LoadingStatus => { + const loadingStatusInfoValues = values(loadingStatusInfos); + const loadingStatuses = loadingStatusInfoValues.map(loadingStatusFromInfo); + return combineLoadingStatuses(...loadingStatuses); + }, +); export { createLoadingStatusSelector, diff --git a/lib/selectors/local-id-selectors.js b/lib/selectors/local-id-selectors.js --- a/lib/selectors/local-id-selectors.js +++ b/lib/selectors/local-id-selectors.js @@ -12,7 +12,7 @@ return parseInt(matches[1], 10); } -function highestLocalIDSelector(state: ?BaseAppState<*>): number { +function highestLocalIDSelector(state: ?BaseAppState<*, *>): number { let highestLocalIDFound = -1; if (state && state.messageStore) { diff --git a/lib/selectors/nav-selectors.js b/lib/selectors/nav-selectors.js --- a/lib/selectors/nav-selectors.js +++ b/lib/selectors/nav-selectors.js @@ -41,11 +41,11 @@ } const currentCalendarQuery: ( - state: BaseAppState<*>, + state: BaseAppState<*, *>, ) => (calendarActive: boolean) => CalendarQuery = createSelector( - (state: BaseAppState<*>) => state.entryStore.lastUserInteractionCalendar, - (state: BaseAppState<*>) => state.navInfo, - (state: BaseAppState<*>) => state.calendarFilters, + (state: BaseAppState<*, *>) => state.entryStore.lastUserInteractionCalendar, + (state: BaseAppState<*, *>) => state.navInfo, + (state: BaseAppState<*, *>) => state.calendarFilters, ( lastUserInteractionCalendar: number, navInfo: BaseNavInfo, diff --git a/lib/selectors/relationship-selectors.js b/lib/selectors/relationship-selectors.js --- a/lib/selectors/relationship-selectors.js +++ b/lib/selectors/relationship-selectors.js @@ -10,40 +10,41 @@ } from '../types/relationship-types.js'; import type { UserInfos } from '../types/user-types.js'; -const userRelationshipsSelector: (state: BaseAppState<*>) => UserRelationships = - createSelector( - (state: BaseAppState<*>) => state.userStore.userInfos, - (userInfos: UserInfos) => { - const unorderedFriendRequests = []; - const unorderedFriends = []; - const blocked = []; - for (const userID in userInfos) { - const userInfo = userInfos[userID]; - const { id, username, relationshipStatus } = userInfo; - if (!username) { - continue; - } - if ( - relationshipStatus === userRelationshipStatus.REQUEST_RECEIVED || - relationshipStatus === userRelationshipStatus.REQUEST_SENT - ) { - unorderedFriendRequests.push({ id, username, relationshipStatus }); - } else if (relationshipStatus === userRelationshipStatus.FRIEND) { - unorderedFriends.push({ id, username, relationshipStatus }); - } else if ( - relationshipStatus === userRelationshipStatus.BLOCKED_BY_VIEWER || - relationshipStatus === userRelationshipStatus.BOTH_BLOCKED - ) { - blocked.push({ id, username, relationshipStatus }); - } +const userRelationshipsSelector: ( + state: BaseAppState<*, *>, +) => UserRelationships = createSelector( + (state: BaseAppState<*, *>) => state.userStore.userInfos, + (userInfos: UserInfos) => { + const unorderedFriendRequests = []; + const unorderedFriends = []; + const blocked = []; + for (const userID in userInfos) { + const userInfo = userInfos[userID]; + const { id, username, relationshipStatus } = userInfo; + if (!username) { + continue; } - const friendRequests = _orderBy('relationshipStatus')('desc')( - unorderedFriendRequests, - ); - const friends = friendRequests.concat(unorderedFriends); + if ( + relationshipStatus === userRelationshipStatus.REQUEST_RECEIVED || + relationshipStatus === userRelationshipStatus.REQUEST_SENT + ) { + unorderedFriendRequests.push({ id, username, relationshipStatus }); + } else if (relationshipStatus === userRelationshipStatus.FRIEND) { + unorderedFriends.push({ id, username, relationshipStatus }); + } else if ( + relationshipStatus === userRelationshipStatus.BLOCKED_BY_VIEWER || + relationshipStatus === userRelationshipStatus.BOTH_BLOCKED + ) { + blocked.push({ id, username, relationshipStatus }); + } + } + const friendRequests = _orderBy('relationshipStatus')('desc')( + unorderedFriendRequests, + ); + const friends = friendRequests.concat(unorderedFriends); - return { friends, blocked }; - }, - ); + return { friends, blocked }; + }, +); export { userRelationshipsSelector }; diff --git a/lib/selectors/thread-selectors.js b/lib/selectors/thread-selectors.js --- a/lib/selectors/thread-selectors.js +++ b/lib/selectors/thread-selectors.js @@ -55,18 +55,19 @@ const _mapValuesWithKeys = _mapValues.convert({ cap: false }); -type ThreadInfoSelectorType = (state: BaseAppState<*>) => { +type ThreadInfoSelectorType = (state: BaseAppState<*, *>) => { +[id: string]: ThreadInfo, }; const threadInfoSelector: ThreadInfoSelectorType = createObjectSelector( - (state: BaseAppState<*>) => state.threadStore.threadInfos, - (state: BaseAppState<*>) => state.currentUserInfo && state.currentUserInfo.id, - (state: BaseAppState<*>) => state.userStore.userInfos, + (state: BaseAppState<*, *>) => state.threadStore.threadInfos, + (state: BaseAppState<*, *>) => + state.currentUserInfo && state.currentUserInfo.id, + (state: BaseAppState<*, *>) => state.userStore.userInfos, threadInfoFromRawThreadInfo, ); const communityThreadSelector: ( - state: BaseAppState<*>, + state: BaseAppState<*, *>, ) => $ReadOnlyArray = createSelector( threadInfoSelector, (threadInfos: { +[id: string]: ThreadInfo }) => { @@ -83,7 +84,7 @@ ); const canBeOnScreenThreadInfos: ( - state: BaseAppState<*>, + state: BaseAppState<*, *>, ) => $ReadOnlyArray = createSelector( threadInfoSelector, (threadInfos: { +[id: string]: ThreadInfo }) => { @@ -100,7 +101,7 @@ ); const onScreenThreadInfos: ( - state: BaseAppState<*>, + state: BaseAppState<*, *>, ) => $ReadOnlyArray = createSelector( filteredThreadIDsSelector, canBeOnScreenThreadInfos, @@ -117,7 +118,7 @@ ); const onScreenEntryEditableThreadInfos: ( - state: BaseAppState<*>, + state: BaseAppState<*, *>, ) => $ReadOnlyArray = createSelector( onScreenThreadInfos, (threadInfos: $ReadOnlyArray) => @@ -126,24 +127,25 @@ ), ); -const entryInfoSelector: (state: BaseAppState<*>) => { +const entryInfoSelector: (state: BaseAppState<*, *>) => { +[id: string]: EntryInfo, } = createObjectSelector( - (state: BaseAppState<*>) => state.entryStore.entryInfos, - (state: BaseAppState<*>) => state.currentUserInfo && state.currentUserInfo.id, - (state: BaseAppState<*>) => state.userStore.userInfos, + (state: BaseAppState<*, *>) => state.entryStore.entryInfos, + (state: BaseAppState<*, *>) => + state.currentUserInfo && state.currentUserInfo.id, + (state: BaseAppState<*, *>) => state.userStore.userInfos, createEntryInfo, ); // "current" means within startDate/endDate range, not deleted, and in // onScreenThreadInfos -const currentDaysToEntries: (state: BaseAppState<*>) => { +const currentDaysToEntries: (state: BaseAppState<*, *>) => { +[dayString: string]: EntryInfo[], } = createSelector( entryInfoSelector, - (state: BaseAppState<*>) => state.entryStore.daysToEntries, - (state: BaseAppState<*>) => state.navInfo.startDate, - (state: BaseAppState<*>) => state.navInfo.endDate, + (state: BaseAppState<*, *>) => state.entryStore.daysToEntries, + (state: BaseAppState<*, *>) => state.navInfo.startDate, + (state: BaseAppState<*, *>) => state.navInfo.endDate, onScreenThreadInfos, includeDeletedSelector, ( @@ -179,7 +181,7 @@ }, ); -const childThreadInfos: (state: BaseAppState<*>) => { +const childThreadInfos: (state: BaseAppState<*, *>) => { +[id: string]: $ReadOnlyArray, } = createSelector( threadInfoSelector, @@ -214,11 +216,11 @@ return null; } -const sidebarInfoSelector: (state: BaseAppState<*>) => { +const sidebarInfoSelector: (state: BaseAppState<*, *>) => { +[id: string]: $ReadOnlyArray, } = createObjectSelector( childThreadInfos, - (state: BaseAppState<*>) => state.messageStore, + (state: BaseAppState<*, *>) => state.messageStore, (childThreads: $ReadOnlyArray, messageStore: MessageStore) => { const sidebarInfos = []; for (const childThreadInfo of childThreads) { @@ -248,8 +250,8 @@ }, ); -const unreadCount: (state: BaseAppState<*>) => number = createSelector( - (state: BaseAppState<*>) => state.threadStore.threadInfos, +const unreadCount: (state: BaseAppState<*, *>) => number = createSelector( + (state: BaseAppState<*, *>) => state.threadStore.threadInfos, (threadInfos: { +[id: string]: RawThreadInfo }): number => values(threadInfos).filter( threadInfo => @@ -257,9 +259,9 @@ ).length, ); -const unreadBackgroundCount: (state: BaseAppState<*>) => number = +const unreadBackgroundCount: (state: BaseAppState<*, *>) => number = createSelector( - (state: BaseAppState<*>) => state.threadStore.threadInfos, + (state: BaseAppState<*, *>) => state.threadStore.threadInfos, (threadInfos: { +[id: string]: RawThreadInfo }): number => values(threadInfos).filter( threadInfo => @@ -270,7 +272,7 @@ const baseAncestorThreadInfos = (threadID: string) => createSelector( - (state: BaseAppState<*>) => threadInfoSelector(state), + (state: BaseAppState<*, *>) => threadInfoSelector(state), (threadInfos: { +[id: string]: ThreadInfo, }): $ReadOnlyArray => { @@ -287,13 +289,13 @@ const ancestorThreadInfos: ( threadID: string, -) => (state: BaseAppState<*>) => $ReadOnlyArray = _memoize( +) => (state: BaseAppState<*, *>) => $ReadOnlyArray = _memoize( baseAncestorThreadInfos, ); const baseOtherUsersButNoOtherAdmins = (threadID: string) => createSelector( - (state: BaseAppState<*>) => state.threadStore.threadInfos[threadID], + (state: BaseAppState<*, *>) => state.threadStore.threadInfos[threadID], relativeMemberInfoSelectorForMembersOfThread(threadID), ( threadInfo: ?RawThreadInfo, @@ -324,7 +326,7 @@ const otherUsersButNoOtherAdmins: ( threadID: string, -) => (state: BaseAppState<*>) => boolean = _memoize( +) => (state: BaseAppState<*, *>) => boolean = _memoize( baseOtherUsersButNoOtherAdmins, ); @@ -359,17 +361,17 @@ return mostRecent ? mostRecent.threadID : null; } -const mostRecentlyReadThreadSelector: (state: BaseAppState<*>) => ?string = +const mostRecentlyReadThreadSelector: (state: BaseAppState<*, *>) => ?string = createSelector( - (state: BaseAppState<*>) => state.messageStore, - (state: BaseAppState<*>) => state.threadStore.threadInfos, + (state: BaseAppState<*, *>) => state.messageStore, + (state: BaseAppState<*, *>) => state.threadStore.threadInfos, mostRecentlyReadThread, ); -const threadInfoFromSourceMessageIDSelector: (state: BaseAppState<*>) => { +const threadInfoFromSourceMessageIDSelector: (state: BaseAppState<*, *>) => { +[id: string]: ThreadInfo, } = createSelector( - (state: BaseAppState<*>) => state.threadStore.threadInfos, + (state: BaseAppState<*, *>) => state.threadStore.threadInfos, threadInfoSelector, ( rawThreadInfos: { +[id: string]: RawThreadInfo }, @@ -423,8 +425,8 @@ containingThreadID: ?string, ) => createSelector( - (state: BaseAppState<*>) => threadInfoSelector(state)[threadID], - (state: BaseAppState<*>) => + (state: BaseAppState<*, *>) => threadInfoSelector(state)[threadID], + (state: BaseAppState<*, *>) => containingThreadID ? threadInfoSelector(state)[containingThreadID] : null, (threadInfo: ThreadInfo, containingThreadInfo: ?ThreadInfo) => { return () => { @@ -440,7 +442,7 @@ const savedEmojiAvatarSelectorForThread: ( threadID: string, containingThreadID: ?string, -) => (state: BaseAppState<*>) => () => ClientEmojiAvatar = _memoize( +) => (state: BaseAppState<*, *>) => () => ClientEmojiAvatar = _memoize( baseSavedEmojiAvatarSelectorForThread, ); diff --git a/lib/selectors/user-selectors.js b/lib/selectors/user-selectors.js --- a/lib/selectors/user-selectors.js +++ b/lib/selectors/user-selectors.js @@ -101,25 +101,25 @@ return () => emptyArray; } return createSelector( - (state: BaseAppState<*>) => state.threadStore.threadInfos[threadID], - (state: BaseAppState<*>) => + (state: BaseAppState<*, *>) => state.threadStore.threadInfos[threadID], + (state: BaseAppState<*, *>) => state.currentUserInfo && state.currentUserInfo.id, - (state: BaseAppState<*>) => state.userStore.userInfos, + (state: BaseAppState<*, *>) => state.userStore.userInfos, getRelativeMemberInfos, ); }; const relativeMemberInfoSelectorForMembersOfThread: ( threadID: ?string, -) => (state: BaseAppState<*>) => $ReadOnlyArray = _memoize( - baseRelativeMemberInfoSelectorForMembersOfThread, -); +) => (state: BaseAppState<*, *>) => $ReadOnlyArray = + _memoize(baseRelativeMemberInfoSelectorForMembersOfThread); -const userInfoSelectorForPotentialMembers: (state: BaseAppState<*>) => { +const userInfoSelectorForPotentialMembers: (state: BaseAppState<*, *>) => { [id: string]: AccountUserInfo, } = createSelector( - (state: BaseAppState<*>) => state.userStore.userInfos, - (state: BaseAppState<*>) => state.currentUserInfo && state.currentUserInfo.id, + (state: BaseAppState<*, *>) => state.userStore.userInfos, + (state: BaseAppState<*, *>) => + state.currentUserInfo && state.currentUserInfo.id, ( userInfos: UserInfos, currentUserID: ?string, @@ -153,22 +153,22 @@ } const userSearchIndexForPotentialMembers: ( - state: BaseAppState<*>, + state: BaseAppState<*, *>, ) => SearchIndex = createSelector( userInfoSelectorForPotentialMembers, searchIndexFromUserInfos, ); -const isLoggedIn = (state: BaseAppState<*>): boolean => +const isLoggedIn = (state: BaseAppState<*, *>): boolean => !!( state.currentUserInfo && !state.currentUserInfo.anonymous && state.dataLoaded ); -const userStoreSearchIndex: (state: BaseAppState<*>) => SearchIndex = +const userStoreSearchIndex: (state: BaseAppState<*, *>) => SearchIndex = createSelector( - (state: BaseAppState<*>) => state.userStore.userInfos, + (state: BaseAppState<*, *>) => state.userStore.userInfos, (userInfos: UserInfos) => { const searchIndex = new SearchIndex(); for (const id in userInfos) { @@ -183,7 +183,7 @@ ); const usersWithPersonalThreadSelector: ( - state: BaseAppState<*>, + state: BaseAppState<*, *>, ) => $ReadOnlySet = createSelector( state => state.currentUserInfo && state.currentUserInfo.id, state => state.threadStore.threadInfos, @@ -208,9 +208,9 @@ ); const savedEmojiAvatarSelectorForCurrentUser: ( - state: BaseAppState<*>, + state: BaseAppState<*, *>, ) => () => ClientEmojiAvatar = createSelector( - (state: BaseAppState<*>) => state.currentUserInfo && state.currentUserInfo, + (state: BaseAppState<*, *>) => state.currentUserInfo && state.currentUserInfo, (currentUser: ?CurrentUserInfo) => { return () => { let userAvatar = getAvatarForUser(currentUser); diff --git a/lib/types/keyserver-types.js b/lib/types/keyserver-types.js --- a/lib/types/keyserver-types.js +++ b/lib/types/keyserver-types.js @@ -1,26 +1,42 @@ // @flow -import t, { type TInterface } from 'tcomb'; +import t, { type TInterface, type TType } from 'tcomb'; import { tShape } from '../utils/validation-utils.js'; // Once we start using the cookie field on web, // the cookie field type should be changed to string | null. // See https://linear.app/comm/issue/ENG-4347/stop-using-browser-cookies -export type KeyserverInfo = { +export type BaseKeyserverInfo = { +cookie?: ?string, + ... }; -export type KeyserverStore = { - +keyserverInfos: { +[key: string]: KeyserverInfo }, +export type WebKeyserverInfo = { + ...$Exact, + +sessionID: ?string, }; -export const keyserverInfoValidator: TInterface = - tShape({ +export type NativeKeyserverInfo = { + ...$Exact, + +sessionID?: void, +}; + +export type KeyserverInfo = NativeKeyserverInfo | WebKeyserverInfo; + +export type KeyserverStore = { + +keyserverInfos: { +[key: string]: KeyserverInfoType }, +}; + +export const webKeyserverInfoValidator: TInterface = + tShape({ cookie: t.maybe(t.String), + sessionID: t.maybe(t.String), }); -export const keyserverStoreValidator: TInterface = - tShape({ - keyserverInfos: t.dict(t.String, keyserverInfoValidator), +export const keyserverStoreValidator = ( + typ: TType, +): TInterface> => + tShape>({ + keyserverInfos: t.dict(t.String, typ), }); diff --git a/lib/types/redux-types.js b/lib/types/redux-types.js --- a/lib/types/redux-types.js +++ b/lib/types/redux-types.js @@ -39,7 +39,12 @@ CalendarThreadFilter, SetCalendarDeletedFilterPayload, } from './filter-types.js'; -import type { KeyserverStore } from './keyserver-types.js'; +import type { + KeyserverStore, + NativeKeyserverInfo, + WebKeyserverInfo, + KeyserverInfo, +} from './keyserver-types.js'; import type { LifecycleState } from './lifecycle-state-types.js'; import type { FetchInviteLinksResponse, @@ -114,7 +119,10 @@ import type { Shape } from '../types/core.js'; import type { NotifPermissionAlertInfo } from '../utils/push-alerts.js'; -export type BaseAppState = { +export type BaseAppState< + NavInfo: BaseNavInfo, + KeyserverInfoType: KeyserverInfo, +> = { navInfo: NavInfo, currentUserInfo: ?CurrentUserInfo, draftStore: DraftStore, @@ -139,17 +147,17 @@ +commServicesAccessToken: ?string, +inviteLinksStore: InviteLinksStore, +lastCommunicatedPlatformDetails: LastCommunicatedPlatformDetails, - +keyserverStore: KeyserverStore, + +keyserverStore: KeyserverStore, ... }; // Web JS runtime doesn't have access to the cookie for security reasons. // Native JS doesn't have a sessionID because the cookieID is used instead. -export type NativeAppState = BaseAppState<*> & { +export type NativeAppState = BaseAppState<*, NativeKeyserverInfo> & { sessionID?: void, ... }; -export type WebAppState = BaseAppState<*> & { +export type WebAppState = BaseAppState<*, WebKeyserverInfo> & { sessionID: ?string, cryptoStore: CryptoStore, pushApiPublicKey: ?string, @@ -474,7 +482,7 @@ } | { +type: 'persist/REHYDRATE', - +payload: ?BaseAppState<*>, + +payload: ?BaseAppState<*, *>, } | { +type: 'FETCH_MESSAGES_BEFORE_CURSOR_STARTED', diff --git a/lib/utils/sanitization.js b/lib/utils/sanitization.js --- a/lib/utils/sanitization.js +++ b/lib/utils/sanitization.js @@ -295,6 +295,7 @@ state: AppState, redactionHelpers: RedactionHelpers, ): AppState { + // $FlowIssue[incompatible-type] const keyserverInfos = { ...state.keyserverStore.keyserverInfos }; for (const key in keyserverInfos) { if (keyserverInfos[key].cookie === undefined) { @@ -303,6 +304,7 @@ keyserverInfos[key] = { ...keyserverInfos[key], cookie: null }; } const keyserverStore = { ...state.keyserverStore, keyserverInfos }; + // $FlowIssue[incompatible-type] state = { ...state, keyserverStore }; if (state.deviceToken !== undefined && state.deviceToken !== null) { diff --git a/native/redux/state-types.js b/native/redux/state-types.js --- a/native/redux/state-types.js +++ b/native/redux/state-types.js @@ -8,7 +8,10 @@ import type { EnabledApps } from 'lib/types/enabled-apps.js'; import type { EntryStore } from 'lib/types/entry-types.js'; import type { CalendarFilter } from 'lib/types/filter-types.js'; -import type { KeyserverStore } from 'lib/types/keyserver-types.js'; +import type { + KeyserverStore, + NativeKeyserverInfo, +} from 'lib/types/keyserver-types.js'; import type { LifecycleState } from 'lib/types/lifecycle-state-types.js'; import type { InviteLinksStore } from 'lib/types/link-types.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; @@ -61,5 +64,5 @@ +commServicesAccessToken: ?string, +inviteLinksStore: InviteLinksStore, +lastCommunicatedPlatformDetails: LastCommunicatedPlatformDetails, - +keyserverStore: KeyserverStore, + +keyserverStore: KeyserverStore, }; diff --git a/web/redux/redux-setup.js b/web/redux/redux-setup.js --- a/web/redux/redux-setup.js +++ b/web/redux/redux-setup.js @@ -23,7 +23,10 @@ import type { EnabledApps } from 'lib/types/enabled-apps.js'; import type { EntryStore } from 'lib/types/entry-types.js'; import { type CalendarFilter } from 'lib/types/filter-types.js'; -import type { KeyserverStore } from 'lib/types/keyserver-types.js'; +import type { + KeyserverStore, + WebKeyserverInfo, +} from 'lib/types/keyserver-types.js'; import type { LifecycleState } from 'lib/types/lifecycle-state-types.js'; import type { InviteLinksStore } from 'lib/types/link-types.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; @@ -100,7 +103,7 @@ +commServicesAccessToken: ?string, +inviteLinksStore: InviteLinksStore, +lastCommunicatedPlatformDetails: LastCommunicatedPlatformDetails, - +keyserverStore: KeyserverStore, + +keyserverStore: KeyserverStore, }; export type Action =