1- import {
2- addMonths ,
3- addYears ,
4- endOfDay ,
5- endOfMonth ,
6- endOfYear ,
7- format ,
8- getYear ,
9- isSameDay ,
10- parseISO ,
11- setDate ,
12- setMonth ,
13- setYear ,
14- startOfDay ,
15- startOfMonth ,
16- startOfYear ,
17- subMonths ,
18- subYears ,
19- } from 'date-fns' ;
1+ import { addMonths , addYears , format , isSameDay , parseISO , setDate , setMonth , setYear , startOfDay , subMonths , subYears } from 'date-fns' ;
202import { Str } from 'expensify-common' ;
213import React , { useCallback , useEffect , useRef , useState } from 'react' ;
224import type { StyleProp , ViewStyle } from 'react-native' ;
@@ -99,8 +81,8 @@ function CalendarPicker({
9981 const initialHeight = ( calendarDaysMatrix ?. length || CONST . MAX_CALENDAR_PICKER_ROWS ) * CONST . CALENDAR_PICKER_DAY_HEIGHT ;
10082 const heightValue = useSharedValue ( initialHeight ) ;
10183
102- const minYear = getYear ( new Date ( minDate ) ) ;
103- const maxYear = getYear ( new Date ( maxDate ) ) ;
84+ const minYear = CONST . CALENDAR_PICKER . MIN_YEAR ;
85+ const maxYear = CONST . CALENDAR_PICKER . MAX_YEAR ;
10486
10587 const [ years , setYears ] = useState < CalendarPickerListItem [ ] > ( ( ) =>
10688 Array . from ( { length : maxYear - minYear + 1 } , ( v , i ) => i + minYear ) . map ( ( year ) => ( {
@@ -140,12 +122,20 @@ function CalendarPicker({
140122 onSelected ?.( format ( newCurrentDateView , CONST . DATE . FNS_FORMAT_STRING ) ) ;
141123 } ;
142124
125+ const isAtMinBoundary = currentYearView <= CONST . CALENDAR_PICKER . MIN_YEAR && currentMonthView === 0 ;
126+ const isAtMaxBoundary = currentYearView >= CONST . CALENDAR_PICKER . MAX_YEAR && currentMonthView === 11 ;
127+ const isAtMinYear = currentYearView <= CONST . CALENDAR_PICKER . MIN_YEAR ;
128+ const isAtMaxYear = currentYearView >= CONST . CALENDAR_PICKER . MAX_YEAR ;
129+
143130 /**
144131 * Handles the user pressing the previous month arrow of the calendar picker.
145132 */
146133 const moveToPrevMonth = ( ) => {
147134 setCurrentDateView ( ( prev ) => {
148135 const prevMonth = subMonths ( new Date ( prev ) , 1 ) ;
136+ if ( prevMonth . getFullYear ( ) < CONST . CALENDAR_PICKER . MIN_YEAR ) {
137+ return prev ;
138+ }
149139 // if year is subtracted, we need to update the years list
150140 if ( prevMonth . getFullYear ( ) < prev . getFullYear ( ) ) {
151141 setYears ( ( prevYears ) =>
@@ -165,6 +155,9 @@ function CalendarPicker({
165155 const moveToNextMonth = ( ) => {
166156 setCurrentDateView ( ( prev ) => {
167157 const nextMonth = addMonths ( new Date ( prev ) , 1 ) ;
158+ if ( nextMonth . getFullYear ( ) > CONST . CALENDAR_PICKER . MAX_YEAR ) {
159+ return prev ;
160+ }
168161 // if year is added, we need to update the years list
169162 if ( nextMonth . getFullYear ( ) > prev . getFullYear ( ) ) {
170163 setYears ( ( prevYears ) =>
@@ -181,9 +174,9 @@ function CalendarPicker({
181174
182175 const moveToPrevYear = ( ) => {
183176 setCurrentDateView ( ( prev ) => {
184- let prevYear = subYears ( new Date ( prev ) , 1 ) ;
185- if ( prevYear < new Date ( minDate ) ) {
186- prevYear = new Date ( minDate ) ;
177+ const prevYear = subYears ( new Date ( prev ) , 1 ) ;
178+ if ( prevYear . getFullYear ( ) < CONST . CALENDAR_PICKER . MIN_YEAR ) {
179+ return prev ;
187180 }
188181 setYears ( ( prevYears ) => prevYears . map ( ( item ) => ( { ...item , isSelected : item . value === prevYear . getFullYear ( ) } ) ) ) ;
189182 return prevYear ;
@@ -192,9 +185,9 @@ function CalendarPicker({
192185
193186 const moveToNextYear = ( ) => {
194187 setCurrentDateView ( ( prev ) => {
195- let nextYear = addYears ( new Date ( prev ) , 1 ) ;
196- if ( nextYear > new Date ( maxDate ) ) {
197- nextYear = new Date ( maxDate ) ;
188+ const nextYear = addYears ( new Date ( prev ) , 1 ) ;
189+ if ( nextYear . getFullYear ( ) > CONST . CALENDAR_PICKER . MAX_YEAR ) {
190+ return prev ;
198191 }
199192 setYears ( ( prevYears ) => prevYears . map ( ( item ) => ( { ...item , isSelected : item . value === nextYear . getFullYear ( ) } ) ) ) ;
200193 return nextYear ;
@@ -203,11 +196,6 @@ function CalendarPicker({
203196
204197 const monthNames = DateUtils . getMonthNames ( ) . map ( ( month ) => Str . UCFirst ( month ) ) ;
205198 const daysOfWeek = DateUtils . getDaysOfWeek ( ) . map ( ( day ) => day . toUpperCase ( ) ) ;
206- const hasAvailableDatesNextMonth = startOfDay ( new Date ( maxDate ) ) > endOfMonth ( new Date ( currentDateView ) ) ;
207- const hasAvailableDatesPrevMonth = endOfDay ( new Date ( minDate ) ) < startOfMonth ( new Date ( currentDateView ) ) ;
208- const hasAvailableDatesNextYear = startOfDay ( new Date ( maxDate ) ) > endOfYear ( new Date ( currentDateView ) ) ;
209- const hasAvailableDatesPrevYear = endOfDay ( new Date ( minDate ) ) < startOfYear ( new Date ( currentDateView ) ) ;
210-
211199 useEffect ( ( ) => {
212200 if ( isSmallScreenWidth || isFirstRender . current ) {
213201 isFirstRender . current = false ;
@@ -247,15 +235,15 @@ function CalendarPicker({
247235 < PressableWithFeedback
248236 shouldUseAutoHitSlop = { false }
249237 testID = "prev-month-arrow"
250- disabled = { ! hasAvailableDatesPrevMonth }
238+ disabled = { isAtMinBoundary }
251239 onPress = { moveToPrevMonth }
252240 hoverDimmingValue = { 1 }
253241 accessibilityLabel = { translate ( 'common.previousMonth' ) }
254242 role = { CONST . ROLE . BUTTON }
255243 sentryLabel = { CONST . SENTRY_LABEL . CALENDAR_PICKER . PREV_MONTH }
256244 >
257245 < ArrowIcon
258- disabled = { ! hasAvailableDatesPrevMonth }
246+ disabled = { isAtMinBoundary }
259247 direction = { CONST . DIRECTION . LEFT }
260248 />
261249 </ PressableWithFeedback >
@@ -286,29 +274,29 @@ function CalendarPicker({
286274 < PressableWithFeedback
287275 shouldUseAutoHitSlop = { false }
288276 testID = "next-month-arrow"
289- disabled = { ! hasAvailableDatesNextMonth }
277+ disabled = { isAtMaxBoundary }
290278 onPress = { moveToNextMonth }
291279 hoverDimmingValue = { 1 }
292280 accessibilityLabel = { translate ( 'common.nextMonth' ) }
293281 role = { CONST . ROLE . BUTTON }
294282 sentryLabel = { CONST . SENTRY_LABEL . CALENDAR_PICKER . NEXT_MONTH }
295283 >
296- < ArrowIcon disabled = { ! hasAvailableDatesNextMonth } />
284+ < ArrowIcon disabled = { isAtMaxBoundary } />
297285 </ PressableWithFeedback >
298286 </ View >
299287 < View style = { [ themeStyles . alignItemsCenter , themeStyles . flexRow , { flex : 2 } ] } >
300288 < PressableWithFeedback
301289 shouldUseAutoHitSlop = { false }
302290 testID = "prev-year-arrow"
303- disabled = { ! hasAvailableDatesPrevYear }
291+ disabled = { isAtMinYear }
304292 onPress = { moveToPrevYear }
305293 hoverDimmingValue = { 1 }
306294 accessibilityLabel = { translate ( 'common.previousYear' ) }
307295 role = { CONST . ROLE . BUTTON }
308296 sentryLabel = { CONST . SENTRY_LABEL . CALENDAR_PICKER . PREV_YEAR }
309297 >
310298 < ArrowIcon
311- disabled = { ! hasAvailableDatesPrevYear }
299+ disabled = { isAtMinYear }
312300 direction = { CONST . DIRECTION . LEFT }
313301 />
314302 </ PressableWithFeedback >
@@ -322,7 +310,6 @@ function CalendarPicker({
322310 style = { [ themeStyles . alignItemsCenter ] }
323311 wrapperStyle = { [ themeStyles . alignItemsCenter ] }
324312 hoverDimmingValue = { 1 }
325- disabled = { years . length <= 1 }
326313 testID = "currentYearButton"
327314 accessibilityLabel = { `${ currentYearView } , ${ translate ( 'common.currentYear' ) } ` }
328315 role = { CONST . ROLE . BUTTON }
@@ -339,14 +326,14 @@ function CalendarPicker({
339326 < PressableWithFeedback
340327 shouldUseAutoHitSlop = { false }
341328 testID = "next-year-arrow"
342- disabled = { ! hasAvailableDatesNextYear }
329+ disabled = { isAtMaxYear }
343330 onPress = { moveToNextYear }
344331 hoverDimmingValue = { 1 }
345332 accessibilityLabel = { translate ( 'common.nextYear' ) }
346333 role = { CONST . ROLE . BUTTON }
347334 sentryLabel = { CONST . SENTRY_LABEL . CALENDAR_PICKER . NEXT_YEAR }
348335 >
349- < ArrowIcon disabled = { ! hasAvailableDatesNextYear } />
336+ < ArrowIcon disabled = { isAtMaxYear } />
350337 </ PressableWithFeedback >
351338 </ View >
352339 </ View >
@@ -432,9 +419,6 @@ function CalendarPicker({
432419 < MonthPickerModal
433420 isVisible = { isMonthPickerVisible }
434421 currentMonth = { currentMonthView }
435- currentYear = { currentYearView }
436- minDate = { minDate }
437- maxDate = { maxDate }
438422 onMonthChange = { onMonthSelected }
439423 onClose = { ( ) => setIsMonthPickerVisible ( false ) }
440424 />
0 commit comments