-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
35380 virtual viewport on report screen #38755
Changes from 2 commits
b692714
750d321
4df3eb8
52e6585
14232fe
9215e2d
967184b
8114339
5f3f51c
1f8265c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/** | ||
* Native doesn't support DOM so default value is 0 | ||
*/ | ||
|
||
export default function useViewportOffsetTop(): number { | ||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,51 @@ | ||||||||||
import {useEffect, useRef, useState} from 'react'; | ||||||||||
import addViewportResizeListener from '@libs/VisualViewport'; | ||||||||||
|
||||||||||
/** | ||||||||||
* A hook that returns the offset of the top edge of the visual viewport | ||||||||||
*/ | ||||||||||
export default function useViewportOffsetTop(shouldAdjustScrollView = false): number { | ||||||||||
const [viewportOffsetTop, setViewportOffsetTop] = useState(0); | ||||||||||
const initialHeight = useRef(window.visualViewport?.height ?? window.innerHeight).current; | ||||||||||
const cachedDefaultOffsetTop = useRef<number>(0); | ||||||||||
useEffect(() => { | ||||||||||
const updateOffsetTop = (event?: Event) => { | ||||||||||
let targetOffsetTop = window.visualViewport?.offsetTop || 0; | ||||||||||
if (event?.target instanceof VisualViewport) { | ||||||||||
targetOffsetTop = event.target.offsetTop; | ||||||||||
} | ||||||||||
|
||||||||||
if (shouldAdjustScrollView && window.visualViewport) { | ||||||||||
const adjustScrollY = Math.round(initialHeight - window.visualViewport.height); | ||||||||||
if (cachedDefaultOffsetTop.current === 0) { | ||||||||||
cachedDefaultOffsetTop.current = targetOffsetTop; | ||||||||||
} | ||||||||||
|
||||||||||
if (adjustScrollY > targetOffsetTop) { | ||||||||||
setViewportOffsetTop(adjustScrollY); | ||||||||||
} else if (targetOffsetTop !== 0 && adjustScrollY === targetOffsetTop) { | ||||||||||
setViewportOffsetTop(cachedDefaultOffsetTop.current); | ||||||||||
} else { | ||||||||||
setViewportOffsetTop(targetOffsetTop); | ||||||||||
} | ||||||||||
} else { | ||||||||||
setViewportOffsetTop(targetOffsetTop); | ||||||||||
} | ||||||||||
}; | ||||||||||
updateOffsetTop(); | ||||||||||
const removeViewportResizeListener = addViewportResizeListener(updateOffsetTop); | ||||||||||
|
||||||||||
return () => { | ||||||||||
removeViewportResizeListener(); | ||||||||||
}; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is not needed right?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes I have cleanup this function |
||||||||||
}, [initialHeight, shouldAdjustScrollView]); | ||||||||||
|
||||||||||
useEffect(() => { | ||||||||||
if (!shouldAdjustScrollView) { | ||||||||||
return; | ||||||||||
} | ||||||||||
window.scrollTo({top: viewportOffsetTop}); | ||||||||||
}, [shouldAdjustScrollView, viewportOffsetTop]); | ||||||||||
|
||||||||||
return viewportOffsetTop; | ||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,6 +60,7 @@ | |
onModalClose(); | ||
onModalClose = null; | ||
isNavigate = undefined; | ||
setModalVisibility(false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please make sure that this change doesn't cause any regressions. Actually, which bug does this change fix? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll check condition only There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @situchan i have updated the condition to avoid regression and we don't need to change |
||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,14 +21,14 @@ | |
import TaskHeaderActionButton from '@components/TaskHeaderActionButton'; | ||
import withCurrentReportID from '@components/withCurrentReportID'; | ||
import type {CurrentReportIDContextValue} from '@components/withCurrentReportID'; | ||
import withViewportOffsetTop from '@components/withViewportOffsetTop'; | ||
import type {ViewportOffsetTopProps} from '@components/withViewportOffsetTop'; | ||
import useAppFocusEvent from '@hooks/useAppFocusEvent'; | ||
import useLocalize from '@hooks/useLocalize'; | ||
import usePrevious from '@hooks/usePrevious'; | ||
import useThemeStyles from '@hooks/useThemeStyles'; | ||
import useViewportOffsetTop from '@hooks/useViewportOffsetTop'; | ||
import useWindowDimensions from '@hooks/useWindowDimensions'; | ||
import Timing from '@libs/actions/Timing'; | ||
import * as Browser from '@libs/Browser'; | ||
import Navigation from '@libs/Navigation/Navigation'; | ||
import clearReportNotifications from '@libs/Notification/clearReportNotifications'; | ||
import reportWithoutHasDraftSelector from '@libs/OnyxSelectors/reportWithoutHasDraftSelector'; | ||
|
@@ -46,7 +46,8 @@ | |
import ONYXKEYS from '@src/ONYXKEYS'; | ||
import ROUTES from '@src/ROUTES'; | ||
import type SCREENS from '@src/SCREENS'; | ||
import type * as OnyxTypes from '@src/types/onyx'; | ||
import type {Modal} from '@src/types/onyx'; | ||
import {isEmptyObject} from '@src/types/utils/EmptyObject'; | ||
import HeaderView from './HeaderView'; | ||
import ReportActionsView from './report/ReportActionsView'; | ||
|
@@ -55,6 +56,8 @@ | |
import type {ActionListContextType, ReactionListRef, ScrollPosition} from './ReportScreenContext'; | ||
|
||
type ReportScreenOnyxProps = { | ||
/** Indicates if there is a modal currently visible or not */ | ||
modal: OnyxEntry<Modal>; | ||
/** Tells us if the sidebar has rendered */ | ||
isSidebarLoaded: OnyxEntry<boolean>; | ||
|
||
|
@@ -93,7 +96,7 @@ | |
|
||
type ReportScreenNavigationProps = StackScreenProps<CentralPaneNavigatorParamList, typeof SCREENS.REPORT>; | ||
|
||
type ReportScreenProps = OnyxHOCProps & ViewportOffsetTopProps & CurrentReportIDContextValue & ReportScreenOnyxProps & ReportScreenNavigationProps; | ||
type ReportScreenProps = OnyxHOCProps & CurrentReportIDContextValue & ReportScreenOnyxProps & ReportScreenNavigationProps; | ||
|
||
/** Get the currently viewed report ID as number */ | ||
function getReportID(route: ReportScreenNavigationProps['route']): string { | ||
|
@@ -131,7 +134,7 @@ | |
markReadyForHydration, | ||
policies = {}, | ||
isSidebarLoaded = false, | ||
viewportOffsetTop, | ||
modal = {isVisible: false}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. default value here is redundant |
||
isComposerFullSize = false, | ||
userLeavingStatus = false, | ||
currentReportID = '', | ||
|
@@ -257,6 +260,8 @@ | |
Timing.start(CONST.TIMING.CHAT_RENDER); | ||
Performance.markStart(CONST.TIMING.CHAT_RENDER); | ||
} | ||
const [isComposerFocus, setIsComposerFocus] = useState(false); | ||
const viewportOffsetTop = useViewportOffsetTop(Browser.isMobileSafari() && isComposerFocus && !modal?.isVisible); | ||
|
||
const {reportPendingAction, reportErrors} = ReportUtils.getReportOfflinePendingActionAndErrors(report); | ||
const screenWrapperStyle: ViewStyle[] = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}]; | ||
|
@@ -638,6 +643,8 @@ | |
|
||
{isReportReadyForDisplay ? ( | ||
<ReportFooter | ||
onComposerFocus={() => setIsComposerFocus(true)} | ||
onComposerBlur={() => setIsComposerFocus(false)} | ||
report={report} | ||
pendingAction={reportPendingAction} | ||
isComposerFullSize={!!isComposerFullSize} | ||
|
@@ -657,81 +664,82 @@ | |
|
||
ReportScreen.displayName = 'ReportScreen'; | ||
|
||
export default withViewportOffsetTop( | ||
withCurrentReportID( | ||
withOnyx<ReportScreenProps, ReportScreenOnyxProps>( | ||
{ | ||
isSidebarLoaded: { | ||
key: ONYXKEYS.IS_SIDEBAR_LOADED, | ||
}, | ||
sortedAllReportActions: { | ||
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getReportID(route)}`, | ||
canEvict: false, | ||
selector: (allReportActions: OnyxEntry<OnyxTypes.ReportActions>) => ReportActionsUtils.getSortedReportActionsForDisplay(allReportActions, true), | ||
}, | ||
report: { | ||
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${getReportID(route)}`, | ||
allowStaleData: true, | ||
selector: reportWithoutHasDraftSelector, | ||
}, | ||
reportMetadata: { | ||
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_METADATA}${getReportID(route)}`, | ||
initialValue: { | ||
isLoadingInitialReportActions: true, | ||
isLoadingOlderReportActions: false, | ||
isLoadingNewerReportActions: false, | ||
}, | ||
}, | ||
isComposerFullSize: { | ||
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${getReportID(route)}`, | ||
initialValue: false, | ||
}, | ||
betas: { | ||
key: ONYXKEYS.BETAS, | ||
}, | ||
policies: { | ||
key: ONYXKEYS.COLLECTION.POLICY, | ||
allowStaleData: true, | ||
}, | ||
accountManagerReportID: { | ||
key: ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, | ||
initialValue: null, | ||
}, | ||
userLeavingStatus: { | ||
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${getReportID(route)}`, | ||
initialValue: false, | ||
export default withCurrentReportID( | ||
withOnyx<ReportScreenProps, ReportScreenOnyxProps>( | ||
{ | ||
modal: { | ||
key: ONYXKEYS.MODAL, | ||
}, | ||
isSidebarLoaded: { | ||
key: ONYXKEYS.IS_SIDEBAR_LOADED, | ||
}, | ||
sortedAllReportActions: { | ||
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getReportID(route)}`, | ||
canEvict: false, | ||
selector: (allReportActions: OnyxEntry<OnyxTypes.ReportActions>) => ReportActionsUtils.getSortedReportActionsForDisplay(allReportActions, true), | ||
}, | ||
report: { | ||
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${getReportID(route)}`, | ||
allowStaleData: true, | ||
selector: reportWithoutHasDraftSelector, | ||
}, | ||
reportMetadata: { | ||
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_METADATA}${getReportID(route)}`, | ||
initialValue: { | ||
isLoadingInitialReportActions: true, | ||
isLoadingOlderReportActions: false, | ||
isLoadingNewerReportActions: false, | ||
}, | ||
parentReportAction: { | ||
key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : 0}`, | ||
selector: (parentReportActions: OnyxEntry<OnyxTypes.ReportActions>, props: WithOnyxInstanceState<ReportScreenOnyxProps>): OnyxEntry<OnyxTypes.ReportAction> => { | ||
const parentReportActionID = props?.report?.parentReportActionID; | ||
if (!parentReportActionID) { | ||
return null; | ||
} | ||
return parentReportActions?.[parentReportActionID] ?? null; | ||
}, | ||
canEvict: false, | ||
}, | ||
isComposerFullSize: { | ||
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${getReportID(route)}`, | ||
initialValue: false, | ||
}, | ||
betas: { | ||
key: ONYXKEYS.BETAS, | ||
}, | ||
policies: { | ||
key: ONYXKEYS.COLLECTION.POLICY, | ||
allowStaleData: true, | ||
}, | ||
accountManagerReportID: { | ||
key: ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, | ||
initialValue: null, | ||
}, | ||
userLeavingStatus: { | ||
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${getReportID(route)}`, | ||
initialValue: false, | ||
}, | ||
parentReportAction: { | ||
key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : 0}`, | ||
selector: (parentReportActions: OnyxEntry<OnyxTypes.ReportActions>, props: WithOnyxInstanceState<ReportScreenOnyxProps>): OnyxEntry<OnyxTypes.ReportAction> => { | ||
const parentReportActionID = props?.report?.parentReportActionID; | ||
if (!parentReportActionID) { | ||
return null; | ||
} | ||
return parentReportActions?.[parentReportActionID] ?? null; | ||
}, | ||
canEvict: false, | ||
}, | ||
true, | ||
)( | ||
memo( | ||
ReportScreen, | ||
(prevProps, nextProps) => | ||
prevProps.isSidebarLoaded === nextProps.isSidebarLoaded && | ||
lodashIsEqual(prevProps.sortedAllReportActions, nextProps.sortedAllReportActions) && | ||
lodashIsEqual(prevProps.reportMetadata, nextProps.reportMetadata) && | ||
prevProps.isComposerFullSize === nextProps.isComposerFullSize && | ||
lodashIsEqual(prevProps.betas, nextProps.betas) && | ||
lodashIsEqual(prevProps.policies, nextProps.policies) && | ||
prevProps.accountManagerReportID === nextProps.accountManagerReportID && | ||
prevProps.userLeavingStatus === nextProps.userLeavingStatus && | ||
prevProps.currentReportID === nextProps.currentReportID && | ||
prevProps.viewportOffsetTop === nextProps.viewportOffsetTop && | ||
lodashIsEqual(prevProps.parentReportAction, nextProps.parentReportAction) && | ||
lodashIsEqual(prevProps.route, nextProps.route) && | ||
lodashIsEqual(prevProps.report, nextProps.report), | ||
), | ||
}, | ||
true, | ||
)( | ||
memo( | ||
ReportScreen, | ||
(prevProps, nextProps) => | ||
prevProps.isSidebarLoaded === nextProps.isSidebarLoaded && | ||
lodashIsEqual(prevProps.sortedAllReportActions, nextProps.sortedAllReportActions) && | ||
lodashIsEqual(prevProps.reportMetadata, nextProps.reportMetadata) && | ||
prevProps.isComposerFullSize === nextProps.isComposerFullSize && | ||
lodashIsEqual(prevProps.betas, nextProps.betas) && | ||
lodashIsEqual(prevProps.policies, nextProps.policies) && | ||
prevProps.accountManagerReportID === nextProps.accountManagerReportID && | ||
prevProps.userLeavingStatus === nextProps.userLeavingStatus && | ||
prevProps.currentReportID === nextProps.currentReportID && | ||
lodashIsEqual(prevProps.modal, nextProps.modal) && | ||
lodashIsEqual(prevProps.parentReportAction, nextProps.parentReportAction) && | ||
lodashIsEqual(prevProps.route, nextProps.route) && | ||
lodashIsEqual(prevProps.report, nextProps.report), | ||
), | ||
), | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also affects mChrome. Is it safe?
It would be good to add comment on platform specific changes like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it must be specific for mSafari, i have update and add comment