Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F2902555
D12256.id40937.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
53 KB
Referenced Files
None
Subscribers
None
D12256.id40937.diff
View Options
diff --git a/native/calendar/calendar-input-bar.react.js b/native/calendar/calendar-input-bar.react.js
--- a/native/calendar/calendar-input-bar.react.js
+++ b/native/calendar/calendar-input-bar.react.js
@@ -32,6 +32,7 @@
},
inactiveContainer: {
opacity: 0,
+ height: 0,
},
saveButtonText: {
color: 'link',
diff --git a/native/calendar/calendar.react.js b/native/calendar/calendar-screen.react.js
copy from native/calendar/calendar.react.js
copy to native/calendar/calendar-screen.react.js
--- a/native/calendar/calendar.react.js
+++ b/native/calendar/calendar-screen.react.js
@@ -1,5 +1,10 @@
// @flow
+import type {
+ BottomTabNavigationEventMap,
+ TabNavigationState,
+ BottomTabOptions,
+} from '@react-navigation/core';
import invariant from 'invariant';
import _filter from 'lodash/fp/filter.js';
import _find from 'lodash/fp/find.js';
@@ -47,6 +52,7 @@
import sleep from 'lib/utils/sleep.js';
import CalendarInputBar from './calendar-input-bar.react.js';
+import type { CalendarNavigationProp } from './calendar.react.js';
import {
dummyNodeForEntryHeightMeasurement,
Entry,
@@ -54,7 +60,6 @@
} from './entry.react.js';
import SectionFooter from './section-footer.react.js';
import ContentLoading from '../components/content-loading.react.js';
-import KeyboardAvoidingView from '../components/keyboard-avoiding-view.react.js';
import ListLoadingIndicator from '../components/list-loading-indicator.react.js';
import NodeHeightMeasurer from '../components/node-height-measurer.react.js';
import {
@@ -68,7 +73,10 @@
createIsForegroundSelector,
} from '../navigation/nav-selectors.js';
import { NavContext } from '../navigation/navigation-context.js';
-import type { NavigationRoute } from '../navigation/route-names.js';
+import type {
+ NavigationRoute,
+ ScreenParamList,
+} from '../navigation/route-names.js';
import {
CalendarRouteName,
ThreadPickerModalRouteName,
@@ -127,19 +135,6 @@
backgroundColor: 'listBackground',
flex: 1,
},
- keyboardAvoidingViewContainer: {
- position: 'absolute',
- top: 0,
- bottom: 0,
- left: 0,
- right: 0,
- },
- keyboardAvoidingView: {
- position: 'absolute',
- left: 0,
- right: 0,
- bottom: 0,
- },
sectionHeader: {
backgroundColor: 'panelSecondaryForeground',
borderBottomWidth: 2,
@@ -155,8 +150,8 @@
};
type BaseProps = {
- +navigation: TabNavigationProp<'Calendar'>,
- +route: NavigationRoute<'Calendar'>,
+ +navigation: CalendarNavigationProp<'CalendarScreen'>,
+ +route: NavigationRoute<'CalendarScreen'>,
};
type Props = {
...BaseProps,
@@ -186,7 +181,7 @@
+extraData: ExtraData,
+currentlyEditing: $ReadOnlyArray<string>,
};
-class Calendar extends React.PureComponent<Props, State> {
+class CalendarScreen extends React.PureComponent<Props, State> {
flatList: ?FlatList<CalendarItemWithHeight> = null;
currentState: ?string = NativeAppState.currentState;
appStateListener: ?EventSubscription;
@@ -237,7 +232,16 @@
this.keyboardDismissListener = addKeyboardDismissListener(
this.keyboardDismiss,
);
- this.props.navigation.addListener('tabPress', this.onTabPress);
+ this.props.navigation
+ .getParent<
+ ScreenParamList,
+ 'Calendar',
+ TabNavigationState,
+ BottomTabOptions,
+ BottomTabNavigationEventMap,
+ TabNavigationProp<'Calendar'>,
+ >()
+ ?.addListener('tabPress', this.onTabPress);
}
componentWillUnmount() {
@@ -253,7 +257,16 @@
removeKeyboardListener(this.keyboardDismissListener);
this.keyboardDismissListener = null;
}
- this.props.navigation.removeListener('tabPress', this.onTabPress);
+ this.props.navigation
+ .getParent<
+ ScreenParamList,
+ 'Calendar',
+ TabNavigationState,
+ BottomTabOptions,
+ BottomTabNavigationEventMap,
+ TabNavigationProp<'Calendar'>,
+ >()
+ ?.removeListener('tabPress', this.onTabPress);
}
handleAppStateChange = (nextAppState: ?string) => {
@@ -349,7 +362,7 @@
}
const { lastStartDate, newStartDate, lastEndDate, newEndDate } =
- Calendar.datesFromListData(lastLDWH, newLDWH);
+ CalendarScreen.datesFromListData(lastLDWH, newLDWH);
if (newStartDate > lastStartDate || newEndDate < lastEndDate) {
// If there are fewer items in our new data, which happens when the
@@ -425,12 +438,12 @@
lastLDWH: $ReadOnlyArray<CalendarItemWithHeight>,
newLDWH: $ReadOnlyArray<CalendarItemWithHeight>,
) {
- const existingKeys = new Set(_map(Calendar.keyExtractor)(lastLDWH));
+ const existingKeys = new Set(_map(CalendarScreen.keyExtractor)(lastLDWH));
const newItems = _filter(
(item: CalendarItemWithHeight) =>
- !existingKeys.has(Calendar.keyExtractor(item)),
+ !existingKeys.has(CalendarScreen.keyExtractor(item)),
)(newLDWH);
- const heightOfNewItems = Calendar.heightOfItems(newItems);
+ const heightOfNewItems = CalendarScreen.heightOfItems(newItems);
const flatList = this.flatList;
invariant(flatList, 'flatList should be set');
const scrollAction = () => {
@@ -560,9 +573,11 @@
if (!data) {
return { length: 0, offset: 0, index };
}
- const offset = Calendar.heightOfItems(data.filter((_, i) => i < index));
+ const offset = CalendarScreen.heightOfItems(
+ data.filter((_, i) => i < index),
+ );
const item = data[index];
- const length = item ? Calendar.itemHeight(item) : 0;
+ const length = item ? CalendarScreen.itemHeight(item) : 0;
return { length, offset, index };
};
@@ -585,7 +600,7 @@
static heightOfItems = (
data: $ReadOnlyArray<CalendarItemWithHeight>,
): number => {
- return _sum(data.map(Calendar.itemHeight));
+ return _sum(data.map(CalendarScreen.itemHeight));
};
render(): React.Node {
@@ -598,8 +613,8 @@
<FlatList
data={listDataWithHeights}
renderItem={this.renderItem}
- keyExtractor={Calendar.keyExtractor}
- getItemLayout={Calendar.getItemLayout}
+ keyExtractor={CalendarScreen.keyExtractor}
+ getItemLayout={CalendarScreen.getItemLayout}
onViewableItemsChanged={this.onViewableItemsChanged}
onScroll={this.onScroll}
initialScrollIndex={initialScrollIndex}
@@ -626,7 +641,7 @@
<>
<NodeHeightMeasurer
listData={this.props.listData}
- itemToID={Calendar.keyExtractor}
+ itemToID={CalendarScreen.keyExtractor}
itemToMeasureKey={this.heightMeasurerKey}
itemToDummy={this.heightMeasurerDummy}
mergeItemWithHeight={this.heightMeasurerMergeItem}
@@ -637,17 +652,12 @@
{loadingIndicator}
{flatList}
</View>
- <KeyboardAvoidingView
- behavior="position"
- style={this.props.styles.keyboardAvoidingViewContainer}
- contentContainerStyle={this.props.styles.keyboardAvoidingView}
- pointerEvents="box-none"
- >
+ <View pointerEvents="box-none">
<CalendarInputBar
onSave={this.onSaveEntry}
disabled={disableInputBar}
/>
- </KeyboardAvoidingView>
+ </View>
</>
);
}
@@ -659,12 +669,12 @@
initialScrollIndex(data: $ReadOnlyArray<CalendarItemWithHeight>): number {
const todayIndex = _findIndex(['dateString', dateString(new Date())])(data);
- const heightOfTodayHeader = Calendar.itemHeight(data[todayIndex]);
+ const heightOfTodayHeader = CalendarScreen.itemHeight(data[todayIndex]);
let returnIndex = todayIndex;
let heightLeft = (this.flatListHeight() - heightOfTodayHeader) / 2;
while (heightLeft > 0) {
- heightLeft -= Calendar.itemHeight(data[--returnIndex]);
+ heightLeft -= CalendarScreen.itemHeight(data[--returnIndex]);
}
return returnIndex;
}
@@ -792,13 +802,15 @@
invariant(data, 'should be set');
const index = data.findIndex(
(item: CalendarItemWithHeight) =>
- Calendar.keyExtractor(item) === lastEntryKeyActive,
+ CalendarScreen.keyExtractor(item) === lastEntryKeyActive,
);
if (index === -1) {
return;
}
- const itemStart = Calendar.heightOfItems(data.filter((_, i) => i < index));
- const itemHeight = Calendar.itemHeight(data[index]);
+ const itemStart = CalendarScreen.heightOfItems(
+ data.filter((_, i) => i < index),
+ );
+ const itemHeight = CalendarScreen.itemHeight(data[index]);
const entryAdditionalActiveHeight = Platform.OS === 'android' ? 21 : 20;
const itemEnd = itemStart + itemHeight + entryAdditionalActiveHeight;
const visibleHeight = this.flatListHeight() - keyboardHeight;
@@ -841,7 +853,7 @@
const { entryInfo } = item;
return {
itemType: 'entryInfo',
- entryInfo: Calendar.entryInfoWithHeight(entryInfo, height),
+ entryInfo: CalendarScreen.entryInfoWithHeight(entryInfo, height),
threadInfo: item.threadInfo,
};
};
@@ -1064,8 +1076,8 @@
ThreadPickerModalRouteName,
);
-const ConnectedCalendar: React.ComponentType<BaseProps> = React.memo<BaseProps>(
- function ConnectedCalendar(props: BaseProps) {
+const ConnectedCalendarScreen: React.ComponentType<BaseProps> =
+ React.memo<BaseProps>(function ConnectedCalendarScreen(props: BaseProps) {
const navContext = React.useContext(NavContext);
const calendarActive =
activeTabSelector(navContext) || activeThreadPickerSelector(navContext);
@@ -1085,7 +1097,7 @@
const callUpdateCalendarQuery = useUpdateCalendarQuery();
return (
- <Calendar
+ <CalendarScreen
{...props}
calendarActive={calendarActive}
listData={listData}
@@ -1102,7 +1114,6 @@
updateCalendarQuery={callUpdateCalendarQuery}
/>
);
- },
-);
+ });
-export default ConnectedCalendar;
+export default ConnectedCalendarScreen;
diff --git a/native/calendar/calendar.react.js b/native/calendar/calendar.react.js
--- a/native/calendar/calendar.react.js
+++ b/native/calendar/calendar.react.js
@@ -1,1108 +1,85 @@
// @flow
-import invariant from 'invariant';
-import _filter from 'lodash/fp/filter.js';
-import _find from 'lodash/fp/find.js';
-import _findIndex from 'lodash/fp/findIndex.js';
-import _map from 'lodash/fp/map.js';
-import _pickBy from 'lodash/fp/pickBy.js';
-import _size from 'lodash/fp/size.js';
-import _sum from 'lodash/fp/sum.js';
-import _throttle from 'lodash/throttle.js';
-import * as React from 'react';
-import {
- AppState as NativeAppState,
- FlatList,
- LayoutAnimation,
- Platform,
- Text,
- TouchableWithoutFeedback,
- View,
-} from 'react-native';
-
-import type { UpdateCalendarQueryInput } from 'lib/actions/entry-actions.js';
-import {
- updateCalendarQueryActionTypes,
- useUpdateCalendarQuery,
-} from 'lib/actions/entry-actions.js';
-import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js';
-import { entryKey } from 'lib/shared/entry-utils.js';
import type {
- CalendarQuery,
- CalendarQueryUpdateResult,
- EntryInfo,
-} from 'lib/types/entry-types.js';
-import type { CalendarFilter } from 'lib/types/filter-types.js';
-import type { LoadingStatus } from 'lib/types/loading-types.js';
-import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
-import {
- dateFromString,
- dateString,
- prettyDate,
-} from 'lib/utils/date-utils.js';
-import {
- type DispatchActionPromise,
- useDispatchActionPromise,
-} from 'lib/utils/redux-promise-utils.js';
-import sleep from 'lib/utils/sleep.js';
+ StackNavigationProp,
+ StackNavigationHelpers,
+} from '@react-navigation/core';
+import { createStackNavigator } from '@react-navigation/stack';
+import * as React from 'react';
+import { View } from 'react-native';
-import CalendarInputBar from './calendar-input-bar.react.js';
-import {
- dummyNodeForEntryHeightMeasurement,
- Entry,
- InternalEntry,
-} from './entry.react.js';
-import SectionFooter from './section-footer.react.js';
-import ContentLoading from '../components/content-loading.react.js';
+import CalendarScreen from './calendar-screen.react.js';
import KeyboardAvoidingView from '../components/keyboard-avoiding-view.react.js';
-import ListLoadingIndicator from '../components/list-loading-indicator.react.js';
-import NodeHeightMeasurer from '../components/node-height-measurer.react.js';
-import {
- addKeyboardDismissListener,
- addKeyboardShowListener,
- removeKeyboardListener,
-} from '../keyboard/keyboard.js';
-import DisconnectedBar from '../navigation/disconnected-bar.react.js';
-import {
- createActiveTabSelector,
- createIsForegroundSelector,
-} from '../navigation/nav-selectors.js';
-import { NavContext } from '../navigation/navigation-context.js';
-import type { NavigationRoute } from '../navigation/route-names.js';
+import CommunityDrawerButton from '../navigation/community-drawer-button.react.js';
import {
- CalendarRouteName,
- ThreadPickerModalRouteName,
+ CalendarScreenRouteName,
+ type CalendarParamList,
+ type ScreenParamList,
} from '../navigation/route-names.js';
import type { TabNavigationProp } from '../navigation/tab-navigator.react.js';
-import { useSelector } from '../redux/redux-utils.js';
-import type {
- CalendarItem,
- LoaderItem,
- SectionFooterItem,
- SectionHeaderItem,
-} from '../selectors/calendar-selectors.js';
-import { calendarListData } from '../selectors/calendar-selectors.js';
-import {
- type DerivedDimensionsInfo,
- derivedDimensionsInfoSelector,
-} from '../selectors/dimensions-selectors.js';
-import {
- type Colors,
- type IndicatorStyle,
- useColors,
- useIndicatorStyle,
- useStyles,
-} from '../themes/colors.js';
-import type {
- EventSubscription,
- KeyboardEvent,
- ScrollEvent,
- ViewableItemsChange,
-} from '../types/react-native.js';
-
-export type EntryInfoWithHeight = {
- ...EntryInfo,
- +textHeight: number,
-};
-type CalendarItemWithHeight =
- | LoaderItem
- | SectionHeaderItem
- | SectionFooterItem
- | {
- itemType: 'entryInfo',
- entryInfo: EntryInfoWithHeight,
- threadInfo: ThreadInfo,
- };
-type ExtraData = {
- +activeEntries: { +[key: string]: boolean },
- +visibleEntries: { +[key: string]: boolean },
-};
+import { useStyles, useColors } from '../themes/colors.js';
-const unboundStyles = {
- container: {
- backgroundColor: 'listBackground',
- flex: 1,
- },
- flatList: {
- backgroundColor: 'listBackground',
- flex: 1,
- },
- keyboardAvoidingViewContainer: {
- position: 'absolute',
- top: 0,
- bottom: 0,
- left: 0,
- right: 0,
- },
- keyboardAvoidingView: {
- position: 'absolute',
- left: 0,
- right: 0,
- bottom: 0,
- },
- sectionHeader: {
- backgroundColor: 'panelSecondaryForeground',
- borderBottomWidth: 2,
- borderColor: 'listBackground',
- height: 31,
- },
- sectionHeaderText: {
- color: 'listSeparatorLabel',
- fontWeight: 'bold',
- padding: 5,
- },
- weekendSectionHeader: {},
-};
+export type CalendarNavigationProp<
+ RouteName: $Keys<CalendarParamList> = $Keys<CalendarParamList>,
+> = StackNavigationProp<ScreenParamList, RouteName>;
-type BaseProps = {
- +navigation: TabNavigationProp<'Calendar'>,
- +route: NavigationRoute<'Calendar'>,
-};
+const Calendar = createStackNavigator<
+ ScreenParamList,
+ CalendarParamList,
+ StackNavigationHelpers<ScreenParamList>,
+>();
type Props = {
- ...BaseProps,
- // Nav state
- +calendarActive: boolean,
- // Redux state
- +listData: ?$ReadOnlyArray<CalendarItem>,
- +startDate: string,
- +endDate: string,
- +calendarFilters: $ReadOnlyArray<CalendarFilter>,
- +dimensions: DerivedDimensionsInfo,
- +loadingStatus: LoadingStatus,
- +connected: boolean,
- +colors: Colors,
- +styles: $ReadOnly<typeof unboundStyles>,
- +indicatorStyle: IndicatorStyle,
- // Redux dispatch functions
- +dispatchActionPromise: DispatchActionPromise,
- // async functions that hit server APIs
- +updateCalendarQuery: (
- input: UpdateCalendarQueryInput,
- ) => Promise<CalendarQueryUpdateResult>,
-};
-type State = {
- +listDataWithHeights: ?$ReadOnlyArray<CalendarItemWithHeight>,
- +readyToShowList: boolean,
- +extraData: ExtraData,
- +currentlyEditing: $ReadOnlyArray<string>,
+ +navigation: TabNavigationProp<'Calendar'>,
+ ...
};
-class Calendar extends React.PureComponent<Props, State> {
- flatList: ?FlatList<CalendarItemWithHeight> = null;
- currentState: ?string = NativeAppState.currentState;
- appStateListener: ?EventSubscription;
- lastForegrounded = 0;
- lastCalendarReset = 0;
- currentScrollPosition: ?number = null;
- // We don't always want an extraData update to trigger a state update, so we
- // cache the most recent value as a member here
- latestExtraData: ExtraData;
- // For some reason, we have to delay the scrollToToday call after the first
- // scroll upwards
- firstScrollComplete = false;
- // When an entry becomes active, we make a note of its key so that once the
- // keyboard event happens, we know where to move the scrollPos to
- lastEntryKeyActive: ?string = null;
- keyboardShowListener: ?EventSubscription;
- keyboardDismissListener: ?EventSubscription;
- keyboardShownHeight: ?number = null;
- // If the query fails, we try it again
- topLoadingFromScroll: ?CalendarQuery = null;
- bottomLoadingFromScroll: ?CalendarQuery = null;
- // We wait until the loaders leave view before letting them be triggered again
- topLoaderWaitingToLeaveView = true;
- bottomLoaderWaitingToLeaveView = true;
- // We keep refs to the entries so CalendarInputBar can save them
- entryRefs: Map<string, ?InternalEntry> = new Map();
-
- constructor(props: Props) {
- super(props);
- this.latestExtraData = {
- activeEntries: {},
- visibleEntries: {},
- };
- this.state = {
- listDataWithHeights: null,
- readyToShowList: false,
- extraData: this.latestExtraData,
- currentlyEditing: [],
- };
- }
-
- componentDidMount() {
- this.appStateListener = NativeAppState.addEventListener(
- 'change',
- this.handleAppStateChange,
- );
- this.keyboardShowListener = addKeyboardShowListener(this.keyboardShow);
- this.keyboardDismissListener = addKeyboardDismissListener(
- this.keyboardDismiss,
- );
- this.props.navigation.addListener('tabPress', this.onTabPress);
- }
-
- componentWillUnmount() {
- if (this.appStateListener) {
- this.appStateListener.remove();
- this.appStateListener = null;
- }
- if (this.keyboardShowListener) {
- removeKeyboardListener(this.keyboardShowListener);
- this.keyboardShowListener = null;
- }
- if (this.keyboardDismissListener) {
- removeKeyboardListener(this.keyboardDismissListener);
- this.keyboardDismissListener = null;
- }
- this.props.navigation.removeListener('tabPress', this.onTabPress);
- }
-
- handleAppStateChange = (nextAppState: ?string) => {
- const lastState = this.currentState;
- this.currentState = nextAppState;
- if (
- !lastState ||
- !lastState.match(/inactive|background/) ||
- this.currentState !== 'active'
- ) {
- // We're only handling foregrounding here
- return;
- }
- if (Date.now() - this.lastCalendarReset < 500) {
- // If the calendar got reset right before this callback triggered, that
- // indicates we should reset the scroll position
- this.lastCalendarReset = 0;
- this.scrollToToday(false);
- } else {
- // Otherwise, it's possible that the calendar is about to get reset. We
- // record a timestamp here so we can scrollToToday there.
- this.lastForegrounded = Date.now();
- }
- };
-
- onTabPress = () => {
- if (this.props.navigation.isFocused()) {
- this.scrollToToday();
- }
- };
-
- componentDidUpdate(prevProps: Props, prevState: State) {
- if (!this.props.listData && this.props.listData !== prevProps.listData) {
- this.latestExtraData = {
- activeEntries: {},
- visibleEntries: {},
- };
- this.setState({
- listDataWithHeights: null,
- readyToShowList: false,
- extraData: this.latestExtraData,
- });
- this.firstScrollComplete = false;
- this.topLoaderWaitingToLeaveView = true;
- this.bottomLoaderWaitingToLeaveView = true;
- }
-
- const { loadingStatus, connected } = this.props;
- const { loadingStatus: prevLoadingStatus, connected: prevConnected } =
- prevProps;
- if (
- (loadingStatus === 'error' && prevLoadingStatus === 'loading') ||
- (connected && !prevConnected)
- ) {
- this.loadMoreAbove();
- this.loadMoreBelow();
- }
-
- const lastLDWH = prevState.listDataWithHeights;
- const newLDWH = this.state.listDataWithHeights;
- if (!newLDWH) {
- return;
- } else if (!lastLDWH) {
- if (!this.props.calendarActive) {
- // FlatList has an initialScrollIndex prop, which is usually close to
- // centering but can be off when there is a particularly large Entry in
- // the list. scrollToToday lets us actually center, but gets overriden
- // by initialScrollIndex if we call it right after the FlatList mounts
- void sleep(50).then(() => this.scrollToToday());
- }
- return;
- }
-
- if (newLDWH.length < lastLDWH.length) {
- this.topLoaderWaitingToLeaveView = true;
- this.bottomLoaderWaitingToLeaveView = true;
- if (this.flatList) {
- if (!this.props.calendarActive) {
- // If the currentCalendarQuery gets reset we scroll to the center
- this.scrollToToday();
- } else if (Date.now() - this.lastForegrounded < 500) {
- // If the app got foregrounded right before the calendar got reset,
- // that indicates we should reset the scroll position
- this.lastForegrounded = 0;
- this.scrollToToday(false);
- } else {
- // Otherwise, it's possible that we got triggered before the
- // foreground callback. Let's record a timestamp here so we can call
- // scrollToToday there
- this.lastCalendarReset = Date.now();
- }
- }
- }
-
- const { lastStartDate, newStartDate, lastEndDate, newEndDate } =
- Calendar.datesFromListData(lastLDWH, newLDWH);
-
- if (newStartDate > lastStartDate || newEndDate < lastEndDate) {
- // If there are fewer items in our new data, which happens when the
- // current calendar query gets reset due to inactivity, let's reset the
- // scroll position to the center (today)
- if (!this.props.calendarActive) {
- void sleep(50).then(() => this.scrollToToday());
- }
- this.firstScrollComplete = false;
- } else if (newStartDate < lastStartDate) {
- this.updateScrollPositionAfterPrepend(lastLDWH, newLDWH);
- } else if (newEndDate > lastEndDate) {
- this.firstScrollComplete = true;
- } else if (newLDWH.length > lastLDWH.length) {
- LayoutAnimation.easeInEaseOut();
- }
-
- if (newStartDate < lastStartDate) {
- this.topLoadingFromScroll = null;
- }
- if (newEndDate > lastEndDate) {
- this.bottomLoadingFromScroll = null;
- }
-
- const { keyboardShownHeight, lastEntryKeyActive } = this;
- if (keyboardShownHeight && lastEntryKeyActive) {
- this.scrollToKey(lastEntryKeyActive, keyboardShownHeight);
- this.lastEntryKeyActive = null;
- }
- }
-
- static datesFromListData(
- lastLDWH: $ReadOnlyArray<CalendarItemWithHeight>,
- newLDWH: $ReadOnlyArray<CalendarItemWithHeight>,
- ): {
- +lastStartDate: Date,
- +newStartDate: Date,
- +lastEndDate: Date,
- +newEndDate: Date,
- } {
- const lastSecondItem = lastLDWH[1];
- const newSecondItem = newLDWH[1];
- invariant(
- newSecondItem.itemType === 'header' &&
- lastSecondItem.itemType === 'header',
- 'second item in listData should be a header',
- );
- const lastStartDate = dateFromString(lastSecondItem.dateString);
- const newStartDate = dateFromString(newSecondItem.dateString);
-
- const lastPenultimateItem = lastLDWH[lastLDWH.length - 2];
- const newPenultimateItem = newLDWH[newLDWH.length - 2];
- invariant(
- newPenultimateItem.itemType === 'footer' &&
- lastPenultimateItem.itemType === 'footer',
- 'penultimate item in listData should be a footer',
- );
- const lastEndDate = dateFromString(lastPenultimateItem.dateString);
- const newEndDate = dateFromString(newPenultimateItem.dateString);
-
- return { lastStartDate, newStartDate, lastEndDate, newEndDate };
- }
-
- /**
- * When prepending list items, FlatList isn't smart about preserving scroll
- * position. If we're at the start of the list before prepending, FlatList
- * will just keep us at the front after prepending. But we want to preserve
- * the previous on-screen items, so we have to do a calculation to get the new
- * scroll position. (And deal with the inherent glitchiness of trying to time
- * that change with the items getting prepended... *sigh*.)
- */
- updateScrollPositionAfterPrepend(
- lastLDWH: $ReadOnlyArray<CalendarItemWithHeight>,
- newLDWH: $ReadOnlyArray<CalendarItemWithHeight>,
- ) {
- const existingKeys = new Set(_map(Calendar.keyExtractor)(lastLDWH));
- const newItems = _filter(
- (item: CalendarItemWithHeight) =>
- !existingKeys.has(Calendar.keyExtractor(item)),
- )(newLDWH);
- const heightOfNewItems = Calendar.heightOfItems(newItems);
- const flatList = this.flatList;
- invariant(flatList, 'flatList should be set');
- const scrollAction = () => {
- invariant(
- this.currentScrollPosition !== undefined &&
- this.currentScrollPosition !== null,
- 'currentScrollPosition should be set',
- );
- const currentScrollPosition = Math.max(this.currentScrollPosition, 0);
- const offset = currentScrollPosition + heightOfNewItems;
- flatList.scrollToOffset({
- offset,
- animated: false,
- });
- };
- scrollAction();
- if (!this.firstScrollComplete) {
- setTimeout(scrollAction, 0);
- this.firstScrollComplete = true;
- }
- }
-
- scrollToToday(animated: ?boolean = undefined) {
- if (animated === undefined) {
- animated = this.props.calendarActive;
- }
- const ldwh = this.state.listDataWithHeights;
- if (!ldwh) {
- return;
- }
- const todayIndex = _findIndex(['dateString', dateString(new Date())])(ldwh);
- invariant(this.flatList, "scrollToToday called, but flatList isn't set");
- this.flatList.scrollToIndex({
- index: todayIndex,
- animated,
- viewPosition: 0.5,
- });
- }
- // ESLint doesn't recognize that invariant always throws
- // eslint-disable-next-line consistent-return
- renderItem = (row: { +item: CalendarItemWithHeight, ... }): React.Node => {
- const item = row.item;
- if (item.itemType === 'loader') {
- return <ListLoadingIndicator />;
- } else if (item.itemType === 'header') {
- return this.renderSectionHeader(item);
- } else if (item.itemType === 'entryInfo') {
- const key = entryKey(item.entryInfo);
- return (
- <Entry
- navigation={this.props.navigation}
- entryInfo={item.entryInfo}
- threadInfo={item.threadInfo}
- active={!!this.state.extraData.activeEntries[key]}
- visible={!!this.state.extraData.visibleEntries[key]}
- makeActive={this.makeActive}
- onEnterEditMode={this.onEnterEntryEditMode}
- onConcludeEditMode={this.onConcludeEntryEditMode}
- onPressWhitespace={this.makeAllEntriesInactive}
- entryRef={this.entryRef}
- />
- );
- } else if (item.itemType === 'footer') {
- return this.renderSectionFooter(item);
- }
- invariant(false, 'renderItem conditions should be exhaustive');
- };
-
- renderSectionHeader = (item: SectionHeaderItem): React.Node => {
- let date = prettyDate(item.dateString);
- if (dateString(new Date()) === item.dateString) {
- date += ' (today)';
- }
- const dateObj = dateFromString(item.dateString).getDay();
- const weekendStyle =
- dateObj === 0 || dateObj === 6
- ? this.props.styles.weekendSectionHeader
- : null;
- return (
- <TouchableWithoutFeedback onPress={this.makeAllEntriesInactive}>
- <View style={[this.props.styles.sectionHeader, weekendStyle]}>
- <Text style={this.props.styles.sectionHeaderText}>{date}</Text>
- </View>
- </TouchableWithoutFeedback>
- );
- };
-
- renderSectionFooter = (item: SectionFooterItem): React.Node => {
- return (
- <SectionFooter
- dateString={item.dateString}
- onAdd={this.onAdd}
- onPressWhitespace={this.makeAllEntriesInactive}
- />
- );
- };
-
- onAdd = (dayString: string) => {
- this.props.navigation.navigate(ThreadPickerModalRouteName, {
- presentedFrom: this.props.route.key,
- dateString: dayString,
- });
- };
-
- static keyExtractor = (
- item: CalendarItemWithHeight | CalendarItem,
- // ESLint doesn't recognize that invariant always throws
- // eslint-disable-next-line consistent-return
- ): string => {
- if (item.itemType === 'loader') {
- return item.key;
- } else if (item.itemType === 'header') {
- return item.dateString + '/header';
- } else if (item.itemType === 'entryInfo') {
- return entryKey(item.entryInfo);
- } else if (item.itemType === 'footer') {
- return item.dateString + '/footer';
- }
- invariant(false, 'keyExtractor conditions should be exhaustive');
- };
-
- static getItemLayout = (
- data: ?$ReadOnlyArray<CalendarItemWithHeight>,
- index: number,
- ): { length: number, offset: number, index: number } => {
- if (!data) {
- return { length: 0, offset: 0, index };
- }
- const offset = Calendar.heightOfItems(data.filter((_, i) => i < index));
- const item = data[index];
- const length = item ? Calendar.itemHeight(item) : 0;
- return { length, offset, index };
- };
-
- // ESLint doesn't recognize that invariant always throws
- // eslint-disable-next-line consistent-return
- static itemHeight = (item: CalendarItemWithHeight): number => {
- if (item.itemType === 'loader') {
- return 56;
- } else if (item.itemType === 'header') {
- return 31;
- } else if (item.itemType === 'entryInfo') {
- const verticalPadding = 10;
- return verticalPadding + item.entryInfo.textHeight;
- } else if (item.itemType === 'footer') {
- return 40;
- }
- invariant(false, 'itemHeight conditions should be exhaustive');
- };
-
- static heightOfItems = (
- data: $ReadOnlyArray<CalendarItemWithHeight>,
- ): number => {
- return _sum(data.map(Calendar.itemHeight));
- };
-
- render(): React.Node {
- const { listDataWithHeights } = this.state;
- let flatList = null;
- if (listDataWithHeights) {
- const flatListStyle = { opacity: this.state.readyToShowList ? 1 : 0 };
- const initialScrollIndex = this.initialScrollIndex(listDataWithHeights);
- flatList = (
- <FlatList
- data={listDataWithHeights}
- renderItem={this.renderItem}
- keyExtractor={Calendar.keyExtractor}
- getItemLayout={Calendar.getItemLayout}
- onViewableItemsChanged={this.onViewableItemsChanged}
- onScroll={this.onScroll}
- initialScrollIndex={initialScrollIndex}
- keyboardShouldPersistTaps="handled"
- keyboardDismissMode="on-drag"
- onMomentumScrollEnd={this.onMomentumScrollEnd}
- onScrollEndDrag={this.onScrollEndDrag}
- scrollsToTop={false}
- extraData={this.state.extraData}
- style={[this.props.styles.flatList, flatListStyle]}
- indicatorStyle={this.props.indicatorStyle}
- ref={this.flatListRef}
- />
- );
- }
- let loadingIndicator = null;
- if (!listDataWithHeights || !this.state.readyToShowList) {
- loadingIndicator = (
- <ContentLoading fillType="absolute" colors={this.props.colors} />
- );
- }
- const disableInputBar = this.state.currentlyEditing.length === 0;
- return (
- <>
- <NodeHeightMeasurer
- listData={this.props.listData}
- itemToID={Calendar.keyExtractor}
- itemToMeasureKey={this.heightMeasurerKey}
- itemToDummy={this.heightMeasurerDummy}
- mergeItemWithHeight={this.heightMeasurerMergeItem}
- allHeightsMeasured={this.allHeightsMeasured}
- />
- <View style={this.props.styles.container}>
- <DisconnectedBar visible={this.props.calendarActive} />
- {loadingIndicator}
- {flatList}
- </View>
- <KeyboardAvoidingView
- behavior="position"
- style={this.props.styles.keyboardAvoidingViewContainer}
- contentContainerStyle={this.props.styles.keyboardAvoidingView}
- pointerEvents="box-none"
- >
- <CalendarInputBar
- onSave={this.onSaveEntry}
- disabled={disableInputBar}
+function CalendarComponent(props: Props): React.Node {
+ const styles = useStyles(unboundStyles);
+ const colors = useColors();
+
+ const headerLeft = React.useCallback(
+ () => <CommunityDrawerButton navigation={props.navigation} />,
+ [props.navigation],
+ );
+
+ const options = React.useMemo(
+ () => ({
+ headerTitle: 'Calendar',
+ headerLeft,
+ headerStyle: {
+ backgroundColor: colors.tabBarBackground,
+ },
+ headerShadowVisible: false,
+ }),
+ [colors.tabBarBackground, headerLeft],
+ );
+
+ return (
+ <View style={styles.view}>
+ <KeyboardAvoidingView
+ behavior="padding"
+ style={styles.keyboardAvoidingView}
+ >
+ <Calendar.Navigator>
+ <Calendar.Screen
+ name={CalendarScreenRouteName}
+ component={CalendarScreen}
+ options={options}
/>
- </KeyboardAvoidingView>
- </>
- );
- }
-
- flatListHeight(): number {
- const { safeAreaHeight, tabBarHeight } = this.props.dimensions;
- return safeAreaHeight - tabBarHeight;
- }
-
- initialScrollIndex(data: $ReadOnlyArray<CalendarItemWithHeight>): number {
- const todayIndex = _findIndex(['dateString', dateString(new Date())])(data);
- const heightOfTodayHeader = Calendar.itemHeight(data[todayIndex]);
-
- let returnIndex = todayIndex;
- let heightLeft = (this.flatListHeight() - heightOfTodayHeader) / 2;
- while (heightLeft > 0) {
- heightLeft -= Calendar.itemHeight(data[--returnIndex]);
- }
- return returnIndex;
- }
-
- flatListRef = (flatList: ?FlatList<CalendarItemWithHeight>) => {
- this.flatList = flatList;
- };
-
- entryRef = (inEntryKey: string, entry: ?InternalEntry) => {
- this.entryRefs.set(inEntryKey, entry);
- };
-
- makeAllEntriesInactive = () => {
- if (_size(this.state.extraData.activeEntries) === 0) {
- if (_size(this.latestExtraData.activeEntries) !== 0) {
- this.latestExtraData = {
- visibleEntries: this.latestExtraData.visibleEntries,
- activeEntries: this.state.extraData.activeEntries,
- };
- }
- return;
- }
- this.latestExtraData = {
- visibleEntries: this.latestExtraData.visibleEntries,
- activeEntries: {},
- };
- this.setState({ extraData: this.latestExtraData });
- };
-
- makeActive = (key: string, active: boolean) => {
- if (!active) {
- const activeKeys = Object.keys(this.latestExtraData.activeEntries);
- if (activeKeys.length === 0) {
- if (Object.keys(this.state.extraData.activeEntries).length !== 0) {
- this.setState({ extraData: this.latestExtraData });
- }
- return;
- }
- const activeKey = activeKeys[0];
- if (activeKey === key) {
- this.latestExtraData = {
- visibleEntries: this.latestExtraData.visibleEntries,
- activeEntries: {},
- };
- this.setState({ extraData: this.latestExtraData });
- }
- return;
- }
-
- if (
- _size(this.state.extraData.activeEntries) === 1 &&
- this.state.extraData.activeEntries[key]
- ) {
- if (
- _size(this.latestExtraData.activeEntries) !== 1 ||
- !this.latestExtraData.activeEntries[key]
- ) {
- this.latestExtraData = {
- visibleEntries: this.latestExtraData.visibleEntries,
- activeEntries: this.state.extraData.activeEntries,
- };
- }
- return;
- }
- this.latestExtraData = {
- visibleEntries: this.latestExtraData.visibleEntries,
- activeEntries: { [key]: true },
- };
- this.setState({ extraData: this.latestExtraData });
- };
-
- onEnterEntryEditMode = (entryInfo: EntryInfoWithHeight) => {
- const key = entryKey(entryInfo);
- const keyboardShownHeight = this.keyboardShownHeight;
- if (keyboardShownHeight && this.state.listDataWithHeights) {
- this.scrollToKey(key, keyboardShownHeight);
- } else {
- this.lastEntryKeyActive = key;
- }
- const newCurrentlyEditing = [
- ...new Set([...this.state.currentlyEditing, key]),
- ];
- if (newCurrentlyEditing.length > this.state.currentlyEditing.length) {
- this.setState({ currentlyEditing: newCurrentlyEditing });
- }
- };
-
- onConcludeEntryEditMode = (entryInfo: EntryInfoWithHeight) => {
- const key = entryKey(entryInfo);
- const newCurrentlyEditing = this.state.currentlyEditing.filter(
- k => k !== key,
- );
- if (newCurrentlyEditing.length < this.state.currentlyEditing.length) {
- this.setState({ currentlyEditing: newCurrentlyEditing });
- }
- };
-
- keyboardShow = (event: KeyboardEvent) => {
- // flatListHeight() factors in the size of the tab bar,
- // but it is hidden by the keyboard since it is at the bottom
- const { bottomInset, tabBarHeight } = this.props.dimensions;
- const inputBarHeight = Platform.OS === 'android' ? 37.7 : 35.5;
- const keyboardHeight: number = Platform.select({
- // Android doesn't include the bottomInset in this height measurement
- android: event.endCoordinates.height,
- default: Math.max(event.endCoordinates.height - bottomInset, 0),
- });
- const keyboardShownHeight =
- inputBarHeight + Math.max(keyboardHeight - tabBarHeight, 0);
- this.keyboardShownHeight = keyboardShownHeight;
-
- const lastEntryKeyActive = this.lastEntryKeyActive;
- if (lastEntryKeyActive && this.state.listDataWithHeights) {
- this.scrollToKey(lastEntryKeyActive, keyboardShownHeight);
- this.lastEntryKeyActive = null;
- }
- };
-
- keyboardDismiss = () => {
- this.keyboardShownHeight = null;
- };
-
- scrollToKey(lastEntryKeyActive: string, keyboardHeight: number) {
- const data = this.state.listDataWithHeights;
- invariant(data, 'should be set');
- const index = data.findIndex(
- (item: CalendarItemWithHeight) =>
- Calendar.keyExtractor(item) === lastEntryKeyActive,
- );
- if (index === -1) {
- return;
- }
- const itemStart = Calendar.heightOfItems(data.filter((_, i) => i < index));
- const itemHeight = Calendar.itemHeight(data[index]);
- const entryAdditionalActiveHeight = Platform.OS === 'android' ? 21 : 20;
- const itemEnd = itemStart + itemHeight + entryAdditionalActiveHeight;
- const visibleHeight = this.flatListHeight() - keyboardHeight;
- if (
- this.currentScrollPosition !== undefined &&
- this.currentScrollPosition !== null &&
- itemStart > this.currentScrollPosition &&
- itemEnd < this.currentScrollPosition + visibleHeight
- ) {
- return;
- }
- const offset = itemStart - (visibleHeight - itemHeight) / 2;
- invariant(this.flatList, 'flatList should be set');
- this.flatList.scrollToOffset({ offset, animated: true });
- }
-
- heightMeasurerKey = (item: CalendarItem): ?string => {
- if (item.itemType !== 'entryInfo') {
- return null;
- }
- return item.entryInfo.text;
- };
-
- heightMeasurerDummy = (item: CalendarItem): React.MixedElement => {
- invariant(
- item.itemType === 'entryInfo',
- 'NodeHeightMeasurer asked for dummy for non-entryInfo item',
- );
- return dummyNodeForEntryHeightMeasurement(item.entryInfo.text);
- };
-
- heightMeasurerMergeItem = (
- item: CalendarItem,
- height: ?number,
- ): CalendarItemWithHeight => {
- if (item.itemType !== 'entryInfo') {
- return item;
- }
- invariant(height !== null && height !== undefined, 'height should be set');
- const { entryInfo } = item;
- return {
- itemType: 'entryInfo',
- entryInfo: Calendar.entryInfoWithHeight(entryInfo, height),
- threadInfo: item.threadInfo,
- };
- };
-
- static entryInfoWithHeight(
- entryInfo: EntryInfo,
- textHeight: number,
- ): EntryInfoWithHeight {
- // Blame Flow for not accepting object spread on exact types
- if (entryInfo.id && entryInfo.localID) {
- return {
- id: entryInfo.id,
- localID: entryInfo.localID,
- threadID: entryInfo.threadID,
- text: entryInfo.text,
- year: entryInfo.year,
- month: entryInfo.month,
- day: entryInfo.day,
- creationTime: entryInfo.creationTime,
- creator: entryInfo.creator,
- deleted: entryInfo.deleted,
- textHeight: Math.ceil(textHeight),
- };
- } else if (entryInfo.id) {
- return {
- id: entryInfo.id,
- threadID: entryInfo.threadID,
- text: entryInfo.text,
- year: entryInfo.year,
- month: entryInfo.month,
- day: entryInfo.day,
- creationTime: entryInfo.creationTime,
- creator: entryInfo.creator,
- deleted: entryInfo.deleted,
- textHeight: Math.ceil(textHeight),
- };
- } else {
- return {
- localID: entryInfo.localID,
- threadID: entryInfo.threadID,
- text: entryInfo.text,
- year: entryInfo.year,
- month: entryInfo.month,
- day: entryInfo.day,
- creationTime: entryInfo.creationTime,
- creator: entryInfo.creator,
- deleted: entryInfo.deleted,
- textHeight: Math.ceil(textHeight),
- };
- }
- }
-
- allHeightsMeasured = (
- listDataWithHeights: $ReadOnlyArray<CalendarItemWithHeight>,
- ) => {
- this.setState({ listDataWithHeights });
- };
-
- onViewableItemsChanged = (info: ViewableItemsChange) => {
- const ldwh = this.state.listDataWithHeights;
- if (!ldwh) {
- // This indicates the listData was cleared (set to null) right before this
- // callback was called. Since this leads to the FlatList getting cleared,
- // we'll just ignore this callback.
- return;
- }
-
- const visibleEntries: { [string]: boolean } = {};
- for (const token of info.viewableItems) {
- if (token.item.itemType === 'entryInfo') {
- visibleEntries[entryKey(token.item.entryInfo)] = true;
- }
- }
- this.latestExtraData = {
- activeEntries: _pickBy((_, key: string) => {
- if (visibleEntries[key]) {
- return true;
- }
- // We don't automatically set scrolled-away entries to be inactive
- // because entries can be out-of-view at creation time if they need to
- // be scrolled into view (see onEnterEntryEditMode). If Entry could
- // distinguish the reasons its active prop gets set to false, it could
- // differentiate the out-of-view case from the something-pressed case,
- // and then we could set scrolled-away entries to be inactive without
- // worrying about this edge case. Until then...
- const foundItem = _find(
- item => item.entryInfo && entryKey(item.entryInfo) === key,
- )(ldwh);
- return !!foundItem;
- })(this.latestExtraData.activeEntries),
- visibleEntries,
- };
-
- const topLoader = _find({ key: 'TopLoader' })(info.viewableItems);
- if (this.topLoaderWaitingToLeaveView && !topLoader) {
- this.topLoaderWaitingToLeaveView = false;
- this.topLoadingFromScroll = null;
- }
-
- const bottomLoader = _find({ key: 'BottomLoader' })(info.viewableItems);
- if (this.bottomLoaderWaitingToLeaveView && !bottomLoader) {
- this.bottomLoaderWaitingToLeaveView = false;
- this.bottomLoadingFromScroll = null;
- }
-
- if (
- !this.state.readyToShowList &&
- !this.topLoaderWaitingToLeaveView &&
- !this.bottomLoaderWaitingToLeaveView &&
- info.viewableItems.length > 0
- ) {
- this.setState({
- readyToShowList: true,
- extraData: this.latestExtraData,
- });
- }
-
- if (
- topLoader &&
- !this.topLoaderWaitingToLeaveView &&
- !this.topLoadingFromScroll
- ) {
- this.topLoaderWaitingToLeaveView = true;
- const start = dateFromString(this.props.startDate);
- start.setDate(start.getDate() - 31);
- const startDate = dateString(start);
- const endDate = this.props.endDate;
- this.topLoadingFromScroll = {
- startDate,
- endDate,
- filters: this.props.calendarFilters,
- };
- this.loadMoreAbove();
- } else if (
- bottomLoader &&
- !this.bottomLoaderWaitingToLeaveView &&
- !this.bottomLoadingFromScroll
- ) {
- this.bottomLoaderWaitingToLeaveView = true;
- const end = dateFromString(this.props.endDate);
- end.setDate(end.getDate() + 31);
- const endDate = dateString(end);
- const startDate = this.props.startDate;
- this.bottomLoadingFromScroll = {
- startDate,
- endDate,
- filters: this.props.calendarFilters,
- };
- this.loadMoreBelow();
- }
- };
-
- dispatchCalendarQueryUpdate(calendarQuery: CalendarQuery) {
- void this.props.dispatchActionPromise(
- updateCalendarQueryActionTypes,
- this.props.updateCalendarQuery({ calendarQuery }),
- );
- }
-
- loadMoreAbove: () => void = _throttle(() => {
- if (
- this.topLoadingFromScroll &&
- this.topLoaderWaitingToLeaveView &&
- this.props.connected
- ) {
- this.dispatchCalendarQueryUpdate(this.topLoadingFromScroll);
- }
- }, 1000);
-
- loadMoreBelow: () => void = _throttle(() => {
- if (
- this.bottomLoadingFromScroll &&
- this.bottomLoaderWaitingToLeaveView &&
- this.props.connected
- ) {
- this.dispatchCalendarQueryUpdate(this.bottomLoadingFromScroll);
- }
- }, 1000);
-
- onScroll = (event: ScrollEvent) => {
- this.currentScrollPosition = event.nativeEvent.contentOffset.y;
- };
-
- // When the user "flicks" the scroll view, this callback gets triggered after
- // the scrolling ends
- onMomentumScrollEnd = () => {
- this.setState({ extraData: this.latestExtraData });
- };
-
- // This callback gets triggered when the user lets go of scrolling the scroll
- // view, regardless of whether it was a "flick" or a pan
- onScrollEndDrag = () => {
- // We need to figure out if this was a flick or not. If it's a flick, we'll
- // let onMomentumScrollEnd handle it once scroll position stabilizes
- const currentScrollPosition = this.currentScrollPosition;
- setTimeout(() => {
- if (this.currentScrollPosition === currentScrollPosition) {
- this.setState({ extraData: this.latestExtraData });
- }
- }, 50);
- };
-
- onSaveEntry = () => {
- const entryKeys = Object.keys(this.latestExtraData.activeEntries);
- if (entryKeys.length === 0) {
- return;
- }
- const entryRef = this.entryRefs.get(entryKeys[0]);
- if (entryRef) {
- entryRef.completeEdit();
- }
- };
+ </Calendar.Navigator>
+ </KeyboardAvoidingView>
+ </View>
+ );
}
-const loadingStatusSelector = createLoadingStatusSelector(
- updateCalendarQueryActionTypes,
-);
-const activeTabSelector = createActiveTabSelector(CalendarRouteName);
-const activeThreadPickerSelector = createIsForegroundSelector(
- ThreadPickerModalRouteName,
-);
-
-const ConnectedCalendar: React.ComponentType<BaseProps> = React.memo<BaseProps>(
- function ConnectedCalendar(props: BaseProps) {
- const navContext = React.useContext(NavContext);
- const calendarActive =
- activeTabSelector(navContext) || activeThreadPickerSelector(navContext);
-
- const listData = useSelector(calendarListData);
- const startDate = useSelector(state => state.navInfo.startDate);
- const endDate = useSelector(state => state.navInfo.endDate);
- const calendarFilters = useSelector(state => state.calendarFilters);
- const dimensions = useSelector(derivedDimensionsInfoSelector);
- const loadingStatus = useSelector(loadingStatusSelector);
- const connected = useSelector(state => state.connectivity.connected);
- const colors = useColors();
- const styles = useStyles(unboundStyles);
- const indicatorStyle = useIndicatorStyle();
-
- const dispatchActionPromise = useDispatchActionPromise();
- const callUpdateCalendarQuery = useUpdateCalendarQuery();
-
- return (
- <Calendar
- {...props}
- calendarActive={calendarActive}
- listData={listData}
- startDate={startDate}
- endDate={endDate}
- calendarFilters={calendarFilters}
- dimensions={dimensions}
- loadingStatus={loadingStatus}
- connected={connected}
- colors={colors}
- styles={styles}
- indicatorStyle={indicatorStyle}
- dispatchActionPromise={dispatchActionPromise}
- updateCalendarQuery={callUpdateCalendarQuery}
- />
- );
+const unboundStyles = {
+ keyboardAvoidingView: {
+ flex: 1,
},
-);
+ view: {
+ flex: 1,
+ backgroundColor: 'panelBackground',
+ },
+};
-export default ConnectedCalendar;
+export default CalendarComponent;
diff --git a/native/calendar/entry.react.js b/native/calendar/entry.react.js
--- a/native/calendar/entry.react.js
+++ b/native/calendar/entry.react.js
@@ -59,7 +59,8 @@
import { useDispatch } from 'lib/utils/redux-utils.js';
import sleep from 'lib/utils/sleep.js';
-import type { EntryInfoWithHeight } from './calendar.react.js';
+import type { EntryInfoWithHeight } from './calendar-screen.react.js';
+import type { CalendarNavigationProp } from './calendar.react.js';
import LoadingIndicator from './loading-indicator.react.js';
import {
type MessageListParams,
@@ -76,7 +77,6 @@
} from '../navigation/nav-selectors.js';
import { NavContext } from '../navigation/navigation-context.js';
import { ThreadPickerModalRouteName } from '../navigation/route-names.js';
-import type { TabNavigationProp } from '../navigation/tab-navigator.react.js';
import { useSelector } from '../redux/redux-utils.js';
import { colors, useStyles } from '../themes/colors.js';
import type { LayoutEvent } from '../types/react-native.js';
@@ -178,7 +178,7 @@
};
type SharedProps = {
- +navigation: TabNavigationProp<'Calendar'>,
+ +navigation: CalendarNavigationProp<'CalendarScreen'>,
+entryInfo: EntryInfoWithHeight,
+visible: boolean,
+active: boolean,
diff --git a/native/navigation/community-drawer-button.react.js b/native/navigation/community-drawer-button.react.js
--- a/native/navigation/community-drawer-button.react.js
+++ b/native/navigation/community-drawer-button.react.js
@@ -12,6 +12,7 @@
+navigation:
| TabNavigationProp<'Chat'>
| TabNavigationProp<'Profile'>
+ | TabNavigationProp<'Calendar'>
| CommunityDrawerNavigationProp<'TabNavigator'>,
};
function CommunityDrawerButton(props: Props): React.Node {
diff --git a/native/navigation/route-names.js b/native/navigation/route-names.js
--- a/native/navigation/route-names.js
+++ b/native/navigation/route-names.js
@@ -65,6 +65,7 @@
export const BlockListRouteName = 'BlockList';
export const BuildInfoRouteName = 'BuildInfo';
export const CalendarRouteName = 'Calendar';
+export const CalendarScreenRouteName = 'CalendarScreen';
export const ChangeRolesScreenRouteName = 'ChangeRolesScreen';
export const ChatCameraModalRouteName = 'ChatCameraModal';
export const ChatRouteName = 'Chat';
@@ -268,6 +269,10 @@
+FarcasterAccountSettings: void,
};
+export type CalendarParamList = {
+ +CalendarScreen: void,
+};
+
export type CommunityDrawerParamList = { +TabNavigator: void };
export type RegistrationParamList = {
@@ -323,6 +328,7 @@
...ChatParamList,
...ChatTopTabsParamList,
...ProfileParamList,
+ ...CalendarParamList,
...CommunityDrawerParamList,
...RegistrationParamList,
...InviteLinkParamList,
diff --git a/native/navigation/tab-navigator.react.js b/native/navigation/tab-navigator.react.js
--- a/native/navigation/tab-navigator.react.js
+++ b/native/navigation/tab-navigator.react.js
@@ -150,13 +150,25 @@
const chatBadge = useSelector(unreadCount);
const isCalendarEnabled = useSelector(state => state.enabledApps.calendar);
+ let calendarTab;
+ if (isCalendarEnabled) {
+ calendarTab = (
+ <Tab.Screen
+ name={CalendarRouteName}
+ component={Calendar}
+ options={calendarTabOptions}
+ />
+ );
+ }
+
const headerLeft = React.useCallback(
() => <CommunityDrawerButton navigation={props.navigation} />,
[props.navigation],
);
- const headerCommonOptions = React.useMemo(
+ const appsOptions = React.useMemo(
() => ({
+ ...appsTabOptions,
headerShown: true,
headerLeft,
headerStyle: {
@@ -167,27 +179,6 @@
[colors.tabBarBackground, headerLeft],
);
- const calendarOptions = React.useMemo(
- () => ({ ...calendarTabOptions, ...headerCommonOptions }),
- [headerCommonOptions],
- );
-
- let calendarTab;
- if (isCalendarEnabled) {
- calendarTab = (
- <Tab.Screen
- name={CalendarRouteName}
- component={Calendar}
- options={calendarOptions}
- />
- );
- }
-
- const appsOptions = React.useMemo(
- () => ({ ...appsTabOptions, ...headerCommonOptions }),
- [headerCommonOptions],
- );
-
const tabBarScreenOptions = React.useMemo(
() => ({
headerShown: false,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Oct 6, 6:01 PM (21 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2250449
Default Alt Text
D12256.id40937.diff (53 KB)
Attached To
Mode
D12256: [native] Fix CalendarInputBar
Attached
Detach File
Event Timeline
Log In to Comment