diff --git a/web/selectors/nav-selectors.js b/web/selectors/nav-selectors.js index c86996cd5..39f32b5af 100644 --- a/web/selectors/nav-selectors.js +++ b/web/selectors/nav-selectors.js @@ -1,140 +1,140 @@ // @flow import invariant from 'invariant'; import { createSelector } from 'reselect'; import { nonThreadCalendarFiltersSelector } from 'lib/selectors/calendar-filter-selectors.js'; import { currentCalendarQuery } from 'lib/selectors/nav-selectors.js'; import type { CalendarQuery } from 'lib/types/entry-types.js'; import type { CalendarFilter } from 'lib/types/filter-types.js'; import type { AppState } from '../redux/redux-setup.js'; import { type WebNavigationTab, - type NavigationSettingsSection, + type WebNavigationSettingsSection, } from '../types/nav-types.js'; const dateExtractionRegex = /^([0-9]{4})-([0-9]{2})-[0-9]{2}$/; function yearExtractor(startDate: string, endDate: string): ?number { const startDateResults = dateExtractionRegex.exec(startDate); const endDateResults = dateExtractionRegex.exec(endDate); if ( !startDateResults || !startDateResults[1] || !endDateResults || !endDateResults[1] || startDateResults[1] !== endDateResults[1] ) { return null; } return parseInt(startDateResults[1], 10); } function yearAssertingExtractor(startDate: string, endDate: string): number { const result = yearExtractor(startDate, endDate); invariant( result !== null && result !== undefined, `${startDate} and ${endDate} aren't in the same year`, ); return result; } const yearAssertingSelector: (state: AppState) => number = createSelector( (state: AppState) => state.navInfo.startDate, (state: AppState) => state.navInfo.endDate, yearAssertingExtractor, ); // 1-indexed function monthExtractor(startDate: string, endDate: string): ?number { const startDateResults = dateExtractionRegex.exec(startDate); const endDateResults = dateExtractionRegex.exec(endDate); if ( !startDateResults || !startDateResults[1] || !startDateResults[2] || !endDateResults || !endDateResults[1] || !endDateResults[2] || startDateResults[1] !== endDateResults[1] || startDateResults[2] !== endDateResults[2] ) { return null; } return parseInt(startDateResults[2], 10); } // 1-indexed function monthAssertingExtractor(startDate: string, endDate: string): number { const result = monthExtractor(startDate, endDate); invariant( result !== null && result !== undefined, `${startDate} and ${endDate} aren't in the same month`, ); return result; } // 1-indexed const monthAssertingSelector: (state: AppState) => number = createSelector( (state: AppState) => state.navInfo.startDate, (state: AppState) => state.navInfo.endDate, monthAssertingExtractor, ); function activeThreadSelector(state: AppState): ?string { return state.navInfo.tab === 'chat' ? state.navInfo.activeChatThreadID : null; } const webCalendarQuery: (state: AppState) => () => CalendarQuery = createSelector( currentCalendarQuery, (state: AppState) => state.navInfo.tab === 'calendar', ( calendarQuery: (calendarActive: boolean) => CalendarQuery, calendarActive: boolean, ) => () => calendarQuery(calendarActive), ); const nonThreadCalendarQuery: (state: AppState) => () => CalendarQuery = createSelector( webCalendarQuery, nonThreadCalendarFiltersSelector, ( calendarQuery: () => CalendarQuery, filters: $ReadOnlyArray, ) => { return (): CalendarQuery => { const query = calendarQuery(); return { startDate: query.startDate, endDate: query.endDate, filters, }; }; }, ); function navTabSelector(state: AppState): WebNavigationTab { return state.navInfo.tab; } function navSettingsSectionSelector( state: AppState, -): ?NavigationSettingsSection { +): ?WebNavigationSettingsSection { return state.navInfo.settingsSection; } export { yearExtractor, yearAssertingSelector, monthExtractor, monthAssertingSelector, activeThreadSelector, webCalendarQuery, nonThreadCalendarQuery, navTabSelector, navSettingsSectionSelector, }; diff --git a/web/settings/user-settings-list-item.react.js b/web/settings/user-settings-list-item.react.js index 572184b14..e240f7693 100644 --- a/web/settings/user-settings-list-item.react.js +++ b/web/settings/user-settings-list-item.react.js @@ -1,38 +1,38 @@ // @flow import classNames from 'classnames'; import * as React from 'react'; import css from './user-settings-list-item.css'; import { useSelector } from '../redux/redux-utils.js'; import { navSettingsSectionSelector } from '../selectors/nav-selectors.js'; -import type { NavigationSettingsSection } from '../types/nav-types.js'; +import type { WebNavigationSettingsSection } from '../types/nav-types.js'; type Props = { - +id: NavigationSettingsSection, + +id: WebNavigationSettingsSection, +name: string, +onClick: () => mixed, }; function UserSettingsListItem(props: Props): React.Node { const { id, name, onClick } = props; const currentSelectedSettings = useSelector(navSettingsSectionSelector); const className = classNames(css.container, { [css.selected]: currentSelectedSettings === id, }); const userSettingsListItem = React.useMemo( () => (
{name}
), [className, name, onClick], ); return userSettingsListItem; } export default UserSettingsListItem; diff --git a/web/types/nav-types.js b/web/types/nav-types.js index c40ff88dc..8f7344e2e 100644 --- a/web/types/nav-types.js +++ b/web/types/nav-types.js @@ -1,60 +1,60 @@ // @flow import type { TInterface } from 'tcomb'; import t from 'tcomb'; import { threadInfoValidator } from 'lib/permissions/minimally-encoded-thread-permissions-validators.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import { type BaseNavInfo } from 'lib/types/nav-types.js'; import { type AccountUserInfo, accountUserInfoValidator, } from 'lib/types/user-types.js'; import { tID, tShape } from 'lib/utils/validation-utils.js'; export type WebNavigationTab = 'calendar' | 'chat' | 'settings'; const webNavigationTabValidator = t.enums.of(['calendar', 'chat', 'settings']); export type WebLoginMethod = 'form' | 'qr-code'; const webLoginMethodValidator = t.enums.of(['form', 'qr-code']); -export type NavigationSettingsSection = +export type WebNavigationSettingsSection = | 'account' | 'friend-list' | 'block-list' | 'keyservers' | 'danger-zone'; -const navigationSettingsSectionValidator = t.enums.of([ +const webNavigationSettingsSectionValidator = t.enums.of([ 'account', 'friend-list', 'block-list', 'keyservers', 'danger-zone', ]); export type NavigationChatMode = 'view' | 'create'; const navigationChatModeValidator = t.enums.of(['view', 'create']); export type NavInfo = { ...$Exact, +tab: WebNavigationTab, +activeChatThreadID: ?string, +pendingThread?: ThreadInfo, - +settingsSection?: NavigationSettingsSection, + +settingsSection?: WebNavigationSettingsSection, +selectedUserList?: $ReadOnlyArray, +chatMode?: NavigationChatMode, +inviteSecret?: ?string, +loginMethod?: WebLoginMethod, }; export const navInfoValidator: TInterface = tShape<$Exact>({ startDate: t.String, endDate: t.String, tab: webNavigationTabValidator, activeChatThreadID: t.maybe(tID), pendingThread: t.maybe(threadInfoValidator), - settingsSection: t.maybe(navigationSettingsSectionValidator), + settingsSection: t.maybe(webNavigationSettingsSectionValidator), selectedUserList: t.maybe(t.list(accountUserInfoValidator)), chatMode: t.maybe(navigationChatModeValidator), inviteSecret: t.maybe(t.String), loginMethod: t.maybe(webLoginMethodValidator), });