diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..259e3dd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "jest.jestCommandLine": "node_modules/.bin/jest", +} \ No newline at end of file diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 22a49b6..f579bd6 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -1,9 +1,9 @@ import { Tabs } from 'expo-router'; import React from 'react'; -import { TabBarIcon } from '@/components/navigation/TabBarIcon'; import { Colors } from '@/constants/Colors'; import { useColorScheme } from '@/hooks/useColorScheme'; +import { TabBarIcon } from '@/components/navigation/TabBarIcon'; export default function TabLayout() { const colorScheme = useColorScheme(); @@ -17,18 +17,18 @@ export default function TabLayout() { ( - + ), }} /> ( - + ), }} /> diff --git a/app/(tabs)/explore.tsx b/app/(tabs)/explore.tsx deleted file mode 100644 index e480218..0000000 --- a/app/(tabs)/explore.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import Ionicons from '@expo/vector-icons/Ionicons'; -import { StyleSheet, Image, Platform } from 'react-native'; - -import { Collapsible } from '@/components/Collapsible'; -import { ExternalLink } from '@/components/ExternalLink'; -import ParallaxScrollView from '@/components/ParallaxScrollView'; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; - -export default function TabTwoScreen() { - return ( - }> - - Explore - - This app includes example code to help you get started. - - - This app has two screens:{' '} - app/(tabs)/index.tsx and{' '} - app/(tabs)/explore.tsx - - - The layout file in app/(tabs)/_layout.tsx{' '} - sets up the tab navigator. - - - Learn more - - - - - You can open this project on Android, iOS, and the web. To open the web version, press{' '} - w in the terminal running this project. - - - - - For static images, you can use the @2x and{' '} - @3x suffixes to provide files for - different screen densities - - - - Learn more - - - - - Open app/_layout.tsx to see how to load{' '} - - custom fonts such as this one. - - - - Learn more - - - - - This template has light and dark mode support. The{' '} - useColorScheme() hook lets you inspect - what the user's current color scheme is, and so you can adjust UI colors accordingly. - - - Learn more - - - - - This template includes an example of an animated component. The{' '} - components/HelloWave.tsx component uses - the powerful react-native-reanimated library - to create a waving hand animation. - - {Platform.select({ - ios: ( - - The components/ParallaxScrollView.tsx{' '} - component provides a parallax effect for the header image. - - ), - })} - - - ); -} - -const styles = StyleSheet.create({ - headerImage: { - color: '#808080', - bottom: -90, - left: -35, - position: 'absolute', - }, - titleContainer: { - flexDirection: 'row', - gap: 8, - }, -}); diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 0fed0e4..3a920d6 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,25 +1,31 @@ -import { Image, StyleSheet, Platform } from 'react-native'; +import { Image, StyleSheet, Platform, ImageBackground } from 'react-native'; -import { HelloWave } from '@/components/HelloWave'; -import ParallaxScrollView from '@/components/ParallaxScrollView'; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; import { SafeAreaView } from 'react-native-safe-area-context'; -import { LengthInput } from '@/components/LengthInput'; -import { setupStore } from '../store'; +import { MeasurementInput } from '@/components/LengthInput'; +import { setupStore, useAppDispatch } from '../store'; +import { selectProducts } from '@/features/product/productSlice'; +import { Product } from '@/lib/product'; +import { ProductTile } from '@/components/ProductTyle'; +import { Measure, area, length } from 'enheter'; export default function HomeScreen() { - const store = setupStore(); + const products = useAppDispatch(selectProducts); function calculatePrice() { } + const selectProduct = (product : Product) => { + + } + return ( - - {} + + {products.map((product) => { + + })} ); } diff --git a/app/(tabs)/product-editor.tsx b/app/(tabs)/product-editor.tsx new file mode 100644 index 0000000..42877cc --- /dev/null +++ b/app/(tabs)/product-editor.tsx @@ -0,0 +1,37 @@ +import { Image, StyleSheet, Platform, ImageBackground } from 'react-native'; + +import { SafeAreaView } from 'react-native-safe-area-context'; +import { MeasurementInput } from '@/components/LengthInput'; +import { setupStore, useAppDispatch } from '../store'; +import { selectProducts } from '@/features/product/productSlice'; +import { Product } from '@/lib/product'; +import { ProductTile } from '@/components/ProductTyle'; +import { Measure, area, length } from 'enheter'; + +export default function HomeScreen() { + + return ( + + + + ); +} + +const styles = StyleSheet.create({ + titleContainer: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + stepContainer: { + gap: 8, + marginBottom: 8, + }, + reactLogo: { + height: 178, + width: 290, + bottom: 0, + left: 0, + position: 'absolute', + }, +}); diff --git a/app/store.ts b/app/store.ts index 62ce741..cd0da6c 100644 --- a/app/store.ts +++ b/app/store.ts @@ -1,3 +1,4 @@ +import { useDispatch, useSelector } from 'react-redux'; import { combineReducers, configureStore } from '@reduxjs/toolkit'; import { rememberReducer, rememberEnhancer } from 'redux-remember'; import reducers from "@/features/product/productSlice" @@ -30,3 +31,6 @@ export function setupStore(preloadedState: Partial = { export type RootState = ReturnType; export type AppStore = ReturnType; export type AppDispatch = AppStore['dispatch']; + +export const useAppDispatch = useDispatch.withTypes(); +export const useAppSelector = useSelector.withTypes(); \ No newline at end of file diff --git a/components/LengthInput.tsx b/components/LengthInput.tsx index 3c73186..fc9b565 100644 --- a/components/LengthInput.tsx +++ b/components/LengthInput.tsx @@ -3,15 +3,15 @@ import { useState } from "react"; import { Button, StyleSheet, Text, TextInput, View } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; +export type t_length_unit = "foot" | "inch" +export type mode = "length" | "area" + export type LengthInputProps = { - onLengthSet?: (length: Measure<"length">) => any, - onAreaSet?: (area: Measure<"area">) => any, + onMeasurementSet?: (length: Measure<"length" | "area">) => any, isArea?: boolean, } -export type t_length_unit = "foot" | "inch" - -export function LengthInput(props: LengthInputProps) { +export function MeasurementInput(props: LengthInputProps) { const [length, setLength] = useState(null as null | number); const [width, setWidth] = useState(null as null | number); @@ -22,11 +22,11 @@ export function LengthInput(props: LengthInputProps) { setLength(value); if (!props.isArea) { const len = en_length(unit, value) - props.onLengthSet && props.onLengthSet(len) + props.onMeasurementSet && props.onMeasurementSet(len) } else { const en_unit = unit == "foot" ? "squareFoot" : "squareInch" const ar = en_area(en_unit, value); - props.onAreaSet && props.onAreaSet(ar); + props.onMeasurementSet && props.onMeasurementSet(ar); } } @@ -34,7 +34,7 @@ export function LengthInput(props: LengthInputProps) { const value = parseFloat(text); setLength(value); const len = en_length(unit, value) - props.onLengthSet && props.onLengthSet(len) + props.onMeasurementSet && props.onMeasurementSet(len) } return ( diff --git a/components/ProductAttributeEditor.tsx b/components/ProductAttributeEditor.tsx new file mode 100644 index 0000000..c494ed3 --- /dev/null +++ b/components/ProductAttributeEditor.tsx @@ -0,0 +1,45 @@ +import { Product } from "@/lib/product"; +import Ionicons from "@expo/vector-icons/Ionicons"; +import React from "react"; +import { ChangeEvent, SetStateAction, useState } from "react"; +import { NativeSyntheticEvent, StyleSheet, Text, TextInput, TextInputChangeEventData, TextInputProps, TouchableHighlight, View } from "react-native"; + +export type ProductAttributeChangeFunc = (product_id: string, key: string, newValue: string) => any; +export type ProductAttributeDeleteFunc = (product_id: string, key: string) => any; + +export type ProductAttributeProps = { product: Product, key: string, value: string, onChange?: ProductAttributeChangeFunc, onDelete?: ProductAttributeChangeFunc, }; + +export default function ProductAttributeEditor({ product, key, value, onDelete, onChange }: ProductAttributeProps) { + const [doEdit, setDoEdit] = useState(true); + const [newValue, setNewValue] = useState(value); + + const doChange = (e : any) => { + setNewValue(e.target.value); + } + + return ( + + + {key} + + + onDelete && onDelete(product.id, key, value)} + aria-label="Delete Attribute"> + + + {doEdit ? + ( + {newValue} + ) : ( + + ) + } + + + ) +} + +const style = StyleSheet.create({ + +}); \ No newline at end of file diff --git a/components/ProductEditor.tsx b/components/ProductEditor.tsx new file mode 100644 index 0000000..85f6a33 --- /dev/null +++ b/components/ProductEditor.tsx @@ -0,0 +1,47 @@ +import { useAppDispatch, useAppSelector } from "@/app/store" +import { deleteProduct, selectProducts, updateProduct } from "@/features/product/productSlice" +import { Product } from "@/lib/product"; +import { FlatListComponent, StyleSheet, Text } from "react-native"; +import { FlatList } from "react-native-reanimated/lib/typescript/Animated"; +import { SafeAreaView } from "react-native-safe-area-context"; +import { ProductEditorItem } from "./ProductEditorItem"; + +export const ProductEditor = () => { + const products = useAppSelector(selectProducts) as Product []; + + const dispatch = useAppDispatch(); + + function onProductDeleted(product_id: string) { + dispatch(deleteProduct(product_id)); + } + + function onProductUpdated(product_id: string, product: Product) { + dispatch(updateProduct(product)); + } + + + return ( + + { + return ( + + ) + } + } + /> + + ) +} + +const styles = StyleSheet.create({ + product: { + + } +}) \ No newline at end of file diff --git a/components/ProductEditorItem.tsx b/components/ProductEditorItem.tsx new file mode 100644 index 0000000..b068523 --- /dev/null +++ b/components/ProductEditorItem.tsx @@ -0,0 +1,58 @@ +import { Product } from "@/lib/product" +import { useState } from "react" +import { StyleSheet, Text, TouchableHighlight } from "react-native" +import { FlatList } from "react-native-reanimated/lib/typescript/Animated"; +import ProductAttributeEditor from "./ProductAttributeEditor"; + +export type ProductUpdatedFunc = (product_id : string, product : Product) => any; +export type ProductDeletedFunc = (product_id : string) => any; + +export type ProductEditorItemProps = { + product : Product, + onProductUpdated?: ProductUpdatedFunc, + onProductDeleted?: ProductDeletedFunc, +} + +export const ProductEditorItem = ({ product, onProductUpdated, onProductDeleted }: ProductEditorItemProps) => { + + const [showAttributes, setShowAttributes] = useState(true); + + function onAttributeChanged(product_id: string, key: string, newValue: string) { + product.attributes[key] = newValue; + onProductUpdated && onProductUpdated(product_id, product); + + } + + function onAttributeDelete(product_id: string, key: string) { + product.removeAttribute(key); + onProductUpdated && onProductUpdated(product_id, product); + } + + return ( + setShowAttributes(true)} + > + {product.attributes.name || `Product ${product.id}`} + {showAttributes && + ( + ( + + )} + /> + ) + } + + ) +} + +const styles = StyleSheet.create({ + product: {}, +}) \ No newline at end of file diff --git a/components/ProductTile.tsx b/components/ProductTile.tsx new file mode 100644 index 0000000..9b4fee3 --- /dev/null +++ b/components/ProductTile.tsx @@ -0,0 +1,44 @@ +import { Product } from "@/lib/product" +import { ImageBackground, StyleProp, StyleSheet, Text, ViewStyle } from "react-native"; +import { AnimatedStyle } from "react-native-reanimated"; +import { View } from "react-native-reanimated/lib/typescript/Animated"; + +export type OnProductSelectedFunc = (product : Product) => any; + +type MyStyle = StyleProp>>; + +export type ProductTileProps = { + product: (Product), + onProductSelected?: OnProductSelectedFunc, + style?: { + tile?: MyStyle, + image?: MyStyle, + } +} + +const FALLBACK_IMAGE = ""; + +export function ProductTile ({product, onProductSelected, style} : ProductTileProps) { + const src = product.attributes.image || FALLBACK_IMAGE; + return ( + + + {product.attributes.name || `Product ${product.id}`} + { product.pricePerUnit.toString() } / {product.measurement.value} {product.measurement.unit.symbol} + + + ); +} + +const styles = StyleSheet.create({ + image: { + + }, + text: { + + }, +}) \ No newline at end of file diff --git a/components/__tests__/ProductAttributeEditor-test.tsx b/components/__tests__/ProductAttributeEditor-test.tsx new file mode 100644 index 0000000..59c68a9 --- /dev/null +++ b/components/__tests__/ProductAttributeEditor-test.tsx @@ -0,0 +1,27 @@ +import { Product } from "@/lib/product" +import ProductAttributeEditor from "../ProductAttributeEditor" +import { renderWithProviders } from "./util" +import { area } from "enheter" +import {screen} from '@testing-library/react-native'; +import React from "react"; + +describe("Product editor tests", () => { + it("Product attributes render", () => { + const product = new Product( + 100, + area("squareFoot", 4 * 7) + ); + const onChange = jest.fn(); + const onDelete = jest.fn(); + renderWithProviders( + ); + expect(screen.findByLabelText("Delete Attribute")).not.toBeNull(); + + }) +}) \ No newline at end of file diff --git a/components/__tests__/util.tsx b/components/__tests__/util.tsx new file mode 100644 index 0000000..b097f81 --- /dev/null +++ b/components/__tests__/util.tsx @@ -0,0 +1,41 @@ +import { RenderOptions, render } from "@testing-library/react-native"; +import { PropsWithChildren, ReactElement } from "react"; +import { Provider } from "react-redux"; +import { setupStore, RootState } from "@/app/store"; +import { Price, Product } from "@/lib/product"; +import { area, length } from "enheter"; + +export interface ExtendedRenderOptions extends Omit { + preloadedState?: Partial; + store?: any; // TODO +} + +const basicState = { + products: [ + new Product(20.00, length("foot", 5), {name: "Track"}), + new Product(20.00, area("squareFoot", 5), {name: "Shelf"}), + ] +} + +export function renderWithProviders( + ui: ReactElement, + preloadedState = basicState, + extendedRenderOptions: ExtendedRenderOptions = {}, +) { + const { + // Automatically create a store instance if no store was passed in + store = setupStore(preloadedState as Partial), + ...renderOptions + } = extendedRenderOptions; + + const Wrapper = ({ children }: PropsWithChildren) => ( + {children} + ); + + // Return an object with the store and all of RTL's query functions + return { + store, + ...render(ui, { wrapper: Wrapper, ...renderOptions }) + }; +} + diff --git a/features/product/initialProducts.ts b/features/product/initialProducts.ts new file mode 100644 index 0000000..b267c57 --- /dev/null +++ b/features/product/initialProducts.ts @@ -0,0 +1,11 @@ +import { Price, Product } from "@/lib/product"; +import { area, length } from "enheter"; + +export default [ + // Sheet goods + new Product(new Price("USD", 35), area("squareFoot", 4 * 8), {name: "Plywood"}), + new Product(new Price("USD", 45), area("squareFoot", 4 * 8), {name: "OSB"}), + new Product(new Price("USD", 45), area("squareFoot", 4 * 8), {name: "MDF"}), + // Beams and trim + new Product(new Price("USD", 1), length("foot", 1), {name: "trim"}), +] \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index b413e10..3494840 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,7 @@ /** @type {import('ts-jest').JestConfigWithTsJest} */ module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', -}; \ No newline at end of file + preset: 'jest-expo', + "transformIgnorePatterns": [ + "node_modules/(?!@ngrx|(?!deck.gl)|ng-dynamic)" + ] +}; diff --git a/lib/__tests__/product-test.ts b/lib/__tests__/product-test.ts index e8aed9b..b8a6c72 100644 --- a/lib/__tests__/product-test.ts +++ b/lib/__tests__/product-test.ts @@ -1,24 +1,24 @@ import { length } from "enheter"; -import { AreaProduct, LengthProduct, price } from "../product"; +import { Product } from "../product"; describe("Product tests", () => { it(`Length product gives correct price for a shorter length`, () => { - const standard = new LengthProduct(price("USD", 20), length("foot", 4)); + const standard = new Product(20, length("foot", 4)); const comparison = standard.priceFor(length("foot", 2)); - expect(comparison.value).toEqual(10); + expect(comparison).toEqual(10); }); it(`Length product gives correct price for a longer length`, () => { - const standard = new LengthProduct(price("USD", 20), length("foot", 4)); + const standard = new Product(20, length("foot", 4)); const comparison = standard.priceFor(length("foot", 8)); - expect(comparison.value).toEqual(40); + expect(comparison).toEqual(40); }); it(`Length product gives correct price if different units`, () => { - const standard = new LengthProduct(price("USD", 10), length("foot", 1)); + const standard = new Product(10, length("foot", 1)); const comparison = standard.priceFor(length("inch", 24)); - expect(comparison.value).toEqual(20); + expect(comparison).toEqual(20); }); }); \ No newline at end of file diff --git a/lib/product.ts b/lib/product.ts index b8daea3..281ace7 100644 --- a/lib/product.ts +++ b/lib/product.ts @@ -1,63 +1,58 @@ import {length as en_length, area as en_area} from "enheter" import {Measure, LengthUnit, AreaUnit, } from "enheter" +import uuid from "react-native-uuid"; export type Id = string; export type Currency = "USD"; -export class Price { - constructor(public value : number) {} -} +export const CURRENCY_SYMBOLS : Map<"USD", string> = { + "USD": "$", +}; -export function price(c : Currency, value : number) { - return new Price(value); +export class Price { + constructor(public currency : Currency, public value : number) {} + public toString() { + const sym = CURRENCY_SYMBOLS.get(this.currency); + return `${sym} ${Math.round(this.value)}`; + } } export type ProductAttributes = { + id?: string, name?: string, image?: string, description?: string, depth?: string, + currency?: Currency, [key:string]: any, } - -export abstract class Product { - id?: Id - constructor(public pricePerUnit : Price, public attributes : ProductAttributes = {}) {} - public abstract priceFor(measure : Measure<"length"> | Measure<"area">) : Price; -} - - -interface ILength { - length : Measure<"length"> -} - -interface IArea { - area: Measure<"area"> -} - -export class LengthProduct extends Product implements ILength { - constructor(public pricePerUnit : Price, public length: Measure<"length">, public attributes : ProductAttributes = {}) { - super(pricePerUnit, attributes); +export class Product { + public id : string; + constructor(public pricePerUnit : number, public measurement : Measure<"length" | "area">, public attributes : ProductAttributes = {}) { + this.id = attributes.id || uuid.v4().toString(); } - public priceFor(length : Measure<"length">): Price { - const ratio = length.convertTo(this.length.unit).divideBy(this.length).value; - return { - value: this.pricePerUnit.value * ratio, - }; - } -} - -export class AreaProduct extends Product implements IArea { - constructor(public pricePerUnit : Price, public area : Measure<"area">, public attributes : ProductAttributes = {}) { - super(pricePerUnit, attributes); + public priceFor(measurement : Measure<"length" | "area">): number { + const ratio = measurement.convertTo(this.measurement.unit).divideBy(this.measurement).value; + return this.pricePerUnit * ratio } - public priceFor(area : Measure<"area">): Price { - const ratio = area.convertTo(this.area.unit).divideBy(this.area).value; - return { - value: this.pricePerUnit.value * ratio, - }; + get isArea() { + return this.measurement.unit.dimension.length === 2; + } + + get isLength() { + return this.measurement.unit.dimension.length === 1; + } + + get attributesAsList() { + return Object.entries(this.attributes).map(([key, value]) => { + return {key, value} + }) + } + + public removeAttribute(key : string) { + delete this.attributes[key] } } \ No newline at end of file diff --git a/package.json b/package.json index d8b5a9f..1b13b7c 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,13 @@ "lint": "expo lint" }, "dependencies": { + "@babel/runtime": "^7.24.7", "@expo/vector-icons": "^14.0.0", "@react-native-async-storage/async-storage": "^1.23.1", + "@react-native/assets-registry": "^0.74.84", "@react-navigation/native": "^6.0.2", "@reduxjs/toolkit": "^2.2.5", + "@testing-library/react-native": "^12.5.1", "enheter": "^1.0.27", "expo": "~51.0.14", "expo-constants": "~16.0.2", @@ -26,6 +29,7 @@ "expo-status-bar": "~1.12.1", "expo-system-ui": "~3.0.6", "expo-web-browser": "~13.0.3", + "interopRequireDefault": "link:@babel/runtime/helpers/interopRequireDefault", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.74.2", @@ -35,6 +39,7 @@ "react-native-screens": "3.31.1", "react-native-uuid": "^2.0.2", "react-native-web": "~0.19.10", + "react-redux": "^9.1.2", "redux-remember": "^5.1.0", "uuid": "^10.0.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e582bfc..a80f6f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,18 +5,27 @@ settings: excludeLinksFromLockfile: false dependencies: + '@babel/runtime': + specifier: ^7.24.7 + version: 7.24.7 '@expo/vector-icons': specifier: ^14.0.0 version: 14.0.2 '@react-native-async-storage/async-storage': specifier: ^1.23.1 version: 1.23.1(react-native@0.74.2) + '@react-native/assets-registry': + specifier: ^0.74.84 + version: 0.74.84 '@react-navigation/native': specifier: ^6.0.2 version: 6.1.17(react-native@0.74.2)(react@18.2.0) '@reduxjs/toolkit': specifier: ^2.2.5 - version: 2.2.5(react@18.2.0) + version: 2.2.5(react-redux@9.1.2)(react@18.2.0) + '@testing-library/react-native': + specifier: ^12.5.1 + version: 12.5.1(jest@29.7.0)(react-native@0.74.2)(react-test-renderer@18.2.0)(react@18.2.0) enheter: specifier: ^1.0.27 version: 1.0.27 @@ -47,6 +56,9 @@ dependencies: expo-web-browser: specifier: ~13.0.3 version: 13.0.3(expo@51.0.14) + interopRequireDefault: + specifier: link:@babel/runtime/helpers/interopRequireDefault + version: link:@babel/runtime/helpers/interopRequireDefault react: specifier: 18.2.0 version: 18.2.0 @@ -74,6 +86,9 @@ dependencies: react-native-web: specifier: ~0.19.10 version: 0.19.12(react-dom@18.2.0)(react@18.2.0) + react-redux: + specifier: ^9.1.2 + version: 9.1.2(@types/react@18.2.79)(react@18.2.0)(redux@5.0.1) redux-remember: specifier: ^5.1.0 version: 5.1.0(redux@5.0.1) @@ -619,7 +634,6 @@ packages: dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 - dev: true /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} @@ -1699,7 +1713,6 @@ packages: /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - dev: true /@egjs/hammerjs@2.0.17: resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==} @@ -2172,12 +2185,10 @@ packages: get-package-type: 0.1.0 js-yaml: 3.14.1 resolve-from: 5.0.0 - dev: true /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - dev: true /@jest/console@29.7.0: resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} @@ -2189,7 +2200,6 @@ packages: jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - dev: true /@jest/core@29.7.0: resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} @@ -2232,7 +2242,6 @@ packages: - babel-plugin-macros - supports-color - ts-node - dev: true /@jest/create-cache-key-function@29.7.0: resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} @@ -2254,7 +2263,6 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: jest-get-type: 29.6.3 - dev: true /@jest/expect@29.7.0: resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} @@ -2264,7 +2272,6 @@ packages: jest-snapshot: 29.7.0 transitivePeerDependencies: - supports-color - dev: true /@jest/fake-timers@29.7.0: resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} @@ -2287,7 +2294,6 @@ packages: jest-mock: 29.7.0 transitivePeerDependencies: - supports-color - dev: true /@jest/reporters@29.7.0: resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} @@ -2324,7 +2330,6 @@ packages: v8-to-istanbul: 9.3.0 transitivePeerDependencies: - supports-color - dev: true /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} @@ -2339,7 +2344,6 @@ packages: '@jridgewell/trace-mapping': 0.3.25 callsites: 3.1.0 graceful-fs: 4.2.11 - dev: true /@jest/test-result@29.7.0: resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} @@ -2349,7 +2353,6 @@ packages: '@jest/types': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 collect-v8-coverage: 1.0.2 - dev: true /@jest/test-sequencer@29.7.0: resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} @@ -2359,7 +2362,6 @@ packages: graceful-fs: 4.2.11 jest-haste-map: 29.7.0 slash: 3.0.0 - dev: true /@jest/transform@29.7.0: resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} @@ -2382,7 +2384,6 @@ packages: write-file-atomic: 4.0.2 transitivePeerDependencies: - supports-color - dev: true /@jest/types@26.6.2: resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} @@ -2944,7 +2945,7 @@ packages: nanoid: 3.3.7 dev: false - /@reduxjs/toolkit@2.2.5(react@18.2.0): + /@reduxjs/toolkit@2.2.5(react-redux@9.1.2)(react@18.2.0): resolution: {integrity: sha512-aeFA/s5NCG7NoJe/MhmwREJxRkDs0ZaSqt0MxhWUrwCf1UQXpwR87RROJEql0uAkLI6U7snBOYOcKw83ew3FPg==} peerDependencies: react: ^16.9.0 || ^17.0.0 || ^18 @@ -2957,6 +2958,7 @@ packages: dependencies: immer: 10.1.1 react: 18.2.0 + react-redux: 9.1.2(@types/react@18.2.79)(react@18.2.0)(redux@5.0.1) redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) reselect: 5.1.1 @@ -3092,6 +3094,26 @@ packages: dependencies: '@sinonjs/commons': 3.0.1 + /@testing-library/react-native@12.5.1(jest@29.7.0)(react-native@0.74.2)(react-test-renderer@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-PApr3f6DmSJF/EIiWYZfcBzuy6w7fK8TW4a6KfQHTeAcfZ6lADtRO7R0QM5WI+b7tJ33JvIPgzCg1MiuRz4v0g==} + peerDependencies: + jest: '>=28.0.0' + react: '>=16.8.0' + react-native: '>=0.59' + react-test-renderer: '>=16.8.0' + peerDependenciesMeta: + jest: + optional: true + dependencies: + jest: 29.7.0 + jest-matcher-utils: 29.7.0 + pretty-format: 29.7.0 + react: 18.2.0 + react-native: 0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7)(@types/react@18.2.79)(react@18.2.0) + react-test-renderer: 18.2.0(react@18.2.0) + redent: 3.0.0 + dev: false + /@tootallnate/once@2.0.0: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} @@ -3105,26 +3127,22 @@ packages: '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.6 - dev: true /@types/babel__generator@7.6.8: resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} dependencies: '@babel/types': 7.24.7 - dev: true /@types/babel__template@7.4.4: resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} dependencies: '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - dev: true /@types/babel__traverse@7.20.6: resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} dependencies: '@babel/types': 7.24.7 - dev: true /@types/cookie@0.6.0: resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} @@ -3134,7 +3152,6 @@ packages: resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} dependencies: '@types/node': 20.14.9 - dev: true /@types/hammerjs@2.0.45: resolution: {integrity: sha512-qkcUlZmX6c4J8q45taBKTL3p+LbITgyx7qhlPYOdOHZB7B31K0mXbP5YA7i7SgDeEGuI9MnumiKPEMrxg8j3KQ==} @@ -3215,6 +3232,10 @@ packages: resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} dev: true + /@types/use-sync-external-store@0.0.3: + resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==} + dev: false + /@types/yargs-parser@21.0.3: resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -3680,7 +3701,6 @@ packages: slash: 3.0.0 transitivePeerDependencies: - supports-color - dev: true /babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} @@ -3693,7 +3713,6 @@ packages: test-exclude: 6.0.0 transitivePeerDependencies: - supports-color - dev: true /babel-plugin-jest-hoist@29.6.3: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} @@ -3703,7 +3722,6 @@ packages: '@babel/types': 7.24.7 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.6 - dev: true /babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.7): resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==} @@ -3788,7 +3806,6 @@ packages: '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7) - dev: true /babel-preset-expo@11.0.10(@babel/core@7.24.7)(@babel/preset-env@7.24.7): resolution: {integrity: sha512-YBg40Om31gw9IPlRw5v8elzgtPUtNEh4GSibBi5MsmmYddGg4VPjWtCZIFJChN543qRmbGb/fa/kejvLX567hQ==} @@ -3817,7 +3834,6 @@ packages: '@babel/core': 7.24.7 babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7) - dev: true /babel-runtime@6.26.0: resolution: {integrity: sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==} @@ -4003,7 +4019,6 @@ packages: /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - dev: true /camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} @@ -4042,7 +4057,6 @@ packages: /char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} - dev: true /char-regex@2.0.1: resolution: {integrity: sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==} @@ -4105,7 +4119,6 @@ packages: /cjs-module-lexer@1.3.1: resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} - dev: true /clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} @@ -4169,11 +4182,9 @@ packages: /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - dev: true /collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - dev: true /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -4343,7 +4354,6 @@ packages: - babel-plugin-macros - supports-color - ts-node - dev: true /cross-fetch@3.1.8: resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} @@ -4528,7 +4538,6 @@ packages: peerDependenciesMeta: babel-plugin-macros: optional: true - dev: true /deep-equal@2.2.3: resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} @@ -4643,12 +4652,10 @@ packages: /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} - dev: true /diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} @@ -4736,7 +4743,6 @@ packages: /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} - dev: true /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -5333,7 +5339,6 @@ packages: /exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} - dev: true /expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} @@ -5344,7 +5349,6 @@ packages: jest-matcher-utils: 29.7.0 jest-message-util: 29.7.0 jest-util: 29.7.0 - dev: true /expo-asset@10.0.9(expo@51.0.14): resolution: {integrity: sha512-KX7LPtVf9eeMidUvYZafXZldrVdzfjZNKKFAjFvDy2twg7sTa2R0L4VdCXp32eGLWZyk+i/rpOUSbyD1YFyJnA==} @@ -5549,7 +5553,6 @@ packages: /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true /fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} @@ -5831,7 +5834,6 @@ packages: /get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} - dev: true /get-port@3.2.0: resolution: {integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==} @@ -6077,7 +6079,6 @@ packages: /html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - dev: true /htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} @@ -6177,7 +6178,6 @@ packages: dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 - dev: true /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -6345,7 +6345,6 @@ packages: /is-generator-fn@2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} - dev: true /is-generator-function@1.0.10: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} @@ -6531,7 +6530,6 @@ packages: /istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} - dev: true /istanbul-lib-instrument@5.2.1: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} @@ -6544,7 +6542,6 @@ packages: semver: 6.3.1 transitivePeerDependencies: - supports-color - dev: true /istanbul-lib-instrument@6.0.2: resolution: {integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==} @@ -6557,7 +6554,6 @@ packages: semver: 7.6.2 transitivePeerDependencies: - supports-color - dev: true /istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} @@ -6566,7 +6562,6 @@ packages: istanbul-lib-coverage: 3.2.2 make-dir: 4.0.0 supports-color: 7.2.0 - dev: true /istanbul-lib-source-maps@4.0.1: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} @@ -6577,7 +6572,6 @@ packages: source-map: 0.6.1 transitivePeerDependencies: - supports-color - dev: true /istanbul-reports@3.1.7: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} @@ -6585,7 +6579,6 @@ packages: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - dev: true /iterator.prototype@1.1.2: resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} @@ -6613,7 +6606,6 @@ packages: execa: 5.1.1 jest-util: 29.7.0 p-limit: 3.1.0 - dev: true /jest-circus@29.7.0: resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} @@ -6642,7 +6634,6 @@ packages: transitivePeerDependencies: - babel-plugin-macros - supports-color - dev: true /jest-cli@29.7.0: resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} @@ -6670,7 +6661,6 @@ packages: - babel-plugin-macros - supports-color - ts-node - dev: true /jest-config@29.7.0(@types/node@20.14.9): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} @@ -6710,7 +6700,6 @@ packages: transitivePeerDependencies: - babel-plugin-macros - supports-color - dev: true /jest-diff@29.7.0: resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} @@ -6720,14 +6709,12 @@ packages: diff-sequences: 29.6.3 jest-get-type: 29.6.3 pretty-format: 29.7.0 - dev: true /jest-docblock@29.7.0: resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: detect-newline: 3.1.0 - dev: true /jest-each@29.7.0: resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} @@ -6738,7 +6725,6 @@ packages: jest-get-type: 29.6.3 jest-util: 29.7.0 pretty-format: 29.7.0 - dev: true /jest-environment-jsdom@29.7.0: resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==} @@ -6821,7 +6807,6 @@ packages: walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 - dev: true /jest-leak-detector@29.7.0: resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} @@ -6829,7 +6814,6 @@ packages: dependencies: jest-get-type: 29.6.3 pretty-format: 29.7.0 - dev: true /jest-matcher-utils@29.7.0: resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} @@ -6839,7 +6823,6 @@ packages: jest-diff: 29.7.0 jest-get-type: 29.6.3 pretty-format: 29.7.0 - dev: true /jest-message-util@29.7.0: resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} @@ -6873,12 +6856,10 @@ packages: optional: true dependencies: jest-resolve: 29.7.0 - dev: true /jest-regex-util@29.6.3: resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true /jest-resolve-dependencies@29.7.0: resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} @@ -6888,7 +6869,6 @@ packages: jest-snapshot: 29.7.0 transitivePeerDependencies: - supports-color - dev: true /jest-resolve@29.7.0: resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} @@ -6903,7 +6883,6 @@ packages: resolve: 1.22.8 resolve.exports: 2.0.2 slash: 3.0.0 - dev: true /jest-runner@29.7.0: resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} @@ -6932,7 +6911,6 @@ packages: source-map-support: 0.5.13 transitivePeerDependencies: - supports-color - dev: true /jest-runtime@29.7.0: resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} @@ -6962,7 +6940,6 @@ packages: strip-bom: 4.0.0 transitivePeerDependencies: - supports-color - dev: true /jest-snapshot@29.7.0: resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} @@ -6990,7 +6967,6 @@ packages: semver: 7.6.2 transitivePeerDependencies: - supports-color - dev: true /jest-util@29.7.0: resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} @@ -7050,7 +7026,6 @@ packages: emittery: 0.13.1 jest-util: 29.7.0 string-length: 4.0.2 - dev: true /jest-worker@29.7.0: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} @@ -7080,7 +7055,6 @@ packages: - babel-plugin-macros - supports-color - ts-node - dev: true /jimp-compact@0.16.1: resolution: {integrity: sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==} @@ -7215,7 +7189,6 @@ packages: /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - dev: true /json-schema-deref-sync@0.13.0: resolution: {integrity: sha512-YBOEogm5w9Op337yb6pAT6ZXDqlxAsQCanM3grid8lMWNxRJO/zWEJi3ZzqDL8boWfwhTFym5EFrNgWwpqcBRg==} @@ -7528,7 +7501,6 @@ packages: engines: {node: '>=10'} dependencies: semver: 7.6.2 - dev: true /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -7847,6 +7819,11 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: false + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -7966,7 +7943,6 @@ packages: /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true /ncp@2.0.0: resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==} @@ -8358,7 +8334,6 @@ packages: error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - dev: true /parse-png@2.1.0: resolution: {integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==} @@ -8467,7 +8442,6 @@ packages: engines: {node: '>=8'} dependencies: find-up: 4.1.0 - dev: true /plist@3.1.0: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} @@ -8590,7 +8564,6 @@ packages: /pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - dev: true /qrcode-terminal@0.11.0: resolution: {integrity: sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==} @@ -8854,6 +8827,25 @@ packages: - utf-8-validate dev: false + /react-redux@9.1.2(@types/react@18.2.79)(react@18.2.0)(redux@5.0.1): + resolution: {integrity: sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==} + peerDependencies: + '@types/react': ^18.2.25 + react: ^18.0 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + dependencies: + '@types/react': 18.2.79 + '@types/use-sync-external-store': 0.0.3 + react: 18.2.0 + redux: 5.0.1 + use-sync-external-store: 1.2.2(react@18.2.0) + dev: false + /react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -8877,7 +8869,6 @@ packages: react-is: 18.3.1 react-shallow-renderer: 16.15.0(react@18.2.0) scheduler: 0.23.2 - dev: true /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} @@ -8920,6 +8911,14 @@ packages: tslib: 2.6.3 dev: false + /redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + dev: false + /redux-remember@5.1.0(redux@5.0.1): resolution: {integrity: sha512-Z6/S/brpwflOsGpX8Az93eujJ5fytMcaefxDfx0iib5d0DkL804zlw/Fhh/4HzZ5nXsP67j1zPUeDNWO1rhfvA==} peerDependencies: @@ -9058,7 +9057,6 @@ packages: engines: {node: '>=8'} dependencies: resolve-from: 5.0.0 - dev: true /resolve-from@3.0.0: resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==} @@ -9435,7 +9433,6 @@ packages: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - dev: true /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -9557,7 +9554,6 @@ packages: dependencies: char-regex: 1.0.2 strip-ansi: 6.0.1 - dev: true /string-length@5.0.1: resolution: {integrity: sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==} @@ -9672,7 +9668,6 @@ packages: /strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} - dev: true /strip-eof@1.0.0: resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} @@ -9683,6 +9678,13 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + /strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: false + /strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -9691,7 +9693,6 @@ packages: /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - dev: true /strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} @@ -9839,7 +9840,6 @@ packages: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.2 - dev: true /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -10204,6 +10204,14 @@ packages: react: 18.2.0 dev: false + /use-sync-external-store@1.2.2(react@18.2.0): + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: false @@ -10244,7 +10252,6 @@ packages: '@jridgewell/trace-mapping': 0.3.25 '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - dev: true /valid-url@1.0.9: resolution: {integrity: sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==} @@ -10470,7 +10477,6 @@ packages: dependencies: imurmurhash: 0.1.4 signal-exit: 3.0.7 - dev: true /ws@6.2.3: resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==} diff --git a/tsconfig.json b/tsconfig.json index 185c1d8..d867257 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "expo/tsconfig.base", "compilerOptions": { + "jsx": "react", "strict": true, "paths": { "@/*": [