diff --git a/common/styleguide.tsx b/common/styleguide.tsx index 2cee80a00..42065cad1 100644 --- a/common/styleguide.tsx +++ b/common/styleguide.tsx @@ -1,8 +1,15 @@ import * as HtmlElements from '@expo/html-elements'; import { TextProps } from '@expo/html-elements/build/primitives/Text'; import Link from 'next/link'; -import { ComponentType, PropsWithChildren, useContext, useState } from 'react'; -import { StyleSheet, TextStyle, View, useWindowDimensions, ViewStyle } from 'react-native'; +import { type ComponentType, type PropsWithChildren, useContext, useState } from 'react'; +import { + StyleSheet, + TextStyle, + View, + useWindowDimensions, + ViewStyle, + StyleProp, +} from 'react-native'; import CustomAppearanceContext from '../context/CustomAppearanceContext'; @@ -87,16 +94,16 @@ type CustomTextProps = TextProps & const createTextComponent = (Element: ComponentType, textStyle?: TextStyles) => { const TextComponent = ({ children, style, id, numberOfLines }: CustomTextProps) => { const { isDark } = useContext(CustomAppearanceContext); + + const elementStyle = Element?.displayName + ? StyleSheet.flatten(textStyles[Element.displayName as keyof typeof textStyles]) + : undefined; + return ( + style={[elementStyle, textStyle, { color: isDark ? colors.white : colors.black }, style]}> {children} ); @@ -118,22 +125,14 @@ export const Caption = createTextComponent(HtmlElements.P, textStyles.caption); export const Label = createTextComponent(HtmlElements.P, textStyles.label); type AProps = PropsWithChildren<{ - style?: TextStyles; + style?: StyleProp; target?: string; href: string; - hoverStyle?: TextStyles; - containerStyle?: ViewStyle; + hoverStyle?: StyleProp; + containerStyle?: StyleProp; }>; -export const A = ({ - href, - target, - children, - style, - hoverStyle, - containerStyle, - ...rest -}: AProps) => { +export function A({ href, target, children, style, hoverStyle, containerStyle, ...rest }: AProps) { const { isDark } = useContext(CustomAppearanceContext); const [isHovered, setIsHovered] = useState(false); @@ -141,7 +140,7 @@ export const A = ({ const linkHoverStyles = getLinkHoverStyles(); if ((target === '_self' && !href.startsWith('#')) || href.startsWith('/')) { - const passedStyle = Array.isArray(style) ? StyleSheet.flatten(style) : style; + const passedStyle = StyleSheet.flatten(style); return ( {children} @@ -174,18 +173,22 @@ export const A = ({ ); -}; +} -const getLinkStyles = (isDark: boolean) => ({ - color: isDark ? colors.white : colors.black, - textDecorationColor: isDark ? colors.gray5 : colors.pewter, - textDecorationLine: 'underline', - fontFamily: 'inherit', -}); +function getLinkStyles(isDark: boolean): TextStyle { + return { + color: isDark ? colors.white : colors.black, + textDecorationColor: isDark ? colors.gray5 : colors.pewter, + textDecorationLine: 'underline', + fontFamily: 'inherit', + }; +} -const getLinkHoverStyles = () => ({ - textDecorationColor: colors.primaryDark, -}); +function getLinkHoverStyles(): TextStyle { + return { + textDecorationColor: colors.primaryDark, + }; +} export function HoverEffect({ children }: PropsWithChildren) { const [isHovered, setIsHovered] = useState(false); diff --git a/components/Button.tsx b/components/Button.tsx index f9791b6e9..7f61d5fbd 100644 --- a/components/Button.tsx +++ b/components/Button.tsx @@ -1,6 +1,6 @@ import { A } from '@expo/html-elements'; import { PropsWithChildren, useContext } from 'react'; -import { StyleSheet, TextStyle, Pressable } from 'react-native'; +import { StyleSheet, TextStyle, Pressable, StyleProp } from 'react-native'; import { colors, darkColors, HoverEffect, P } from '~/common/styleguide'; import CustomAppearanceContext from '~/context/CustomAppearanceContext'; @@ -9,7 +9,7 @@ type Props = PropsWithChildren & { href?: string; onPress?: () => void; openInNewTab?: boolean; - style?: TextStyle | TextStyle[]; + style?: StyleProp; }; export function Button({ children, href, onPress, style, openInNewTab, ...rest }: Props) { diff --git a/components/CheckBox.tsx b/components/CheckBox.tsx index c7a62d4a7..4466ac173 100644 --- a/components/CheckBox.tsx +++ b/components/CheckBox.tsx @@ -1,5 +1,5 @@ import { useContext } from 'react'; -import { StyleSheet, View, ViewStyle } from 'react-native'; +import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { colors, darkColors } from '~/common/styleguide'; import CustomAppearanceContext from '~/context/CustomAppearanceContext'; @@ -7,7 +7,7 @@ import CustomAppearanceContext from '~/context/CustomAppearanceContext'; import { Check } from './Icons'; type Props = { - style?: ViewStyle | ViewStyle[]; + style?: StyleProp; value?: boolean; color?: string; }; diff --git a/components/Filters/FilterButton.tsx b/components/Filters/FilterButton.tsx index c85c0f85f..761b8dc49 100644 --- a/components/Filters/FilterButton.tsx +++ b/components/Filters/FilterButton.tsx @@ -36,7 +36,7 @@ export const FilterButton = ({ ]; const filterCount = Object.keys(query).reduce( - (acc, q) => (params.includes(q) ? acc + 1 : acc), + (acc, q) => (params.includes(q as keyof Query) ? acc + 1 : acc), 0 ); const isFilterCount = !!filterCount; diff --git a/components/Filters/ToggleLink.tsx b/components/Filters/ToggleLink.tsx index ef57eab8e..4675d592b 100644 --- a/components/Filters/ToggleLink.tsx +++ b/components/Filters/ToggleLink.tsx @@ -11,7 +11,7 @@ import { CheckBox } from '../CheckBox'; type Props = { query: Query; - paramName: string; + paramName: keyof Query; title: string; basePath?: string; }; diff --git a/components/Filters/helpers.ts b/components/Filters/helpers.ts index 257fed4bc..905909717 100644 --- a/components/Filters/helpers.ts +++ b/components/Filters/helpers.ts @@ -1,4 +1,11 @@ -export const FILTER_PLATFORMS = [ +import { Query } from '~/types'; + +type FilterParamsType = { + param: keyof Query; + title: string; +}; + +export const FILTER_PLATFORMS: FilterParamsType[] = [ { param: 'android', title: 'Android', @@ -29,7 +36,7 @@ export const FILTER_PLATFORMS = [ }, ]; -export const FILTER_REQUIRES_MAIN_SEARCH = [ +export const FILTER_REQUIRES_MAIN_SEARCH: FilterParamsType[] = [ { param: 'isMaintained', title: 'Maintained', @@ -40,7 +47,7 @@ export const FILTER_REQUIRES_MAIN_SEARCH = [ }, ]; -export const FILTER_STATUS = [ +export const FILTER_STATUS: FilterParamsType[] = [ { param: 'newArchitecture', title: 'Supports New Architecture', @@ -67,7 +74,7 @@ export const FILTER_STATUS = [ }, ]; -export const FILTER_COMPATIBILITY = [ +export const FILTER_COMPATIBILITY: FilterParamsType[] = [ { param: 'expoGo', title: 'Works with Expo Go', diff --git a/components/Filters/index.tsx b/components/Filters/index.tsx index 56d5ff37a..96c601970 100644 --- a/components/Filters/index.tsx +++ b/components/Filters/index.tsx @@ -58,21 +58,21 @@ export function Filters({ query, style, basePath = '/' }: FiltersProps) { Status {isMainSearch && - FILTER_REQUIRES_MAIN_SEARCH.map(platform => ( + FILTER_REQUIRES_MAIN_SEARCH.map(status => ( ))} - {FILTER_STATUS.map(platform => ( + {FILTER_STATUS.map(status => ( ))} @@ -82,12 +82,12 @@ export function Filters({ query, style, basePath = '/' }: FiltersProps) { Compatibility - {FILTER_COMPATIBILITY.map(platform => ( + {FILTER_COMPATIBILITY.map(compatibility => ( ))} diff --git a/components/Footer.tsx b/components/Footer.tsx index 78f411dfc..516893b84 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -1,4 +1,4 @@ -import { FunctionComponent, SVGAttributes, createElement, useContext } from 'react'; +import { ComponentType, createElement, useContext } from 'react'; import { StyleSheet, View, ViewStyle } from 'react-native'; import { A, P, colors, darkColors, useLayout } from '~/common/styleguide'; @@ -6,6 +6,7 @@ import CustomAppearanceContext from '~/context/CustomAppearanceContext'; import ContentContainer from './ContentContainer'; import { + IconProps, Logo, PlatformAndroid, PlatformIOS, @@ -21,7 +22,7 @@ type PlatformProps = { name: string; pkgName: string; url: string; - Icon: FunctionComponent>; + Icon: ComponentType; style?: ViewStyle; }; diff --git a/components/Library/UnmaintainedLabel.tsx b/components/Library/UnmaintainedLabel.tsx index 2c5dcebcf..08f1ebb56 100644 --- a/components/Library/UnmaintainedLabel.tsx +++ b/components/Library/UnmaintainedLabel.tsx @@ -1,5 +1,5 @@ import { Fragment, useContext } from 'react'; -import { StyleSheet, View } from 'react-native'; +import { StyleProp, StyleSheet, TextStyle, View } from 'react-native'; import { A, colors, darkColors, Label, useLayout } from '~/common/styleguide'; import CustomAppearanceContext from '~/context/CustomAppearanceContext'; @@ -15,7 +15,7 @@ function UnmaintainedLabel({ alternatives }: Props) { const { isDark } = useContext(CustomAppearanceContext); const { isSmallScreen } = useLayout(); - const linkHoverStyle = isDark && { color: colors.secondary }; + const linkHoverStyle: StyleProp = isDark && { color: colors.secondary }; const contentColor = isDark ? darkColors.secondary : colors.gray5; return ( diff --git a/components/Search.tsx b/components/Search.tsx index 65afdcec7..4bd821681 100644 --- a/components/Search.tsx +++ b/components/Search.tsx @@ -24,7 +24,7 @@ const Search = ({ query, total }: Props) => { const [isInputFocused, setInputFocused] = useState(false); const [isFilterVisible, setFilterVisible] = useState(Object.keys(filterParams).length > 0); const [isApple, setIsApple] = useState(null); - const inputRef = useRef(null); + const inputRef = useRef(null); const { isSmallScreen } = useLayout(); const { isDark } = useContext(CustomAppearanceContext); @@ -79,7 +79,7 @@ const Search = ({ query, total }: Props) => { if (inputRef.current && event.key === 'Escape') { if (search) { event.preventDefault(); - inputRef.current.value = ''; + inputRef.current.clear(); void Router.replace( urlWithQuery('/', { ...query, diff --git a/pages/_app.tsx b/pages/_app.tsx index f5b613bcf..ba97ce760 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/react'; +import type { AppProps } from 'next/app'; import Head from 'next/head'; import { SafeAreaProvider } from 'react-native-safe-area-context'; @@ -21,22 +22,23 @@ Sentry.init({ tracesSampleRate: isProd ? 0.5 : 1.0, }); -const App = ({ pageProps, Component }) => ( - - - {context => ( - - - - - -
- -