diff --git a/.eslintrc.json b/.eslintrc.json index c902596b..27adfa29 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -25,7 +25,13 @@ "tsconfigRootDir": ".", "project": ["./tsconfig.lint.json"] }, - "plugins": ["react", "react-native", "@typescript-eslint", "etc"], + "plugins": [ + "react", + "react-native", + "@typescript-eslint", + "etc", + "no-dimensions" + ], "ignorePatterns": [ "cypress/", "cypress.config.ts", @@ -40,6 +46,7 @@ } }, "rules": { + "no-dimensions/no-dimensions": "error", "indent": ["error", 4, { "SwitchCase": 1 }], "react-native/no-unused-styles": 2, "react-native/split-platform-components": 2, diff --git a/package-lock.json b/package-lock.json index 77e773d9..8e2f5ee2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@react-navigation/stack": "^6.3.20", "@reduxjs/toolkit": "^1.9.7", "@types/react-numeric-input": "^2.2.6", + "eslint-plugin-no-dimensions": "^1.0.1", "expo": "~49.0.18", "expo-av": "~13.4.1", "expo-font": "~11.4.0", @@ -16703,6 +16704,14 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/eslint-plugin-no-dimensions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-dimensions/-/eslint-plugin-no-dimensions-1.0.1.tgz", + "integrity": "sha512-Enquh9VHLIu6mgW9NogMMGT0L4S4X9cm8OOB/58I5A/YA5R5xD0wGPXI7Z/OK4SzzvaeGGjZ2nPsU3q6O2wl0A==", + "peerDependencies": { + "eslint": "^8.52.0" + } + }, "node_modules/eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", diff --git a/package.json b/package.json index 4e9dcdbe..05304146 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@react-navigation/stack": "^6.3.20", "@reduxjs/toolkit": "^1.9.7", "@types/react-numeric-input": "^2.2.6", + "eslint-plugin-no-dimensions": "^1.0.1", "expo": "~49.0.18", "expo-av": "~13.4.1", "expo-font": "~11.4.0", diff --git a/src/components/WTR/TechProviders.tsx b/src/components/WTR/TechProviders.tsx index eb571619..87a36064 100644 --- a/src/components/WTR/TechProviders.tsx +++ b/src/components/WTR/TechProviders.tsx @@ -24,6 +24,11 @@ export const TechProviders = ({ limit }: Props) => { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', + justifyContent: 'space-between', // Adjusted justify content + }, + providerCard: { + flexBasis: '30%', // Adjusted flex basis for the cards + marginBottom: 10, // Initial margin between elements }, }); @@ -34,7 +39,7 @@ export const TechProviders = ({ limit }: Props) => { return; } - //shuffles the array and then slices it to the limit + // shuffles the array and then slices it to the limit resultItems.sort(() => Math.random() - Math.random()); resultItems = resultItems.slice(0, limit); @@ -54,7 +59,7 @@ export const TechProviders = ({ limit }: Props) => { {displayItems.map((provider) => ( - + )} diff --git a/src/components/general/alerts/Notification.tsx b/src/components/general/alerts/Notification.tsx index 80fcc3fd..84b812d0 100644 --- a/src/components/general/alerts/Notification.tsx +++ b/src/components/general/alerts/Notification.tsx @@ -1,5 +1,11 @@ import React, { useEffect } from 'react'; -import { Animated, Text, StyleSheet, Dimensions, View } from 'react-native'; +import { + Animated, + Text, + StyleSheet, + View, + useWindowDimensions, +} from 'react-native'; import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'; import { @@ -30,6 +36,7 @@ export const Notification = ({ icon, }: NotificationType) => { const dispatch = useAppDispatch(); + const windowDimensions = useWindowDimensions(); const colors = useColorConfig(); const fonts = useFonts(); @@ -70,7 +77,7 @@ export const Notification = ({ return () => clearTimeout(timer); }, [dispatch, animateFade, animateScaleY, animateTranslateY, id]); - const defaultWidth = Dimensions.get('window').width; + const defaultWidth = windowDimensions.width; const alertWidth = width ? width : defaultWidth * 0.97; const defaultHeight = 60; const alertHeight = height ? height : defaultHeight; diff --git a/src/components/general/error/ErrorBoundary.tsx b/src/components/general/error/ErrorBoundary.tsx index d40bb44d..9f321a3b 100644 --- a/src/components/general/error/ErrorBoundary.tsx +++ b/src/components/general/error/ErrorBoundary.tsx @@ -1,65 +1,73 @@ import Constants from 'expo-constants'; import React from 'react'; -import { Dimensions, Pressable, StyleSheet, Text, View } from 'react-native'; +import { + Pressable, + StyleSheet, + Text, + useWindowDimensions, + View, +} from 'react-native'; import { isEnvSettingEnabled } from '../../../lib/utility/env/env'; import { EnvOptions } from '../../../lib/utility/env/env.values'; -const windowWidth = Dimensions.get('window').width; -const windowHeight = Dimensions.get('window').height; -const colors = { - white: 'white', - blue: 'blue', -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - paddingTop: Constants.statusBarHeight, - padding: 8, - textAlign: 'center', - backgroundColor: colors.white, - }, - title: { - fontSize: windowWidth * 0.08, - fontWeight: 'bold', - textAlign: 'center', - }, - textContainer: { - flexDirection: 'column', - alignItems: 'center', - padding: windowWidth * 0.1, - }, - text: { - marginVertical: windowWidth * 0.04, - fontSize: windowWidth * 0.05, - padding: 8, - }, - button: { - justifyContent: 'center', - alignItems: 'center', - width: windowWidth * 0.3, - height: windowHeight * 0.08, - backgroundColor: colors.blue, - borderRadius: 8, - transform: 'scale(1)', - }, - buttonText: { - fontSize: windowWidth * 0.05, - color: colors.white, - textAlign: 'center', - fontWeight: 'bold', - }, -}); - interface Props { error: Error; resetError: () => void; } const ErrorFallback: React.FC = ({ error, resetError }: Props) => { + const windowDimensions = useWindowDimensions(); + const windowWidth = windowDimensions.width; + const windowHeight = windowDimensions.height; + + const colors = { + white: 'white', + blue: 'blue', + }; + + const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + paddingTop: Constants.statusBarHeight, + padding: 8, + textAlign: 'center', + backgroundColor: colors.white, + }, + title: { + fontSize: windowWidth * 0.08, + fontWeight: 'bold', + textAlign: 'center', + }, + textContainer: { + flexDirection: 'column', + alignItems: 'center', + padding: windowWidth * 0.1, + }, + text: { + marginVertical: windowWidth * 0.04, + fontSize: windowWidth * 0.05, + padding: 8, + }, + button: { + justifyContent: 'center', + alignItems: 'center', + width: windowWidth * 0.3, + height: windowHeight * 0.08, + backgroundColor: colors.blue, + borderRadius: 8, + transform: 'scale(1)', + }, + buttonText: { + fontSize: windowWidth * 0.05, + color: colors.white, + textAlign: 'center', + fontWeight: 'bold', + }, + }); + return ( Something happened! diff --git a/src/components/general/input/SelectDropdown.tsx b/src/components/general/input/SelectDropdown.tsx index 0a1ee416..0d76906b 100644 --- a/src/components/general/input/SelectDropdown.tsx +++ b/src/components/general/input/SelectDropdown.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { StyleSheet, View, Text, Dimensions } from 'react-native'; +import { StyleSheet, View, Text, useWindowDimensions } from 'react-native'; import { Dropdown } from 'react-native-element-dropdown'; import { useColorConfig } from '../../../lib/constants/Colors'; @@ -22,8 +22,6 @@ export type Props = { testID?: string; }; -const screenWidth = Dimensions.get('window').width; - export function SelectDropdown({ data, onSelect, @@ -32,6 +30,8 @@ export function SelectDropdown({ width, testID, }: Props) { + const windowDimensions = useWindowDimensions(); + const screenWidth = windowDimensions.width; const colors = useColorConfig(); const fonts = useFonts(); const t = usePreparedTranslator(); diff --git a/src/components/general/views/WhScrollView.tsx b/src/components/general/views/WhScrollView.tsx index f83c3bff..ed8abe19 100644 --- a/src/components/general/views/WhScrollView.tsx +++ b/src/components/general/views/WhScrollView.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { Dimensions, ScrollView, StyleSheet } from 'react-native'; +import { ScrollView, StyleSheet, useWindowDimensions } from 'react-native'; import { useColorConfig } from '../../../lib/constants/Colors'; import { useAppDispatch } from '../../../lib/redux/Hooks'; @@ -14,6 +14,7 @@ const topThreshold = 50; export const WhScrollView = ({ children }: WhScrollViewProps) => { const storeDispatcher = useAppDispatch(); + const windowDimensions = useWindowDimensions(); const colors = useColorConfig(); const [scrollPosition, setScrollPosition] = useState(0); @@ -36,7 +37,7 @@ export const WhScrollView = ({ children }: WhScrollViewProps) => { }; }) => { const position = event.nativeEvent.contentOffset.y; - const screenHeight = Dimensions.get('window').height; + const screenHeight = windowDimensions.height; const maxScroll = event.nativeEvent.contentSize.height - screenHeight; //if content is smaller than screen plus some padding, always show nav bar diff --git a/src/components/navigation/Navbar.tsx b/src/components/navigation/Navbar.tsx index ee9f6c45..a48deb3c 100644 --- a/src/components/navigation/Navbar.tsx +++ b/src/components/navigation/Navbar.tsx @@ -1,5 +1,10 @@ import React, { useEffect, useState } from 'react'; -import { Animated, Dimensions, Pressable, StyleSheet } from 'react-native'; +import { + Animated, + Pressable, + StyleSheet, + useWindowDimensions, +} from 'react-native'; import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'; import { useColorConfig } from '../../lib/constants/Colors'; @@ -9,11 +14,10 @@ import { useAnimatedValueNav } from '../../lib/utility/animate'; import { useNavigation } from '../../lib/utility/navigation/useNavigation'; import { navigationBarLinks } from '../../routes/navigation'; -const screenWidth = Dimensions.get('window').width; - export const NavBar = () => { const navigation = useNavigation(); const colors = useColorConfig(); + const windowDimensions = useWindowDimensions(); const navigationState = useAppSelector((state) => state.navigation); const [showNavBar, setShowNavBar] = useState(true); @@ -30,7 +34,7 @@ export const NavBar = () => { height: 50, marginBottom: 10, position: 'absolute', - width: screenWidth - 40, + width: windowDimensions.width - 40, zIndex: 1, padding: 0, }, @@ -62,14 +66,24 @@ export const NavBar = () => { }, [navigationState]); const [opacity, animateOpacity] = useAnimatedValueNav(1); - const [bottom, animateBottom] = useAnimatedValueNav(screenWidth - 40); - const [width, animateWidth] = useAnimatedValueNav(screenWidth - 40); + const [bottom, animateBottom] = useAnimatedValueNav( + windowDimensions.width - 40, + ); + const [width, animateWidth] = useAnimatedValueNav( + windowDimensions.width - 40, + ); useEffect(() => { animateOpacity(showNavBar ? 1 : 0, 200); animateBottom(showNavBar ? 0 : -100, 200); - animateWidth(showNavBar ? screenWidth - 40 : 0, 200); - }, [showNavBar, animateOpacity, animateBottom, animateWidth]); + animateWidth(showNavBar ? windowDimensions.width - 40 : 0, 200); + }, [ + showNavBar, + animateOpacity, + animateBottom, + animateWidth, + windowDimensions.width, + ]); function isRouteActive(route: string) { return navigationState.selectedNavBarRoute === route; diff --git a/src/screens/Course/StageOverview.tsx b/src/screens/Course/StageOverview.tsx index 8b87c8bb..72a78686 100644 --- a/src/screens/Course/StageOverview.tsx +++ b/src/screens/Course/StageOverview.tsx @@ -1,10 +1,10 @@ import { useRoute } from '@react-navigation/native'; import React from 'react'; import { - Dimensions, ImageBackground, ScrollView, StyleSheet, + useWindowDimensions, View, } from 'react-native'; import FontAwesome5Icon from 'react-native-vector-icons/FontAwesome5'; @@ -39,6 +39,7 @@ export type StageItemProps = { export default function StageOverview() { const fonts = useFonts(); + const windowDimensions = useWindowDimensions(); const t = usePreparedTranslator(); const stateColors = useColorStateConfig(); const colors = useColorConfig(); @@ -50,7 +51,7 @@ export default function StageOverview() { const { data, isLoading, error } = useSingleCourse(courseId); const course = useMapSingleCourseToData(data); - const containerHeight = Dimensions.get('window').height * 0.54; + const containerHeight = windowDimensions.height * 0.54; const styles = StyleSheet.create({ container: { margin: -20,