From 66b72dcb7708abed32e0d9758f2809f561b6dfb4 Mon Sep 17 00:00:00 2001 From: Kichiyaki Date: Mon, 1 Nov 2021 17:17:38 +0100 Subject: [PATCH] refactor: use types for props, not interfaces + update enum values + remove unnecessary useCallbacks from List.tsx --- src/common/Menu/Menu.tsx | 19 +++---- src/config/analytics.ts | 10 ++-- src/config/routing.ts | 8 +-- src/libs/graphql/createClient.ts | 25 +++++---- src/libs/native-base/components/FooterTab.ts | 2 +- src/libs/native-base/components/Header.ts | 4 +- src/libs/native-base/components/ListItem.ts | 2 +- src/libs/native-base/components/TabHeading.ts | 4 +- src/libs/native-base/variables/types.ts | 2 +- src/libs/savedqualifications/Provider.tsx | 10 ++-- src/screens/App.tsx | 10 ++-- src/screens/HomeScreen/HomeScreen.tsx | 2 +- .../HomeScreen/components/Header/Header.tsx | 4 +- .../components/ModeSelector/ModeSelector.tsx | 16 +++--- .../components/Professions/List/List.tsx | 24 ++++----- .../components/Professions/List/ListItem.tsx | 5 +- .../Professions/NetworkConnectionAlert.tsx | 30 ++++++----- .../components/Professions/Professions.tsx | 54 +++++++++++-------- .../Professions/QualificationModal.tsx | 7 ++- src/screens/Navigation.tsx | 4 +- src/screens/TestScreen/TestScreen.tsx | 6 +-- .../TestScreen/components/Content/Content.tsx | 4 +- .../TestScreen/components/Header/Header.tsx | 4 +- .../components/Suggestions/Suggestions.tsx | 6 +-- .../TestScreen/components/Test/Alert.tsx | 4 +- .../TestScreen/components/Test/Image.tsx | 4 +- .../TestScreen/components/Test/Question.tsx | 4 +- .../TestScreen/components/Test/SummaryTab.tsx | 4 +- .../TestScreen/components/Test/Test.tsx | 10 ++-- 29 files changed, 149 insertions(+), 139 deletions(-) diff --git a/src/common/Menu/Menu.tsx b/src/common/Menu/Menu.tsx index 00459b1..52e8b0d 100644 --- a/src/common/Menu/Menu.tsx +++ b/src/common/Menu/Menu.tsx @@ -5,27 +5,28 @@ import buildURL from 'utils/buildURL'; import { Linking, StyleSheet } from 'react-native'; import { ActionSheet, Icon, NativeBase } from 'native-base'; -export interface MenuProps - extends Omit {} - const OPTIONS = ['Strona internetowa', 'Kontakt', 'Anuluj']; -const WEBSITE_OPT_INDEX = 0; -const CONTACT_OPT_INDEX = 1; -const CANCEL_OPT_INDEX = OPTIONS.length - 1; +enum OptionIndex { + WEBSITE, + CONTACT, + CANCEL, +} + +export type MenuProps = Omit; const Menu = ({ style, ...rest }: MenuProps) => { const showMenu = () => { ActionSheet.show( { options: OPTIONS, - cancelButtonIndex: CANCEL_OPT_INDEX, + cancelButtonIndex: OptionIndex.CANCEL, }, index => { switch (index) { - case WEBSITE_OPT_INDEX: + case OptionIndex.WEBSITE: Linking.openURL(WEBSITE); break; - case CONTACT_OPT_INDEX: + case OptionIndex.CONTACT: Linking.openURL(buildURL('email', EMAIL)); break; } diff --git a/src/config/analytics.ts b/src/config/analytics.ts index 635bbb9..6a6cfcc 100644 --- a/src/config/analytics.ts +++ b/src/config/analytics.ts @@ -1,7 +1,7 @@ export enum Event { - SaveQualification = 'save_qualification', - UnSaveQualification = 'unsave_qualification', - StartTest = 'start_test', - FinishTest = 'finish_test', - SelectAnswer = 'select_answer', + SAVE_QUALIFICATION = 'save_qualification', + UNSAVE_QUALIFICATION = 'unsave_qualification', + START_TEST = 'start_test', + FINISH_TEST = 'finish_test', + SELECT_ANSWER = 'select_answer', } diff --git a/src/config/routing.ts b/src/config/routing.ts index 226a04a..458f4ea 100644 --- a/src/config/routing.ts +++ b/src/config/routing.ts @@ -1,6 +1,6 @@ export enum Screen { - Home = 'HomeScreen', - Test = 'TestScreen', + HOME = 'HomeScreen', + TEST = 'TestScreen', } export type TestScreenParams = { @@ -9,6 +9,6 @@ export type TestScreenParams = { }; export type AppStackParamList = { - [Screen.Home]: undefined; - [Screen.Test]: TestScreenParams; + [Screen.HOME]: undefined; + [Screen.TEST]: TestScreenParams; }; diff --git a/src/libs/graphql/createClient.ts b/src/libs/graphql/createClient.ts index 868b807..9fd9914 100644 --- a/src/libs/graphql/createClient.ts +++ b/src/libs/graphql/createClient.ts @@ -15,17 +15,20 @@ export const createClient = ( cache: new InMemoryCache(), link: ApolloLink.from([ onError(({ graphQLErrors, networkError }) => { - if (__DEV__) { - if (graphQLErrors) { - graphQLErrors.forEach(({ message, locations, path }) => - console.log( - `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`, - ), - ); - } - if (networkError) { - console.log(`[Network error]: ${networkError}`); - } + if (!__DEV__) { + return; + } + + if (graphQLErrors) { + graphQLErrors.forEach(({ message, locations, path }) => + console.log( + `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`, + ), + ); + } + + if (networkError) { + console.log(`[Network error]: ${networkError}`); } }), new HttpLink({ diff --git a/src/libs/native-base/components/FooterTab.ts b/src/libs/native-base/components/FooterTab.ts index 08c9b82..cf66300 100644 --- a/src/libs/native-base/components/FooterTab.ts +++ b/src/libs/native-base/components/FooterTab.ts @@ -66,7 +66,7 @@ export default (variables /* : * */ = variable) => { }, }, backgroundColor: - Platform.OS === OS.Android ? variables.footerDefaultBg : undefined, + Platform.OS === OS.ANDROID ? variables.footerDefaultBg : undefined, flexDirection: 'row', justifyContent: 'space-between', flex: 1, diff --git a/src/libs/native-base/components/Header.ts b/src/libs/native-base/components/Header.ts index 7efb14f..6b4e59d 100644 --- a/src/libs/native-base/components/Header.ts +++ b/src/libs/native-base/components/Header.ts @@ -47,9 +47,9 @@ export default (variables /* : * */ = variable) => { shadowOffset: null, shadowRadius: null, shadowOpacity: null, - paddingTop: platform === OS.Android ? StatusBar.currentHeight : undefined, + paddingTop: platform === OS.ANDROID ? StatusBar.currentHeight : undefined, height: - platform === OS.Android + platform === OS.ANDROID ? variables.toolbarHeight + (StatusBar.currentHeight ?? 0) : variables.toolbarHeight, }, diff --git a/src/libs/native-base/components/ListItem.ts b/src/libs/native-base/components/ListItem.ts index 899322b..c9ef49f 100644 --- a/src/libs/native-base/components/ListItem.ts +++ b/src/libs/native-base/components/ListItem.ts @@ -97,7 +97,7 @@ export default (variables /* : * */ = variable) => { paddingTop: platform === OS.IOS ? variables.listItemPadding + 25 : undefined, paddingBottom: - platform === OS.Android ? variables.listItemPadding + 20 : undefined, + platform === OS.ANDROID ? variables.listItemPadding + 20 : undefined, flexDirection: 'row', borderColor: variables.listBorderColor, 'NativeBase.Text': { diff --git a/src/libs/native-base/components/TabHeading.ts b/src/libs/native-base/components/TabHeading.ts index e6137ee..f194f9f 100644 --- a/src/libs/native-base/components/TabHeading.ts +++ b/src/libs/native-base/components/TabHeading.ts @@ -12,8 +12,8 @@ export default (variables /* : * */ = variable) => { justifyContent: 'center', '.scrollable': { paddingHorizontal: 20, - flex: platform === OS.Android ? 0 : 1, - minWidth: platform === OS.Android ? undefined : 60, + flex: platform === OS.ANDROID ? 0 : 1, + minWidth: platform === OS.ANDROID ? undefined : 60, }, 'NativeBase.Text': { color: variables.topTabBarTextColor, diff --git a/src/libs/native-base/variables/types.ts b/src/libs/native-base/variables/types.ts index ab8b802..2105ad3 100644 --- a/src/libs/native-base/variables/types.ts +++ b/src/libs/native-base/variables/types.ts @@ -211,6 +211,6 @@ export type Variables = { }; export enum OS { - Android = 'android', + ANDROID = 'android', IOS = 'ios', } diff --git a/src/libs/savedqualifications/Provider.tsx b/src/libs/savedqualifications/Provider.tsx index 91e7506..0fe4996 100644 --- a/src/libs/savedqualifications/Provider.tsx +++ b/src/libs/savedqualifications/Provider.tsx @@ -1,19 +1,15 @@ -import React, { useCallback, useState } from 'react'; +import React, { PropsWithChildren, useCallback, useState } from 'react'; import { useEffectOnce, useUpdateEffect } from 'react-use'; import { useAsyncStorage } from '@react-native-async-storage/async-storage'; import analytics from '@react-native-firebase/analytics'; import { context as Context } from './context'; import { Event } from 'config/analytics'; -export interface SavedQualificationsProviderProps { - children?: React.ReactNode; -} - const ASYNC_STORAGE_KEY = 'saved_qualifications'; export const SavedQualificationsProvider = ({ children, -}: SavedQualificationsProviderProps) => { +}: PropsWithChildren<{}>) => { const [loading, setLoading] = useState(true); const [savedQualifications, setSavedQualifications] = useState([]); const asyncStorage = useAsyncStorage(ASYNC_STORAGE_KEY); @@ -54,7 +50,7 @@ export const SavedQualificationsProvider = ({ ); analytics().logEvent( - save ? Event.SaveQualification : Event.UnSaveQualification, + save ? Event.SAVE_QUALIFICATION : Event.UNSAVE_QUALIFICATION, { id: id.toString(), }, diff --git a/src/screens/App.tsx b/src/screens/App.tsx index abff647..f09f19a 100644 --- a/src/screens/App.tsx +++ b/src/screens/App.tsx @@ -1,5 +1,5 @@ import 'react-native-gesture-handler'; -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useMemo, useRef } from 'react'; import { ApolloProvider } from '@apollo/client'; import RNBootSplash from 'react-native-bootsplash'; import { Root, StyleProvider } from 'native-base'; @@ -18,8 +18,12 @@ const BaseApp = () => { }; const App = () => { - const theme = useRef(createTheme(variables)).current; - const client = useRef(createClient(API_URI)).current; + const theme = useMemo(() => { + return createTheme(variables); + }, []); + const client = useMemo(() => { + return createClient(API_URI); + }, []); return ( diff --git a/src/screens/HomeScreen/HomeScreen.tsx b/src/screens/HomeScreen/HomeScreen.tsx index c9a848d..83a9797 100644 --- a/src/screens/HomeScreen/HomeScreen.tsx +++ b/src/screens/HomeScreen/HomeScreen.tsx @@ -7,7 +7,7 @@ import Professions from './components/Professions/Professions'; const HomeScreen = () => { const [search, setSearch] = useState(''); - const [mode, setMode] = useState(Mode.All); + const [mode, setMode] = useState(Mode.ALL); return ( diff --git a/src/screens/HomeScreen/components/Header/Header.tsx b/src/screens/HomeScreen/components/Header/Header.tsx index 69e9b11..fc367d8 100644 --- a/src/screens/HomeScreen/components/Header/Header.tsx +++ b/src/screens/HomeScreen/components/Header/Header.tsx @@ -5,9 +5,9 @@ import { Keyboard, StyleSheet, TextInput } from 'react-native'; import { Icon, Input, Item, Header as NBHeader, View } from 'native-base'; import Menu from 'common/Menu/Menu'; -export interface HeaderProps { +export type HeaderProps = { onSearch?: (search: string) => void; -} +}; const Header = ({ onSearch }: HeaderProps) => { const inputRef = useRef(null); diff --git a/src/screens/HomeScreen/components/ModeSelector/ModeSelector.tsx b/src/screens/HomeScreen/components/ModeSelector/ModeSelector.tsx index 23df870..eded086 100644 --- a/src/screens/HomeScreen/components/ModeSelector/ModeSelector.tsx +++ b/src/screens/HomeScreen/components/ModeSelector/ModeSelector.tsx @@ -2,28 +2,28 @@ import React from 'react'; import { Button, Segment, Text } from 'native-base'; export enum Mode { - All, - Saved, + ALL, + SAVED, } -export interface ModeSelectorProps { +export type ModeSelectorProps = { mode: Mode; onChangeMode: (mode: Mode) => void; -} +}; const ModeSelector = ({ mode, onChangeMode }: ModeSelectorProps) => { return ( diff --git a/src/screens/HomeScreen/components/Professions/List/List.tsx b/src/screens/HomeScreen/components/Professions/List/List.tsx index 371ff9b..4330738 100644 --- a/src/screens/HomeScreen/components/Professions/List/List.tsx +++ b/src/screens/HomeScreen/components/Professions/List/List.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, useCallback } from 'react'; +import React, { forwardRef } from 'react'; import { FlatList, @@ -11,24 +11,20 @@ import ListEmpty from './ListEmpty'; import ListLoading from './ListLoading'; export type Item = ListItemProps; -export interface ListProps - extends Pick< - FlatListProps, - 'refreshing' | 'onRefresh' | 'contentContainerStyle' - > { +export type ListProps = { items: Item[]; loading?: boolean; -} +} & Pick< + FlatListProps, + 'refreshing' | 'onRefresh' | 'contentContainerStyle' +>; const noop = () => {}; +const keyExtractor = (item: ListItemProps) => item.id!; +const renderItem = ({ item }: { item: Item }) => ; -const MyList = forwardRef, ListProps>( +const List = forwardRef, ListProps>( ({ items, refreshing, onRefresh, loading, ...rest }: ListProps, ref) => { - const renderItem = useCallback(({ item }: { item: Item }) => { - return ; - }, []); - const keyExtractor = useCallback(item => item.id, []); - return ( { +export type ListItemProps = { onPress?: (id: string) => void; id?: string; text: string; -} +} & Pick; const MyListItem = ({ onPress, diff --git a/src/screens/HomeScreen/components/Professions/NetworkConnectionAlert.tsx b/src/screens/HomeScreen/components/Professions/NetworkConnectionAlert.tsx index 4b8b114..bae814a 100644 --- a/src/screens/HomeScreen/components/Professions/NetworkConnectionAlert.tsx +++ b/src/screens/HomeScreen/components/Professions/NetworkConnectionAlert.tsx @@ -4,25 +4,27 @@ import { Alert, Linking } from 'react-native'; import buildURL from 'utils/buildURL'; import { EMAIL } from 'config/app'; -export interface NetworkConnectionAlertProps { +export type NetworkConnectionAlertProps = { error?: ApolloError; -} +}; const NetworkConnectionAlert = ({ error }: NetworkConnectionAlertProps) => { useUpdateEffect(() => { - if (error && error.networkError) { - Alert.alert( - 'Problem z połączeniem', - 'Prosimy o sprawdzenie połączenia z internetem / spróbowanie ponownie później. Przepraszamy za utrudnienia.', - [ - { - text: 'Zgłoś problem', - onPress: () => Linking.openURL(buildURL('email', EMAIL)), - }, - { text: 'OK' }, - ], - ); + if (!error || !error.networkError) { + return; } + + Alert.alert( + 'Problem z połączeniem', + 'Prosimy o sprawdzenie połączenia z internetem / spróbowanie ponownie później. Przepraszamy za utrudnienia.', + [ + { + text: 'Zgłoś problem', + onPress: () => Linking.openURL(buildURL('email', EMAIL)), + }, + { text: 'OK' }, + ], + ); }, [error]); return null; diff --git a/src/screens/HomeScreen/components/Professions/Professions.tsx b/src/screens/HomeScreen/components/Professions/Professions.tsx index 35994ff..ff15d8e 100644 --- a/src/screens/HomeScreen/components/Professions/Professions.tsx +++ b/src/screens/HomeScreen/components/Professions/Professions.tsx @@ -11,10 +11,10 @@ import List, { Item } from './List/List'; import QualificationModal from './QualificationModal'; import NetworkConnectionAlert from './NetworkConnectionAlert'; -export interface ProfessionsProps { +export type ProfessionsProps = { mode: Mode; search: string; -} +}; const ID_SEPARATOR = '.'; const getQualificationAndProfessionID = (str: string): [number, number] => { @@ -44,16 +44,21 @@ const Professions = ({ mode, search }: ProfessionsProps) => { const [professionID, qualificationID] = getQualificationAndProfessionID( id, ); + const profession = professions.find(p => p.id === professionID); - if (profession) { - const qualification = profession.qualifications.find( - q => q.id === qualificationID, - ); - if (qualification) { - setSelectedQualification(qualification); - setIsModalVisible(true); - } + if (!profession) { + return; } + + const qualification = profession.qualifications.find( + q => q.id === qualificationID, + ); + if (!qualification) { + return; + } + + setSelectedQualification(qualification); + setIsModalVisible(true); }, [setIsModalVisible, setSelectedQualification, professions], ); @@ -62,7 +67,9 @@ const Professions = ({ mode, search }: ProfessionsProps) => { if (professionsLoading) { return; } + let items: Item[] = []; + professions.forEach(profession => { const qualifications = profession.qualifications .filter( @@ -70,7 +77,7 @@ const Professions = ({ mode, search }: ProfessionsProps) => { (!search || qualification.name.toLowerCase().includes(search) || qualification.code.toLowerCase().includes(search)) && - (mode === Mode.All || isSaved(qualification.id)), + (mode === Mode.ALL || isSaved(qualification.id)), ) .map( (qualification): Item => { @@ -83,18 +90,21 @@ const Professions = ({ mode, search }: ProfessionsProps) => { }; }, ); - if (qualifications.length > 0) { - items = [ - ...items, - { - text: profession.name, - itemHeader: true, - itemDivider: true, - id: 'P' + profession.id, - } as Item, - ...qualifications, - ]; + + if (qualifications.length === 0) { + return; } + + items = [ + ...items, + { + text: profession.name, + itemHeader: true, + itemDivider: true, + id: 'P' + profession.id, + } as Item, + ...qualifications, + ]; }); setListItems(items); diff --git a/src/screens/HomeScreen/components/Professions/QualificationModal.tsx b/src/screens/HomeScreen/components/Professions/QualificationModal.tsx index 1c2e66c..b9e24ce 100644 --- a/src/screens/HomeScreen/components/Professions/QualificationModal.tsx +++ b/src/screens/HomeScreen/components/Professions/QualificationModal.tsx @@ -19,10 +19,9 @@ import { } from 'native-base'; import Modal, { ModalProps } from 'common/Modal/Modal'; -export interface QualificationModalProps - extends Pick { +export type QualificationModalProps = { qualification: Maybe; -} +} & Pick; const QualificationModal = ({ qualification, @@ -81,7 +80,7 @@ const QualificationModal = ({ if (onPressBackdrop) { onPressBackdrop(); } - navigation.navigate(Screen.Test, { + navigation.navigate(Screen.TEST, { qualificationID: qualification?.id ?? 0, limit: question, }); diff --git a/src/screens/Navigation.tsx b/src/screens/Navigation.tsx index c6521d7..d498947 100644 --- a/src/screens/Navigation.tsx +++ b/src/screens/Navigation.tsx @@ -15,8 +15,8 @@ const AppStack = createStackNavigator(); const AppScreens = () => ( - - + + ); diff --git a/src/screens/TestScreen/TestScreen.tsx b/src/screens/TestScreen/TestScreen.tsx index 04537d8..061ac2f 100644 --- a/src/screens/TestScreen/TestScreen.tsx +++ b/src/screens/TestScreen/TestScreen.tsx @@ -15,9 +15,9 @@ import Suggestions from './components/Suggestions/Suggestions'; import Test from './components/Test/Test'; import Content from './components/Content/Content'; -export interface TestScreenProps { - route: RouteProp; -} +export type TestScreenProps = { + route: RouteProp; +}; type QueryGenerateTestSimilarQualificationsQualificationArgs = { limitTest: Scalars['Int']; diff --git a/src/screens/TestScreen/components/Content/Content.tsx b/src/screens/TestScreen/components/Content/Content.tsx index 7f45131..34a0496 100644 --- a/src/screens/TestScreen/components/Content/Content.tsx +++ b/src/screens/TestScreen/components/Content/Content.tsx @@ -2,10 +2,10 @@ import React from 'react'; import { StyleSheet } from 'react-native'; import { Content as ContentNB, NativeBase } from 'native-base'; -export interface ContentProps { +export type ContentProps = { children?: React.ReactNode; contentContainerStyle?: NativeBase.Content['contentContainerStyle']; -} +}; const Content = ({ children, contentContainerStyle }: ContentProps) => { return ( diff --git a/src/screens/TestScreen/components/Header/Header.tsx b/src/screens/TestScreen/components/Header/Header.tsx index b772120..e8f5a4b 100644 --- a/src/screens/TestScreen/components/Header/Header.tsx +++ b/src/screens/TestScreen/components/Header/Header.tsx @@ -12,9 +12,9 @@ import { } from 'native-base'; import Menu from 'common/Menu/Menu'; -export interface HeaderProps { +export type HeaderProps = { title: string; -} +}; const Header = ({ title }: HeaderProps) => { const navigation = useNavigation(); diff --git a/src/screens/TestScreen/components/Suggestions/Suggestions.tsx b/src/screens/TestScreen/components/Suggestions/Suggestions.tsx index 519a24f..5cc498b 100644 --- a/src/screens/TestScreen/components/Suggestions/Suggestions.tsx +++ b/src/screens/TestScreen/components/Suggestions/Suggestions.tsx @@ -9,9 +9,9 @@ import { StyleSheet } from 'react-native'; import { H1, View, H3, Card, CardItem, Text, Button, Body } from 'native-base'; import Content from '../Content/Content'; -export interface SuggestionsProps { +export type SuggestionsProps = { qualifications: Qualification[]; -} +}; const Suggestions = ({ qualifications }: SuggestionsProps) => { const navigation = useNavigation(); @@ -51,7 +51,7 @@ const Suggestions = ({ qualifications }: SuggestionsProps) => {