diff --git a/App.tsx b/App.tsx index 886d25f..a081775 100644 --- a/App.tsx +++ b/App.tsx @@ -1,18 +1,39 @@ -import { NavigationContainer } from '@react-navigation/native'; +import { + createStaticNavigation, + StaticParamList, +} from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import React from 'react'; import Details from './src/screens/Details'; import Home from './src/screens/Home'; -const Stack = createNativeStackNavigator(); +const RootStack = createNativeStackNavigator({ + screens: { + Home: { + screen: Home, + options: { + title: 'Places', + }, + }, + Details: { + screen: Details, + options: ({ route }) => ({ + title: route.params?.name, + }), + }, + }, +}); + +type RootStackParamList = StaticParamList; + +declare global { + namespace ReactNavigation { + interface RootParamList extends RootStackParamList {} + } +} + +const Navigation = createStaticNavigation(RootStack); export default function App() { - return ( - - - - - - - ); + return ; } diff --git a/babel.config.js b/babel.config.js index f7b3da3..02c7d13 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,4 @@ module.exports = { presets: ['module:@react-native/babel-preset'], + plugins: ['react-native-reanimated/plugin'], }; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 963e387..4517289 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1575,7 +1575,115 @@ PODS: - React-logger (= 0.76.1) - React-perflogger (= 0.76.1) - React-utils (= 0.76.1) - - RNScreens (3.35.0): + - RNFlashList (1.7.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - RNReanimated (3.16.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNReanimated/reanimated (= 3.16.1) + - RNReanimated/worklets (= 3.16.1) + - Yoga + - RNReanimated/reanimated (3.16.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNReanimated/reanimated/apple (= 3.16.1) + - Yoga + - RNReanimated/reanimated/apple (3.16.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - RNReanimated/worklets (3.16.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - RNScreens (4.0.0): - DoubleConversion - glog - hermes-engine @@ -1596,9 +1704,9 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNScreens/common (= 3.35.0) + - RNScreens/common (= 4.0.0) - Yoga - - RNScreens/common (3.35.0): + - RNScreens/common (4.0.0): - DoubleConversion - glog - hermes-engine @@ -1689,6 +1797,8 @@ DEPENDENCIES: - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - ReactCodegen (from `build/generated/ios`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "RNFlashList (from `../node_modules/@shopify/flash-list`)" + - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) @@ -1824,6 +1934,10 @@ EXTERNAL SOURCES: :path: build/generated/ios ReactCommon: :path: "../node_modules/react-native/ReactCommon" + RNFlashList: + :path: "../node_modules/@shopify/flash-list" + RNReanimated: + :path: "../node_modules/react-native-reanimated" RNScreens: :path: "../node_modules/react-native-screens" Yoga: @@ -1893,7 +2007,9 @@ SPEC CHECKSUMS: React-utils: 5362bd16a9563f9916e7a56c011ddc533507650f ReactCodegen: 865bafc5c17ec2181620ced1a32c39c38ab2951d ReactCommon: 422e364463f33e336fc4db196aeb50fd801d90d6 - RNScreens: e389d6a6a66a4f0d3662924ecae803073ccce8ec + RNFlashList: 6f169ad83e52579b7754cbbcec1b004c27d82c93 + RNReanimated: 77242c6d67416988a2fd9f5cf574bb3e60016362 + RNScreens: 2fe13c8d610ef2d9d5ace2e7d85b716ec0f5217c SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: db69236006b8b1c6d55ab453390c882306cbf219 diff --git a/metro.config.js b/metro.config.js index 9d41685..71232fc 100644 --- a/metro.config.js +++ b/metro.config.js @@ -1,4 +1,7 @@ -const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); +const { getDefaultConfig } = require('@react-native/metro-config'); +const { + wrapWithReanimatedMetroConfig, +} = require('react-native-reanimated/metro-config'); /** * Metro configuration @@ -6,6 +9,6 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); * * @type {import('metro-config').MetroConfig} */ -const config = {}; +const config = getDefaultConfig(__dirname); -module.exports = mergeConfig(getDefaultConfig(__dirname), config); +module.exports = wrapWithReanimatedMetroConfig(config); diff --git a/package-lock.json b/package-lock.json index e2dae8d..c25d31d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,12 +8,14 @@ "name": "borisyankovonboarding", "version": "0.0.1", "dependencies": { - "@react-navigation/native": "^6.1.18", - "@react-navigation/native-stack": "^6.11.0", + "@react-navigation/native": "^7.0.0", + "@react-navigation/native-stack": "^7.0.0", + "@shopify/flash-list": "^1.7.1", "react": "18.3.1", "react-native": "0.76.1", + "react-native-reanimated": "^3.16.1", "react-native-safe-area-context": "^4.14.0", - "react-native-screens": "^3.35.0" + "react-native-screens": "^4.0.0" }, "devDependencies": { "@babel/core": "^7.25.2", @@ -3420,82 +3422,114 @@ } }, "node_modules/@react-navigation/core": { - "version": "6.4.17", - "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.17.tgz", - "integrity": "sha512-Nd76EpomzChWAosGqWOYE3ItayhDzIEzzZsT7PfGcRFDgW5miHV2t4MZcq9YIK4tzxZjVVpYbIynOOQQd1e0Cg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.0.0.tgz", + "integrity": "sha512-HZbWgx7Fco9orcQdAPgcq/G8C1/+Smm8LrzBsih1rBlpVornyEj8lumF6Y9fnYnHjuBhBydvYBgBqTwh87rWXA==", "license": "MIT", "dependencies": { - "@react-navigation/routers": "^6.1.9", + "@react-navigation/routers": "^7.0.0", "escape-string-regexp": "^4.0.0", - "nanoid": "^3.1.23", + "nanoid": "3.3.7", "query-string": "^7.1.3", - "react-is": "^16.13.0", - "use-latest-callback": "^0.2.1" + "react-is": "^18.2.0", + "use-latest-callback": "^0.2.1", + "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "react": "*" } }, "node_modules/@react-navigation/core/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "license": "MIT" }, "node_modules/@react-navigation/elements": { - "version": "1.3.31", - "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.31.tgz", - "integrity": "sha512-bUzP4Awlljx5RKEExw8WYtif8EuQni2glDaieYROKTnaxsu9kEIA515sXQgUDZU4Ob12VoL7+z70uO3qrlfXcQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.0.0.tgz", + "integrity": "sha512-kt2Q5WLJ9jjJMA/Jt8S3z3Jub2V+HIJ2LM4z+dZqL00FVsTfa4rSk3BTktI3MmBiUCgzUo6jPOxkxsUbjoL/ig==", "license": "MIT", + "dependencies": { + "color": "^4.2.3" + }, "peerDependencies": { - "@react-navigation/native": "^6.0.0", - "react": "*", + "@react-native-masked-view/masked-view": ">= 0.2.0", + "@react-navigation/native": "^7.0.0", + "react": ">= 18.2.0", "react-native": "*", - "react-native-safe-area-context": ">= 3.0.0" + "react-native-safe-area-context": ">= 4.0.0" + }, + "peerDependenciesMeta": { + "@react-native-masked-view/masked-view": { + "optional": true + } } }, "node_modules/@react-navigation/native": { - "version": "6.1.18", - "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.18.tgz", - "integrity": "sha512-mIT9MiL/vMm4eirLcmw2h6h/Nm5FICtnYSdohq4vTLA2FF/6PNhByM7s8ffqoVfE5L0uAa6Xda1B7oddolUiGg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.0.0.tgz", + "integrity": "sha512-OkfVzQNAuvy6WduON+4ctLImQWybBnOEycVpEEFA3y8nheLuo4hT+ObyyYu2DVqtslltUlTNGcd2G7pbB7y/bA==", "license": "MIT", "dependencies": { - "@react-navigation/core": "^6.4.17", + "@react-navigation/core": "^7.0.0", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", - "nanoid": "^3.1.23" + "nanoid": "3.3.7", + "use-latest-callback": "^0.2.1" }, "peerDependencies": { - "react": "*", + "react": ">= 18.2.0", "react-native": "*" } }, "node_modules/@react-navigation/native-stack": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.11.0.tgz", - "integrity": "sha512-U5EcUB9Q2NQspCFwYGGNJm0h6wBCOv7T30QjndmvlawLkNt7S7KWbpWyxS9XBHSIKF57RgWjfxuJNTgTstpXxw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.0.0.tgz", + "integrity": "sha512-OZEvXaQDZesWnib+XD7PgWaVeS95/oD/gCSmDXXQZLTtGBXutmLycGtvjunFHRAkQ8u3TB89oOhs9YxDBAXl5Q==", "license": "MIT", "dependencies": { - "@react-navigation/elements": "^1.3.31", - "warn-once": "^0.1.0" + "@react-navigation/elements": "^2.0.0", + "warn-once": "^0.1.1" }, "peerDependencies": { - "@react-navigation/native": "^6.0.0", - "react": "*", + "@react-navigation/native": "^7.0.0", + "react": ">= 18.2.0", "react-native": "*", - "react-native-safe-area-context": ">= 3.0.0", - "react-native-screens": ">= 3.0.0" + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.0.0" } }, "node_modules/@react-navigation/routers": { - "version": "6.1.9", - "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz", - "integrity": "sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.0.0.tgz", + "integrity": "sha512-b2ehNmgAfDziTd0EERm0C9JI9JH1kdRS4SNBWbKQOVPv23WG+5ExovwWet26sGtMabLJ5lxSE8Z2/fByfggjNQ==", "license": "MIT", "dependencies": { - "nanoid": "^3.1.23" + "nanoid": "3.3.7" } }, + "node_modules/@shopify/flash-list": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@shopify/flash-list/-/flash-list-1.7.1.tgz", + "integrity": "sha512-sUYl7h8ydJutufA26E42Hj7cLvaBTpkMIyNJiFrxUspkcANb6jnFiLt9rEwAuDjvGk/C0lHau+WyT6ZOxqVPwg==", + "license": "MIT", + "dependencies": { + "recyclerlistview": "4.2.1", + "tslib": "2.6.3" + }, + "peerDependencies": { + "@babel/runtime": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/@shopify/flash-list/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "license": "0BSD" + }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -4886,6 +4920,19 @@ "dev": true, "license": "MIT" }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4904,6 +4951,16 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", @@ -10086,7 +10143,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10642,7 +10698,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -10654,7 +10709,6 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/punycode": { @@ -10864,6 +10918,30 @@ } } }, + "node_modules/react-native-reanimated": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.16.1.tgz", + "integrity": "sha512-Wnbo7toHZ6kPLAD8JWKoKCTfNoqYOMW5vUEP76Rr4RBmJCrdXj6oauYP0aZnZq8NCbiP5bwwu7+RECcWtoetnQ==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-arrow-functions": "^7.0.0-0", + "@babel/plugin-transform-class-properties": "^7.0.0-0", + "@babel/plugin-transform-classes": "^7.0.0-0", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", + "@babel/plugin-transform-optional-chaining": "^7.0.0-0", + "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", + "@babel/plugin-transform-template-literals": "^7.0.0-0", + "@babel/plugin-transform-unicode-regex": "^7.0.0-0", + "@babel/preset-typescript": "^7.16.7", + "convert-source-map": "^2.0.0", + "invariant": "^2.2.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0", + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-safe-area-context": { "version": "4.14.0", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.14.0.tgz", @@ -10875,9 +10953,9 @@ } }, "node_modules/react-native-screens": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.35.0.tgz", - "integrity": "sha512-rmkqb/M/SQIrXwygk6pXcOhgHltYAhidf1WceO7ujAxkr6XtwmgFyd1HIztsrJa568GrAuwPdQ11I7TpVk+XsA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.0.0.tgz", + "integrity": "sha512-QGQ8+d90chOZ9JwA2K01nFzrGCTMNjsiAKJGPUXcLEiIF77/VSjLjQE9ZluMtkva0gzGI9tb/yxETkJnkw1iag==", "license": "MIT", "dependencies": { "react-freeze": "^1.0.0", @@ -11038,6 +11116,21 @@ "node": ">= 4" } }, + "node_modules/recyclerlistview": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/recyclerlistview/-/recyclerlistview-4.2.1.tgz", + "integrity": "sha512-NtVYjofwgUCt1rEsTp6jHQg/47TWjnO92TU2kTVgJ9wsc/ely4HnizHHa+f/dI7qaw4+zcSogElrLjhMltN2/g==", + "license": "Apache-2.0", + "dependencies": { + "lodash.debounce": "4.0.8", + "prop-types": "15.8.1", + "ts-object-utils": "0.0.5" + }, + "peerDependencies": { + "react": ">= 15.2.1", + "react-native": ">= 0.30.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -11585,6 +11678,21 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -12209,6 +12317,12 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-object-utils": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/ts-object-utils/-/ts-object-utils-0.0.5.tgz", + "integrity": "sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA==", + "license": "ISC" + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -12494,6 +12608,15 @@ "react": ">=16.8" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 3c01fd0..46253d3 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,14 @@ "test": "jest" }, "dependencies": { - "@react-navigation/native": "^6.1.18", - "@react-navigation/native-stack": "^6.11.0", + "@react-navigation/native": "^7.0.0", + "@react-navigation/native-stack": "^7.0.0", + "@shopify/flash-list": "^1.7.1", "react": "18.3.1", "react-native": "0.76.1", + "react-native-reanimated": "^3.16.1", "react-native-safe-area-context": "^4.14.0", - "react-native-screens": "^3.35.0" + "react-native-screens": "^4.0.0" }, "devDependencies": { "@babel/core": "^7.25.2", diff --git a/src/api/api.ts b/src/api/api.ts index 57a5c67..3343359 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -3,7 +3,7 @@ import { WeatherIcon } from './types'; const API_KEY = 'placeholder'; export function iconUrl(code: WeatherIcon) { - return `https://openweathermap.org/img/wn/${code}@2x.png`; + return `https://openweathermap.org/img/wn/${code}@4x.png`; } export async function fetchData(url: string) { diff --git a/src/api/current.ts b/src/api/current.ts index c4d0b1f..9b63f23 100644 --- a/src/api/current.ts +++ b/src/api/current.ts @@ -26,8 +26,7 @@ type CurrentWeatherResponse = { cod: number; }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const sampleResponse: CurrentWeatherResponse = { +export const sampleResponse: CurrentWeatherResponse = { coord: { lon: 0, lat: 0, diff --git a/src/api/forecast.ts b/src/api/forecast.ts index cfdface..2de5208 100644 --- a/src/api/forecast.ts +++ b/src/api/forecast.ts @@ -30,8 +30,7 @@ type ForecastResponse = { city: City; }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const sampleResponse: ForecastResponse = { +export const sampleForecastResponse: ForecastResponse = { cod: '200', message: 0, cnt: 40, diff --git a/src/api/geocoding.ts b/src/api/geocoding.ts index a304748..2862462 100644 --- a/src/api/geocoding.ts +++ b/src/api/geocoding.ts @@ -5,8 +5,7 @@ import { City, CountryCode } from './types'; type DirectResponse = City[]; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const sampleDirectResponse: DirectResponse = [ +export const sampleDirectResponse: DirectResponse = [ { name: 'London', local_names: { @@ -287,8 +286,7 @@ type ZipResponse = { country: CountryCode; }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const sampleZipResponse: ZipResponse = { +export const sampleZipResponse: ZipResponse = { zip: 'E14', name: 'London', lat: 51.4969, @@ -304,8 +302,7 @@ export async function fetchZip(zipCode: string, countryCode: string) { type ReverseResponse = City[]; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const sampleReverseResponse: ReverseResponse = [ +export const sampleReverseResponse: ReverseResponse = [ { name: 'City of Westminster', local_names: { diff --git a/src/api/group.ts b/src/api/group.ts index 7724acb..e5d2f6d 100644 --- a/src/api/group.ts +++ b/src/api/group.ts @@ -15,12 +15,12 @@ type GroupResponse = { list: GroupedWeather[]; }; -type GroupedWeather = { +export type GroupedWeather = { coord: Coord; sys: System; weather: WeatherCondition[]; main: Weather; - visibility: 10000; + visibility: number; wind: Wind; clouds: Clouds; dt: number; @@ -28,17 +28,44 @@ type GroupedWeather = { name: string; }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const sampleGroupWeather: GroupResponse = { - cnt: 1, +export const sampleGroupWeather: GroupResponse = { + cnt: 4, list: [ { - coord: { lon: 30.5167, lat: 50.4333 }, + coord: { lon: 24.75, lat: 42.15 }, sys: { - country: 'UA', + country: 'BG', timezone: 7200, - sunrise: 1730782547, - sunset: 1730816836, + sunrise: 1730869221, + sunset: 1730905736, + }, + weather: [ + { id: 800, main: 'Clear', description: 'clear sky', icon: '01n' }, + ], + main: { + temp: 280.19, + feels_like: 278.84, + temp_min: 280.19, + temp_max: 282.32, + pressure: 1034, + sea_level: 1034, + grnd_level: 984, + humidity: 49, + }, + visibility: 10000, + wind: { speed: 2.06, deg: 90 }, + clouds: { all: 0 }, + dt: 1730909346, + id: 728193, + name: 'Plovdiv', + }, + { + coord: { lon: 17.0333, lat: 51.1 }, + sys: { + country: 'PL', + timezone: 3600, + sunrise: 1730872406, + sunset: 1730906255, }, weather: [ { @@ -49,21 +76,75 @@ const sampleGroupWeather: GroupResponse = { }, ], main: { - temp: 279.11, - feels_like: 275.61, - temp_min: 279.11, - temp_max: 280.18, - pressure: 1028, - sea_level: 1028, - grnd_level: 1011, - humidity: 69, + temp: 278.28, + feels_like: 275.41, + temp_min: 277.15, + temp_max: 278.66, + pressure: 1031, + sea_level: 1031, + grnd_level: 1014, + humidity: 93, + }, + visibility: 9000, + wind: { speed: 3.6, deg: 310 }, + clouds: { all: 100 }, + dt: 1730909367, + id: 3081368, + name: 'Wroclaw', + }, + { + coord: { lon: -0.1257, lat: 51.5085 }, + sys: { + country: 'GB', + timezone: 0, + sunrise: 1730876603, + sunset: 1730910295, + }, + weather: [{ id: 721, main: 'Haze', description: 'haze', icon: '50d' }], + main: { + temp: 283.31, + feels_like: 282.76, + temp_min: 282.35, + temp_max: 284.21, + pressure: 1031, + sea_level: 1031, + grnd_level: 1027, + humidity: 91, + }, + visibility: 5000, + wind: { speed: 2.06, deg: 140 }, + clouds: { all: 100 }, + dt: 1730909365, + id: 2643743, + name: 'London', + }, + { + coord: { lon: -122.4194, lat: 37.7749 }, + sys: { + country: 'US', + timezone: -28800, + sunrise: 1730904056, + sunset: 1730941545, + }, + weather: [ + { id: 801, main: 'Clouds', description: 'few clouds', icon: '02d' }, + ], + main: { + temp: 287.08, + feels_like: 285.44, + temp_min: 284.91, + temp_max: 288.35, + pressure: 1016, + sea_level: 1016, + grnd_level: 1012, + humidity: 35, }, visibility: 10000, - wind: { speed: 5.16, deg: 276 }, - clouds: { all: 90 }, - dt: 1730826549, - id: 703448, - name: 'Kyiv', + wind: { speed: 4.02, deg: 36 }, + clouds: { all: 20 }, + dt: 1730906307, + id: 5391959, + name: 'San Francisco', }, ], }; diff --git a/src/api/pollution.ts b/src/api/pollution.ts index afd164b..0717b46 100644 --- a/src/api/pollution.ts +++ b/src/api/pollution.ts @@ -8,8 +8,7 @@ type PollutionResponse = { list: Pollution[]; }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const samplePollutionResponse: PollutionResponse = { +export const samplePollutionResponse: PollutionResponse = { coord: { lon: 0, lat: 0, diff --git a/src/screens/CityForecast.tsx b/src/screens/CityForecast.tsx new file mode 100644 index 0000000..7081f6c --- /dev/null +++ b/src/screens/CityForecast.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { StyleSheet, Text, View } from 'react-native'; +import { GroupedWeather } from '../api/group'; +import { sampleForecastResponse } from '../api/forecast'; +import { formatDate, formatTemperature } from '../utils'; + +type Props = { + city: GroupedWeather; +}; + +export default function CityForecast({ city }: Props) { + return ( + + 5-day forecast + {sampleForecastResponse.list.map(x => ( + + {formatDate(x.dt)} {formatTemperature(x.main.temp_max)}{' '} + {formatTemperature(x.main.temp_min)} + + ))} + + ); +} + +const styles = StyleSheet.create({ + wrapper: { + padding: 16, + }, + header: { + fontSize: 24, + }, + icon: { + width: 128, + height: 128, + }, +}); diff --git a/src/screens/CityItem.tsx b/src/screens/CityItem.tsx new file mode 100644 index 0000000..1584507 --- /dev/null +++ b/src/screens/CityItem.tsx @@ -0,0 +1,67 @@ +import { useNavigation } from '@react-navigation/native'; +import React from 'react'; +import { Image, StyleSheet, Text, View } from 'react-native'; +import { iconUrl } from '../api/api'; +import { GroupedWeather } from '../api/group'; +import { formatTemperature } from '../utils'; +import { PressableScale } from './PressableScale'; + +type Props = { + city: GroupedWeather; +}; + +export default function CityItem({ city }: Props) { + const navigation = useNavigation(); + return ( + + navigation.navigate('Details', { id: city.id, name: city.name }) + }> + + + + {city.name}, {city.sys.country} + + {formatTemperature(city.main.temp)} + {city.weather[0].main} + + + + + ); +} + +const styles = StyleSheet.create({ + card: { + backgroundColor: 'white', + shadowColor: 'black', + shadowOffset: { + width: 0, + height: 1, + }, + shadowOpacity: 0.2, + shadowRadius: 1.41, + elevation: 2, + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + margin: 8, + paddingHorizontal: 16, + }, + name: { + fontWeight: 'bold', + }, + temp: { + fontSize: 32, + fontWeight: 'bold', + fontFamily: 'monospace', + }, + icon: { + width: 96, + height: 96, + }, +}); diff --git a/src/screens/CityList.tsx b/src/screens/CityList.tsx new file mode 100644 index 0000000..f1a0757 --- /dev/null +++ b/src/screens/CityList.tsx @@ -0,0 +1,14 @@ +import { FlashList } from '@shopify/flash-list'; +import React from 'react'; +import CityItem from './CityItem'; +import { sampleGroupWeather } from '../api/group'; + +export default function CityList() { + return ( + } + estimatedItemSize={200} + /> + ); +} diff --git a/src/screens/CityWeather.tsx b/src/screens/CityWeather.tsx new file mode 100644 index 0000000..bac3d68 --- /dev/null +++ b/src/screens/CityWeather.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { Image, StyleSheet, Text, View } from 'react-native'; +import { GroupedWeather } from '../api/group'; +import { formatTemperature } from '../utils'; +import { iconUrl } from '../api/api'; + +type Props = { + city: GroupedWeather; +}; + +export default function CityWeather({ city }: Props) { + return ( + + + + {formatTemperature(city.main.temp)} + + + + + Feels like {Math.round(city.main.feels_like - 273.15)}° + Humidity: {city.main.humidity}% + Wind: {city.wind.speed} km/h + + + ); +} + +const styles = StyleSheet.create({ + wrapper: { + flexDirection: 'row', + padding: 16, + justifyContent: 'space-between', + }, + tempWrapper: { + flexDirection: 'row', + alignItems: 'center', + }, + temp: { + fontSize: 32, + }, + icon: { + width: 64, + height: 64, + }, + details: { + alignItems: 'flex-end', + }, +}); diff --git a/src/screens/Details.tsx b/src/screens/Details.tsx index 4784a34..69be68c 100644 --- a/src/screens/Details.tsx +++ b/src/screens/Details.tsx @@ -1,10 +1,24 @@ +import type { StaticScreenProps } from '@react-navigation/native'; import React from 'react'; import { StyleSheet, Text, View } from 'react-native'; +import { sampleGroupWeather } from '../api/group'; +import CityForecast from './CityForecast'; +import CityWeather from './CityWeather'; -export default function Details() { +export type Props = StaticScreenProps<{ + id: number; + name: string; +}>; + +export default function Details({ route }: Props) { + const city = sampleGroupWeather.list.find(c => c.id === route.params.id); + if (!city) { + return City not found; + } return ( - Home Screen + + ); } @@ -12,7 +26,13 @@ export default function Details() { const styles = StyleSheet.create({ screen: { flex: 1, - alignItems: 'center', - justifyContent: 'center', + backgroundColor: 'white', + }, + city: { + fontSize: 32, + }, + icon: { + width: 128, + height: 128, }, }); diff --git a/src/screens/Home.tsx b/src/screens/Home.tsx index 7731c32..ca06492 100644 --- a/src/screens/Home.tsx +++ b/src/screens/Home.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import { Button, StyleSheet, Text, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import CityList from './CityList'; -export default function Home({ navigation }) { +export default function Home() { return ( - Home Screen -