App
App copied to clipboard
[HOLD for payment 2024-10-30] [$1000] Create a useResponsiveLayout hook
Slack context: https://expensify.slack.com/archives/C01GTK53T8Q/p1698364514493649
Problem
There are some times when we want to use the same layout on mobile devices or in the RHP on wide screens. In these cases, we can't rely on isSmallScreenWidth
from useWindowDimensions
to determine which layout to use, so we have hard-coded and/or different solutions we use to address the same problem.
Solution
-
In the navigation stack, add an
isInRHP
route prop to all screens in the RHP. -
Write a custom hook called
useResponsiveLayout
that does something like this:function useResponsiveLayout() { const {isSmallScreenWidth} = useWindowDimensions(); const {params} = useRoute(); return { shouldUseNarrowLayout = isSmallScreenWidth || params.isInRHP, }; }
-
Replace most layout-motivated uses of useWindowDimensions with useResponsiveLayout like so:
- const {isSmallScreenWidth} = useWindowDimensions(); + const {shouldUseNarrowLayout} = useResponsiveLayout();
-
Introduce a new lint rule that prohibits using
isSmallScreenWidth
fromuseWindowDimensions
, advising developers to useshouldUseNarrowLayout
instead.
Upwork Automation - Do Not Edit
- Upwork Job URL: https://www.upwork.com/jobs/~01f36dcb081cdf74b2
- Upwork Job ID: 1717949899365408768
- Last Price Increase: 2023-12-29
Issue Owner
Current Issue Owner: @alexpensify
Job added to Upwork: https://www.upwork.com/jobs/~01f36dcb081cdf74b2
Triggered auto assignment to @maddylewis (NewFeature
), see https://stackoverflowteams.com/c/expensify/questions/14418#:~:text=BugZero%20process%20steps%20for%20feature%20requests for more details.
Triggered auto assignment to Contributor-plus team member for initial proposal review - @getusha (External
)
I can work on this if no one is assigned yet, or should I write proposal
Since it is a new feature main idea is explained well in description, and I think anything else could be discussed in the PR
Proposal
Please re-state the problem that we are trying to solve in this issue.
Create a useResponsiveLayout hook
What is the root cause of that problem?
This is a new functionality
What changes do you think we should make in order to solve the problem?
First, we need open RHP modals navigator
And add new route option isInRHP
in screenOptions
Then we need to create a new hook For example, using the suggested option
And then we need to find isSmallScreenWidth
values
Remove those values and use shouldUseNarrowLayout
from the new hook
And at the last, we need to introduce a new lint rule that disallow using isSmallScreenWidth from useWindowDimensions
We can write a new rule
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Do not use isSmallScreenWidth from useWindowDimensions. Instead of that use shouldUseNarrowLayout from useResponsiveLayout',
category: 'Best Practices',
},
},
create: function (context) {
return {
VariableDeclarator(node) {
if ( node.init.callee && node.init.callee.name === 'useWindowDimensions') {
const properties = node.id.properties;
for (const property of properties) {
if (property.key.name === 'isSmallScreenWidth') {
context.report({
node,
message: 'Do not use isSmallScreenWidth from useWindowDimensions. Instead of that use shouldUseNarrowLayout from useResponsiveLayout',
});
}
}
}
},
};
},
};
What alternative solutions did you explore? (Optional)
NA
Proposal
Please re-state the problem that we are trying to solve in this issue.
The problem we aim to address is the lack of a consistent and reliable method to determine the appropriate layout for mobile devices and wide screens within our application. Currently, we rely on the isSmallScreenWidth
property from the useWindowDimensions
hook to make layout decisions. However, this approach falls short in situations where we need to maintain the same layout for both mobile devices and the Right-Hand Panel (RHP) on wide screens. As a result, we resort to hard-coded solutions or utilize different methods, which leads to code inconsistency and maintainability challenges.
What is the root cause of that problem?
The root cause of this problem lies in the absence of a dedicated mechanism to determine the layout based on specific conditions, such as whether a screen is within the RHP or on a small screen.
What changes do you think we should make in order to solve the problem?
- Introduce
isInRHP
:
https://github.com/Expensify/App/blob/7b836cfcee79084177191e3dfb4a927698b09eb4/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx#L60-L64
Include an isInRHP option with a default value (true). This initial param will allow screens to determine whether they are within the Right-Hand Panel (RHP) and make layout decisions accordingly.
<ModalStackNavigator.Screen
key={name}
name={name}
getComponent={(screens as Required<Screens>)[name as Screen]}
initialParams={{isInRHP: true} as TStackParams[string]} //here
/>
- Create
useResponsiveLayout
Custom Hook: Develop a custom hook calleduseResponsiveLayout
that provides a unified way to determine the appropriate layout. This hook will take advantage ofuseWindowDimensions
and theisInRHP
route prop to decide whether a narrow layout should be used. The hook's implementation is as follows:
export default function useResponsiveLayout(): ResponsiveLayoutResult {
const {isSmallScreenWidth} = useWindowDimensions();
try {
const {params} = useRoute<RouteProp<RouteParams, 'params'>>();
return {shouldUseNarrowLayout: isSmallScreenWidth || (params?.isInRHP ?? false)};
} catch (error) {
return {
shouldUseNarrowLayout: isSmallScreenWidth,
};
}
}
- Replace
useWindowDimensions
withuseResponsiveLayout
: Update the codebase to replace most instances whereuseWindowDimensions
is used for layout decisions withuseResponsiveLayout
as follows:
Before:
const { isSmallScreenWidth } = useWindowDimensions();
After:
const { shouldUseNarrowLayout } = useResponsiveLayout();
- Introduce a Lint Rule: Implement a new linting rule in https://github.com/Expensify/eslint-config-expensify/tree/main/eslint-plugin-expensify that prohibits the direct use of
isSmallScreenWidth
fromuseWindowDimensions
for layout determination. This rule will encourage developers to use 'shouldUseNarrowLayout' fromuseResponsiveLayout
instead, ensuring consistency in layout decisions throughout the codebase.
const _ = require('underscore');
const message = require('./CONST').MESSAGE.PREFER_USE_RESPONSIVE_FOR_LAYOUT;
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Prohibit the direct use of isSmallScreenWidth from useWindowDimensions',
},
},
create(context) {
return {
VariableDeclarator(node) {
if (!node.init || !node.init.callee || node.init.callee.name !== 'useWindowDimensions') {
return;
}
// Check for 'const {isSmallScreenWidth, ...} = useWindowDimensions();' pattern
if (node.id.type === 'ObjectPattern') {
node.id.properties.forEach((property) => {
if (!property.key || property.key.name !== 'isSmallScreenWidth') {
return;
}
context.report({
node: property,
message,
});
});
}
// Check for 'const var = useWindowDimensions();' and use of this var
const variableName = node.id.name;
const variableUsages = _.filter(context.getScope().references, reference => reference.identifier.name === variableName);
variableUsages.forEach((usage) => {
const parent = usage.identifier.parent;
// Check for 'const isSmallScreen = var.isSmallScreenWidth;' pattern
if (parent.type === 'MemberExpression' && parent.property.name === 'isSmallScreenWidth') {
context.report({
node: parent.property,
message,
});
}
// Check for 'const {isSmallScreenWidth} = var;' pattern
if (parent.type === 'VariableDeclarator' && parent.id.type === 'ObjectPattern') {
parent.id.properties.forEach((property) => {
if (!property.key || property.key.name !== 'isSmallScreenWidth') {
return;
}
context.report({
node: property,
message,
});
});
}
});
},
MemberExpression(node) {
// Check for 'const isSmallScreen = useWindowDimensions().isSmallScreenWidth;' pattern
if (node.object.type !== 'CallExpression' || node.object.callee.name !== 'useWindowDimensions' || node.property.name !== 'isSmallScreenWidth') {
return;
}
context.report({
node,
message,
});
},
};
},
};
we should also add a test for this rule.
What alternative solutions did you explore? (Optional)
N/A
Problem: In the current application, there is a challenge when needing to use the same layout for both mobile devices and the right-hand panel (RHP) on wide screens. This problem arises because it's not always feasible to rely on the isSmallScreenWidth variable from useWindowDimensions to determine which layout to use. As a result, developers often resort to hard-coded and inconsistent solutions to address this issue.
Solution: To address this problem, the following changes are proposed:
Route Prop for RHP Detection: In the navigation stack, it is suggested to add an isInRHP route prop to all screens within the RHP. This addition allows for easy identification of screens within the RHP.
Custom Hook - useResponsiveLayout: Create a custom hook called useResponsiveLayout to determine the appropriate layout based on the screen width and the isInRHP route prop.
function useResponsiveLayout() {
const { isSmallScreenWidth } = useWindowDimensions();
const { params } = useRoute();
return {
shouldUseNarrowLayout: isSmallScreenWidth || params.isInRHP,
};
}
Replace useWindowDimensions with useResponsiveLayout: To ensure a consistent approach to layout handling, replace most of the current uses of useWindowDimensions with useResponsiveLayout as shown below:
// Before
const { isSmallScreenWidth } = useWindowDimensions();
// After
const { shouldUseNarrowLayout } = useResponsiveLayout();
Introduce a Lint Rule: To maintain code quality and consistency, introduce a new lint rule that discourages the use of isSmallScreenWidth from useWindowDimensions. Developers are advised to use shouldUseNarrowLayout instead.
This proposed solution aims to provide a more unified and maintainable approach to handling responsive layouts in the application. These changes simplify the management of layouts across various screen sizes and ensure a more consistent user experience.
📣 @WebflowGuru! 📣 Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork. Please follow these steps:
- Make sure you've read and understood the contributing guidelines.
- Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
- Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
- Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>
Thanks for all the proposals everyone. At this time I've decided to move forward with the one that feels the clearest and most high-quality, which in my opinion is from @rayane-djouah here.
Thank you, I will raise a PR asap
@rayane-djouah any updates?
Updating now
can someone provide an update on the status of this issue? thanks!
PR still in review by C+
Since we're splitting this up into a few PRs, @rayane-djouah, can you lay out the related PRs in sequence please?
Based on the changes in this PR https://github.com/Expensify/App/pull/31476, we're splitting this up into several PRs as follows:
- A PR introducing
useResponsiveLayout
. https://github.com/Expensify/App/pull/33425 - ~~A PR to add the
isInRHP
prop. https://github.com/Expensify/App/pull/33426~~ (I am modifying the logic for theuseResponsiveLayout
hook in https://github.com/Expensify/App/pull/34743. Instead of passing a parameter to screens in the modal stack navigator and checking for it in the hook, I am now obtaining the navigator name directly from the navigationRef in the hook without altering the navigation logic.) - A PR to replace usages of
shouldShowSmallScreen
andisInModal
props in components. https://github.com/Expensify/App/pull/34837 - A PR to replace usages of
isSmallScreenWidth
fromuseWindowDimensions
incomponents
folder files. https://github.com/Expensify/App/pull/36292 - A PR to replace usages of
isSmallScreenWidth
fromuseWindowDimensions
inpages
folder files. https://github.com/Expensify/App/pull/43961 - ~~A PR to replace usages of
isSmallScreenWidth
fromuseWindowDimensions
inlibs
folder files. https://github.com/Expensify/App/pull/36294~~ - ~~A PR to migrate remaining uses of
isSmallScreenWidth
prop from thewithWindowDimensions
HOC in functional components. https://github.com/Expensify/App/pull/36295~~ - ~~A PR to rename
isSmallScreenWidth
parameter names in various functions (which doesn't have much effect). https://github.com/Expensify/App/pull/36296~~ - A PR to replace remmaing usages of
useWindowDimensions
withuseResponsiveLayout
, refactoruseWindowDimensions
to exportwindowWidth
andwindowHeight
only, and makeuseResponsiveLayout
responsible for any logic determining the correct layout. https://github.com/Expensify/App/pull/46788 - A PR to update
eslint-config-expensify
version to use the new rule after https://github.com/Expensify/eslint-config-expensify/pull/82 is merged.
Upwork job price has been updated to $1000
Doubling the bounty here due to large scope
Reviewing
label has been removed, please complete the "BugZero Checklist".
The solution for this issue has been :rocket: deployed to production :rocket: in version 1.4.21-4 and is now subject to a 7-day regression period :calendar:. Here is the list of pull requests that resolve this issue:
- https://github.com/Expensify/App/pull/33425
If no regressions arise, payment will be issued on 2024-01-11. :confetti_ball:
After the hold period is over and BZ checklist items are completed, please complete any of the applicable payments for this issue, and check them off once done.
- [ ] External issue reporter
- [ ] Contributor that fixed the issue
- [ ] Contributor+ that helped on the issue and/or PR
For reference, here are some details about the assignees on this issue:
- @getusha requires payment (Needs manual offer from BZ)
- @rayane-djouah requires payment (Needs manual offer from BZ)
BugZero Checklist: The PR adding this new feature has been merged! The following checklist (instructions) will need to be completed before the issue can be closed:
- [ ] [@getusha] Please propose regression test steps to ensure the new feature will work correctly on production in further releases.
- [ ] [] Link the GH issue for creating/updating the regression test once above steps have been agreed upon.
moving to daily since payment is coming up soon
@maddylewis since we are splitting the PR into multiple small PRs we should only process payment once the last PR gets merged. this is only the first one.
Agreed. Left some comments on the 2nd PR.
@rayane-djouah we're awaiting changes in the 2nd PR
sorry for the delay, I updated the PR now
Issue is ready for payment but no BZ is assigned. @alexpensify you are the lucky winner! Please verify the payment summary looks correct and complete the checklist. Thanks!
Payment Summary
- ROLE: @getusha paid $1000 via Upwork
- ROLE: @rayane-djouah paid $1000 via Upwork
BugZero Checklist (@alexpensify)
- [x] I have verified the correct assignees and roles are listed above and updated the neccesary manual offers
- [x] I have verified that there are no duplicate or incorrect contracts on Upwork for this job (https://www.upwork.com/ab/applicants/1717949899365408768/hired)
- [x] I have paid out the Upwork contracts or cancelled the ones that are incorrect
- [x] I have verified the payment summary above is correct
Based on this comment here, this one is not ready for payment and there is an upcoming PR that needs to go to production before I can move forward with the payment process.
For now, I updated the Upwork job link:
https://www.upwork.com/jobs/~0123a7c663aaab8163
@getusha and @rayane-djouah -- please apply and we can be ready for the payment date.
Heads up, I will be offline until Tuesday, but will catch up then to identify the status of the last PR. I will not be actively watching over this GitHub during that period. If anything urgent is needed here, please ask for help in the Open Source Slack Room-- thanks!
⚠️ Looks like this issue was linked to a Deploy Blocker here
If you are the assigned CME please investigate whether the linked PR caused a regression and leave a comment with the results.
If a regression has occurred and you are the assigned CM follow the instructions here.
If this regression could have been avoided please consider also proposing a recommendation to the PR checklist so that we can avoid it in the future.