diff --git a/android/app/src/main/java/net/rpcsx/next/MainActivity.kt b/android/app/src/main/java/net/rpcsx/next/MainActivity.kt index 1caf005..84db62f 100644 --- a/android/app/src/main/java/net/rpcsx/next/MainActivity.kt +++ b/android/app/src/main/java/net/rpcsx/next/MainActivity.kt @@ -4,6 +4,14 @@ import expo.modules.splashscreen.SplashScreenManager import android.os.Build import android.os.Bundle +import android.content.res.Configuration +import android.view.View +import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.WindowInsetsControllerCompat + import com.facebook.react.ReactActivity import com.facebook.react.ReactActivityDelegate import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled @@ -21,6 +29,11 @@ class MainActivity : ReactActivity() { SplashScreenManager.registerOnActivity(this) // @generated end expo-splashscreen super.onCreate(null) + + val orientation = resources.configuration.orientation + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + enableImmersive() + } } /** @@ -49,17 +62,56 @@ class MainActivity : ReactActivity() { * where moving root activities to background instead of finishing activities. * @see onBackPressed */ - override fun invokeDefaultOnBackPressed() { - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) { - if (!moveTaskToBack(false)) { - // For non-root activities, use the default implementation to finish them. - super.invokeDefaultOnBackPressed() - } - return - } + override fun invokeDefaultOnBackPressed() { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) { + if (!moveTaskToBack(false)) { + // For non-root activities, use the default implementation to finish them. + super.invokeDefaultOnBackPressed() + } + return + } - // Use the default back button implementation on Android S - // because it's doing more than [Activity.moveTaskToBack] in fact. - super.invokeDefaultOnBackPressed() - } + // Use the default back button implementation on Android S + // because it's doing more than [Activity.moveTaskToBack] in fact. + super.invokeDefaultOnBackPressed() + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + enableImmersive() + } else { + disableImmersive() + } + } + + + private fun enableImmersive() { + with(window) { + WindowCompat.setDecorFitsSystemWindows(this, false) + val insetsController = WindowInsetsControllerCompat(this, decorView) + insetsController.apply { + hide(WindowInsetsCompat.Type.systemBars()) + systemBarsBehavior = + WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + } + attributes.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + } + } + + private fun disableImmersive() { + with(window) { + val insetsController = WindowInsetsControllerCompat(this, decorView) + insetsController.apply { + hide(WindowInsetsCompat.Type.systemBars()) + } + } + } + + override fun onWindowFocusChanged(hasFocus: Boolean) { + super.onWindowFocusChanged(hasFocus) + val orientation = resources.configuration.orientation + if (hasFocus && orientation == Configuration.ORIENTATION_LANDSCAPE) enableImmersive() + } } diff --git a/package-lock.json b/package-lock.json index c8b6838..af78361 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@react-navigation/bottom-tabs": "^7.3.10", "@react-navigation/elements": "^2.3.8", "@react-navigation/native": "^7.1.6", + "@shopify/react-native-skia": "v2.0.0-next.4", "expo": "^53.0.20", "expo-blur": "~14.1.5", "expo-constants": "~17.1.7", @@ -44,9 +45,11 @@ "react-native": "0.79.6", "react-native-device-info": "^14.0.4", "react-native-gesture-handler": "~2.24.0", - "react-native-reanimated": "~3.17.4", + "react-native-localize": "^3.6.0", + "react-native-reanimated": "^3.17.5", "react-native-safe-area-context": "^5.4.0", "react-native-screens": "~4.11.1", + "react-native-skia": "^0.0.1", "react-native-web": "^0.20.0", "react-native-webview": "13.13.5" }, @@ -141,6 +144,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -167,13 +171,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -394,9 +398,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -525,12 +529,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", - "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.2" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -931,9 +935,9 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", - "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", + "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -941,7 +945,7 @@ "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/traverse": "^7.28.4" }, "engines": { "node": ">=6.9.0" @@ -1525,17 +1529,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", - "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.3", + "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2", + "@babel/types": "^7.28.5", "debug": "^4.3.1" }, "engines": { @@ -1562,13 +1566,13 @@ } }, "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -2829,9 +2833,9 @@ } }, "node_modules/@expo/cli/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -3134,9 +3138,9 @@ } }, "node_modules/@expo/config-plugins/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -3218,9 +3222,9 @@ } }, "node_modules/@expo/config/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -3307,9 +3311,9 @@ } }, "node_modules/@expo/devcert/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -3400,9 +3404,9 @@ } }, "node_modules/@expo/fingerprint/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -3553,9 +3557,9 @@ } }, "node_modules/@expo/metro-config/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -4187,9 +4191,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -4955,6 +4959,7 @@ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.17.tgz", "integrity": "sha512-uEcYWi1NV+2Qe1oELfp9b5hTYekqWATv2cuwcOAg5EvsIsUPtzFrKIasgUXLBRGb9P7yR5ifoJ+ug4u6jdqSTQ==", "license": "MIT", + "peer": true, "dependencies": { "@react-navigation/core": "^7.12.4", "escape-string-regexp": "^4.0.0", @@ -5044,6 +5049,32 @@ "dev": true, "license": "MIT" }, + "node_modules/@shopify/react-native-skia": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/@shopify/react-native-skia/-/react-native-skia-2.0.0-next.4.tgz", + "integrity": "sha512-NzvdgryRz6tkKMHgCChCKa3wXfN9TZhlV0/LrfIU/wKLC1uKgGXkoZgNz7Is0wwdhtao1JJJJ81fqHCGHgzk9g==", + "license": "MIT", + "dependencies": { + "canvaskit-wasm": "0.40.0", + "react-reconciler": "0.31.0" + }, + "bin": { + "setup-skia-web": "scripts/setup-canvaskit.js" + }, + "peerDependencies": { + "react": ">=19.0", + "react-native": ">=0.78", + "react-native-reanimated": "^3.0" + }, + "peerDependenciesMeta": { + "react-native": { + "optional": true + }, + "react-native-reanimated": { + "optional": true + } + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -5267,6 +5298,7 @@ "integrity": "sha512-ixLZ7zG7j1fM0DijL9hDArwhwcCb4vqmePgwtV0GfnkHRSCUEv4LvzarcTdhoqgyMznUx/EhoTUv31CKZzkQlw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -5349,6 +5381,7 @@ "integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.40.0", "@typescript-eslint/types": "8.40.0", @@ -5853,6 +5886,12 @@ "@urql/core": "^5.0.0" } }, + "node_modules/@webgpu/types": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.21.tgz", + "integrity": "sha512-pUrWq3V5PiSGFLeLxoGqReTZmiiXwY3jRkIG5sLLKjyqNxrwm/04b4nw7LSmGWJcKk59XOM/YRTUwOzo4MMlow==", + "license": "BSD-3-Clause" + }, "node_modules/@xmldom/xmldom": { "version": "0.8.11", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", @@ -5899,6 +5938,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6666,6 +6706,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001735", "electron-to-chromium": "^1.5.204", @@ -6985,6 +7026,15 @@ ], "license": "CC-BY-4.0" }, + "node_modules/canvaskit-wasm": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/canvaskit-wasm/-/canvaskit-wasm-0.40.0.tgz", + "integrity": "sha512-Od2o+ZmoEw9PBdN/yCGvzfu0WVqlufBPEWNG452wY7E9aT8RBE+ChpZF526doOlg7zumO4iCS+RAeht4P0Gbpw==", + "license": "BSD-3-Clause", + "dependencies": { + "@webgpu/types": "0.1.21" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7421,9 +7471,9 @@ } }, "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -7984,16 +8034,6 @@ "node": ">= 0.8" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -8307,6 +8347,7 @@ "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -8520,6 +8561,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -8873,6 +8915,7 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-53.0.20.tgz", "integrity": "sha512-Nh+HIywVy9KxT/LtH08QcXqrxtUOA9BZhsXn3KCsAYA+kNb80M8VKN8/jfQF+I6CgeKyFKJoPNsWgI0y0VBGrA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "0.24.20", @@ -8947,6 +8990,7 @@ "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-17.1.7.tgz", "integrity": "sha512-byBjGsJ6T6FrLlhOBxw4EaiMXrZEn/MlUYIj/JAd+FS7ll5X/S4qVRbIimSJtdW47hXMq0zxPfJX6njtA56hHA==", "license": "MIT", + "peer": true, "dependencies": { "@expo/config": "~11.0.12", "@expo/env": "~1.0.7" @@ -9054,6 +9098,7 @@ "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-13.3.2.tgz", "integrity": "sha512-wUlMdpqURmQ/CNKK/+BIHkDA5nGjMqNlYmW0pJFXY/KE/OG80Qcavdu2sHsL4efAIiNGvYdBS10WztuQYU4X0A==", "license": "MIT", + "peer": true, "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -9109,6 +9154,7 @@ "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-7.1.7.tgz", "integrity": "sha512-ZJaH1RIch2G/M3hx2QJdlrKbYFUTOjVVW4g39hfxrE5bPX9xhZUYXqxqQtzMNl1ylAevw9JkgEfWbBWddbZ3UA==", "license": "MIT", + "peer": true, "dependencies": { "expo-constants": "~17.1.7", "invariant": "^2.2.4" @@ -9159,9 +9205,9 @@ } }, "node_modules/expo-modules-autolinking/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -10024,14 +10070,14 @@ } }, "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "license": "ISC", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "license": "BlueOak-1.0.0", "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -10060,10 +10106,10 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "license": "ISC", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, @@ -11354,18 +11400,6 @@ "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", "license": "MIT" }, - "node_modules/jiti": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", - "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -11373,9 +11407,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -12981,9 +13015,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", + "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" @@ -14026,6 +14060,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -14066,6 +14101,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.25.0" }, @@ -14102,6 +14138,7 @@ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.79.6.tgz", "integrity": "sha512-kvIWSmf4QPfY41HC25TR285N7Fv0Pyn3DAEK8qRL9dA35usSaxsJkHfw+VqnonqJjXOaoKCEanwudRAJ60TBGA==", "license": "MIT", + "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.79.6", @@ -14200,11 +14237,32 @@ "react-native": "*" } }, + "node_modules/react-native-localize": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/react-native-localize/-/react-native-localize-3.6.0.tgz", + "integrity": "sha512-CByn67b9dPDBA7qvAx18PpXhuLqBbdTpedobGOUcjqiGuSAEXZzwas9ziMYSShAR2AvX2Cl7ePErCpj9V39Ctw==", + "license": "MIT", + "peerDependencies": { + "@expo/config-plugins": "*", + "react": "*", + "react-native": "*", + "react-native-macos": "*" + }, + "peerDependenciesMeta": { + "@expo/config-plugins": { + "optional": true + }, + "react-native-macos": { + "optional": true + } + } + }, "node_modules/react-native-reanimated": { "version": "3.17.5", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.17.5.tgz", "integrity": "sha512-SxBK7wQfJ4UoWoJqQnmIC7ZjuNgVb9rcY5Xc67upXAFKftWg0rnkknTw6vgwnjRcvYThrjzUVti66XoZdDJGtw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", @@ -14240,6 +14298,7 @@ "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.1.tgz", "integrity": "sha512-/wJE58HLEAkATzhhX1xSr+fostLsK8Q97EfpfMDKo8jlOc1QKESSX/FQrhk7HhQH/2uSaox4Y86sNaI02kteiA==", "license": "MIT", + "peer": true, "peerDependencies": { "react": "*", "react-native": "*" @@ -14250,6 +14309,7 @@ "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.11.1.tgz", "integrity": "sha512-F0zOzRVa3ptZfLpD0J8ROdo+y1fEPw+VBFq1MTY/iyDu08al7qFUO5hLMd+EYMda5VXGaTFCa8q7bOppUszhJw==", "license": "MIT", + "peer": true, "dependencies": { "react-freeze": "^1.0.0", "react-native-is-edge-to-edge": "^1.1.7", @@ -14260,11 +14320,18 @@ "react-native": "*" } }, + "node_modules/react-native-skia": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/react-native-skia/-/react-native-skia-0.0.1.tgz", + "integrity": "sha512-etuNQDOiDBmncaw17aij6ygh9rb7P3v6Hz+moU5QcmznoeD2tXRepOJO2wSN0PzibVhMNZrBqTyA8Yg5OkHwuA==", + "license": "MIT" + }, "node_modules/react-native-web": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.20.0.tgz", "integrity": "sha512-OOSgrw+aON6R3hRosCau/xVxdLzbjEcsLysYedka0ZON4ZZe6n9xgeN9ZkoejhARM36oTlUgHIQqxGutEJ9Wxg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", @@ -14297,6 +14364,7 @@ "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.13.5.tgz", "integrity": "sha512-MfC2B+woL4Hlj2WCzcb1USySKk+SteXnUKmKktOk/H/AQy5+LuVdkPKm8SknJ0/RxaxhZ48WBoTRGaqgR137hw==", "license": "MIT", + "peer": true, "dependencies": { "escape-string-regexp": "^4.0.0", "invariant": "2.2.4" @@ -14438,6 +14506,21 @@ "async-limiter": "~1.0.0" } }, + "node_modules/react-reconciler": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.31.0.tgz", + "integrity": "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.25.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -15100,6 +15183,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -16191,9 +16275,9 @@ } }, "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -16749,6 +16833,7 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 0c4d73f..28bf6f1 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,9 @@ "dev:ui": "npx expo start --dev-client", "dev:web:server": "electron electron/build/main.js --dev", "install:android:release": "adb install android/app/build/outputs/apk/release/app-release.apk", - "install:android": "adb install android/app/build/outputs/apk/debug/app-debug.apk" + "install:android": "adb install android/app/build/outputs/apk/debug/app-debug.apk", + "android": "expo run:android", + "ios": "expo run:ios" }, "license": "GPL-3.0-only", "workspaces": [ @@ -63,11 +65,14 @@ "react-native": "0.79.6", "react-native-device-info": "^14.0.4", "react-native-gesture-handler": "~2.24.0", - "react-native-reanimated": "~3.17.4", + "react-native-localize": "^3.6.0", + "react-native-reanimated": "^3.17.5", "react-native-safe-area-context": "^5.4.0", "react-native-screens": "~4.11.1", + "react-native-skia": "^0.0.1", "react-native-web": "^0.20.0", - "react-native-webview": "13.13.5" + "react-native-webview": "13.13.5", + "@shopify/react-native-skia": "v2.0.0-next.4" }, "devDependencies": { "@expo/metro-config": "~0.20.0", diff --git a/rpcsx-ui/assets/images/rpcsx_bg.webp b/rpcsx-ui/assets/images/rpcsx_bg.webp new file mode 100644 index 0000000..196cbbd Binary files /dev/null and b/rpcsx-ui/assets/images/rpcsx_bg.webp differ diff --git a/rpcsx-ui/src/app/component.json b/rpcsx-ui/src/app/component.json index 23a4b2c..706beb4 100644 --- a/rpcsx-ui/src/app/component.json +++ b/rpcsx-ui/src/app/component.json @@ -4,12 +4,15 @@ "dependencies": [ { "name": "settings" - }, + } , { "name": "explorer" }, { "name": "fs" + }, + { + "name": "setup" } ] } \ No newline at end of file diff --git a/rpcsx-ui/src/app/server/initialization.ts b/rpcsx-ui/src/app/server/initialization.ts index 0f63648..054be4a 100644 --- a/rpcsx-ui/src/app/server/initialization.ts +++ b/rpcsx-ui/src/app/server/initialization.ts @@ -1,5 +1,6 @@ import * as bridge from '$core/bridge'; import * as explorer from '$explorer'; +import * as setup from '$setup'; import { Window } from '$core/Window'; @@ -9,12 +10,12 @@ const mainWindow: Window = { popView: () => bridge.viewPop(), }; -export function initialize() { - return explorer.pushExplorerView(mainWindow, { - filter: { - type: 'game' - } - }); +export async function initialize() { + if (await setup.setupShouldShow({})) { + return setup.setInitialSetupView(mainWindow, {}); + } else { + return explorer.setExplorerView(mainWindow, { + filter: { type: 'game' }, + }); + } } - - diff --git a/rpcsx-ui/src/app/server/initialization.web.ts b/rpcsx-ui/src/app/server/initialization.web.ts index 4c63566..f5d5b61 100644 --- a/rpcsx-ui/src/app/server/initialization.web.ts +++ b/rpcsx-ui/src/app/server/initialization.web.ts @@ -7,6 +7,7 @@ import * as path from '$core/path'; import { fileURLToPath, pathToFileURL } from 'url'; import { Future } from '$core/Future.js'; import * as explorer from '$explorer'; +import * as setup from '$setup'; import { Window } from '$core/Window'; function toWindow(browserWindow: BrowserWindow): Window { @@ -73,11 +74,13 @@ export async function initialize() { uiInitializedFuture.dispose(); console.log('initialization complete'); - return explorer.pushExplorerView(toWindow(MainWindow), { - filter: { - type: 'game' - } - }); + //if (setup.settings.getShowInitialSetupScreen()) { + return setup.setInitialSetupView(toWindow(MainWindow), {}); + /*} else { + return explorer.setExplorerView(toWindow(MainWindow), { + filter: { type: 'game' }, + }); + }*/ }; app.on('activate', () => { diff --git a/rpcsx-ui/src/core/renderer/FocusableText.tsx b/rpcsx-ui/src/core/renderer/FocusableText.tsx new file mode 100644 index 0000000..3045045 --- /dev/null +++ b/rpcsx-ui/src/core/renderer/FocusableText.tsx @@ -0,0 +1,102 @@ +import React, { useRef } from 'react'; +import { Pressable } from 'react-native'; +import Animated, { + useSharedValue, + useAnimatedStyle, + withSpring, + withTiming, + cancelAnimation, +} from 'react-native-reanimated'; +import { ThemedText } from './ThemedText'; + +const AnimatedText = Animated.createAnimatedComponent(ThemedText); + +export function FocusableText({ + children, + onPress, +}: { + children: React.ReactNode; + onPress?: () => void; +}) { + const scale = useSharedValue(1); + const opacity = useSharedValue(0.85); + + const isFocused = useRef(false); + const isHovered = useRef(false); + const isActive = useRef(false); + const suppressHover = useRef(false); + + const updateState = () => { + const nextActive = + isFocused.current || + (isHovered.current && !suppressHover.current); + + if (isActive.current === nextActive) return; + isActive.current = nextActive; + + cancelAnimation(scale); + cancelAnimation(opacity); + + if (nextActive) { + scale.value = withSpring(1.25, { + damping: 16, + stiffness: 180, + }); + opacity.value = withTiming(1, { duration: 120 }); + } else { + scale.value = withTiming(1, { duration: 180 }); + opacity.value = withTiming(0.85, { duration: 200 }); + } + }; + + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ scale: scale.value }], + opacity: opacity.value, + })); + + return ( + { + suppressHover.current = true; + isFocused.current = false; + isHovered.current = false; + updateState(); + onPress?.(); + setTimeout(() => { + suppressHover.current = false; + }, 120); + }} + onFocus={() => { + isFocused.current = true; + updateState(); + }} + onBlur={() => { + isFocused.current = false; + updateState(); + }} + onHoverIn={() => { + if (suppressHover.current) return; + isHovered.current = true; + updateState(); + }} + onHoverOut={() => { + isHovered.current = false; + updateState(); + }} + > + + {children} + + + ); +} diff --git a/rpcsx-ui/src/core/renderer/LanguageItem.tsx b/rpcsx-ui/src/core/renderer/LanguageItem.tsx new file mode 100644 index 0000000..14be63c --- /dev/null +++ b/rpcsx-ui/src/core/renderer/LanguageItem.tsx @@ -0,0 +1,115 @@ +import React, { memo, useEffect, useState } from 'react'; +import { Pressable } from 'react-native'; +import Animated, { + useAnimatedStyle, + useSharedValue, + withSpring, + withTiming, + interpolateColor, +} from 'react-native-reanimated'; +import { ThemedText } from './ThemedText'; + +const AnimatedPressable = Animated.createAnimatedComponent(Pressable); + +const ITEM_WIDTH = 220; +const ITEM_HEIGHT = 56; +const BORDER_SIZE = 2; + +type Props = { + lang: { id: string; label: string }; + selected: boolean; + onPress: () => void; + primaryColor: string; +}; + +export const LanguageItem = memo(function LanguageItem({ + lang, + selected, + onPress, + primaryColor, +}: Props) { + const [focused, setFocused] = useState(false); + const [hovered, setHovered] = useState(false); + + const progress = useSharedValue(0); + + useEffect(() => { + progress.value = + focused || hovered || selected + ? withSpring(1, { damping: 16, stiffness: 200 }) + : withTiming(0, { duration: 160 }); + }, [focused, hovered, selected]); + + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ scale: 1 + progress.value * 0.06 }], + backgroundColor: interpolateColor( + progress.value, + [0, 1], + ['rgba(0,0,0,0.25)', 'rgba(77,163,255,0.16)'], + ), + })); + + const borderStyle = useAnimatedStyle(() => ({ + opacity: progress.value, + borderColor: primaryColor, + })); + + return ( + setFocused(true)} + onBlur={() => setFocused(false)} + onHoverIn={() => setHovered(true)} + onHoverOut={() => setHovered(false)} + onPress={onPress} + style={{ + width: ITEM_WIDTH, + height: ITEM_HEIGHT, + margin: 8, + alignItems: 'center', + justifyContent: 'center', + }} + > + + {/* Border overlay */} + + + + {lang.label} + + + + ); +}); + diff --git a/rpcsx-ui/src/core/renderer/RPCSXBackground.tsx b/rpcsx-ui/src/core/renderer/RPCSXBackground.tsx new file mode 100644 index 0000000..dfbf7a1 --- /dev/null +++ b/rpcsx-ui/src/core/renderer/RPCSXBackground.tsx @@ -0,0 +1,199 @@ +import React, { useEffect } from "react"; +import { useWindowDimensions, StyleSheet } from "react-native"; +import { useSharedValue, withSequence, withTiming, withRepeat, useDerivedValue, Easing, } from "react-native-reanimated"; +import { Canvas, Fill, Path, Circle, BlurMask, LinearGradient, Skia, vec, Group, interpolateColors } from "@shopify/react-native-skia"; + +const X_ICON_PATH = 'm19.27 5.2773h0.13672l0.023438 0.007812 0.027343 0.027344 0.007813 0.015625v0.050781l-0.007813 0.015625-0.027343 0.042969-0.023438 0.015625-0.0625 0.078125-0.11719 0.12891c-0.019531 0.023438-0.039062 0.042969-0.058593 0.066406-0.046875 0.054688-0.097657 0.11328-0.14844 0.16406-0.058593 0.074219-0.12109 0.14062-0.18359 0.21094-0.011719 0.015626-0.027344 0.035157-0.042969 0.050782-0.074219 0.078125-0.14453 0.15625-0.21484 0.23828-0.023437 0.023437-0.042969 0.050781-0.066406 0.078125-0.046875 0.0625-0.10156 0.125-0.15625 0.17969l-0.050781 0.050781c-0.039063 0.042969-0.074219 0.082031-0.10938 0.125-0.082031 0.10547-0.17187 0.21094-0.26562 0.30859l-0.03125 0.03125-0.007813 0.011719h-0.011719l-0.015625 0.03125c-0.011719 0.011719-0.023437 0.023438-0.035156 0.035157-0.042969 0.046874-0.085937 0.09375-0.125 0.14453-0.035156 0.039062-0.070313 0.074219-0.10938 0.11328-0.039062 0.054688-0.082031 0.10547-0.12891 0.15234-0.074219 0.078125-0.14844 0.16016-0.21484 0.24609l-0.050781 0.058593c-0.015625 0.015625-0.03125 0.035157-0.042969 0.050781-0.097656 0.10156-0.19141 0.20703-0.28125 0.31641l-0.11719 0.12891-0.078124 0.089844c-0.011719 0.011719-0.023438 0.027343-0.035157 0.042969l-0.007812 0.011718-0.09375 0.09375c-0.023438 0.023438-0.039063 0.050782-0.058594 0.074219-0.050781 0.066406-0.10547 0.12891-0.16797 0.1875l-0.085937 0.09375c-0.035157 0.039063-0.070313 0.074219-0.10156 0.11719l-0.070312 0.085938c-0.078125 0.078125-0.15625 0.16406-0.22656 0.25l-0.097656 0.10938c-0.042969 0.050781-0.085937 0.10156-0.13281 0.15234l-0.09375 0.10156-0.019531 0.027344-0.007812 0.015625c-0.050781 0.058594-0.097657 0.11719-0.15234 0.17188l-0.015625 0.015625h-0.011719l-0.007812 0.015625-0.007813 0.011719c-0.015625 0.015625-0.027344 0.03125-0.042968 0.046875-0.007813 0.007812-0.015626 0.015625-0.023438 0.027343l-0.011719 0.007813-0.082031 0.09375c-0.066406 0.082031-0.14062 0.16406-0.21484 0.24609-0.042968 0.050781-0.085937 0.10156-0.12891 0.14844l-0.09375 0.10156-0.023438 0.03125-0.007812 0.011719c-0.046875 0.054687-0.09375 0.10937-0.14453 0.16016-0.050781 0.070312-0.10938 0.13281-0.17188 0.19531l-0.09375 0.10156-0.074219 0.078125-0.078125 0.10156c-0.035156 0.035156-0.070312 0.070313-0.10156 0.10938l-0.085938 0.10156c-0.078125 0.078125-0.14844 0.16016-0.21484 0.24609l-0.050781 0.054687-0.046875 0.050781c-0.042969 0.046875-0.085938 0.09375-0.12891 0.14453l-0.011719 0.007812-0.007813 0.015625-0.007812 0.011719v0.015625l0.015625 0.015625 0.011719 0.019531 0.066406 0.074219 0.09375 0.10156 0.066406 0.078125 0.046875 0.066407c0.050781 0.050781 0.097657 0.10156 0.14062 0.15625 0.046875 0.050781 0.09375 0.10547 0.13672 0.16016 0.046875 0.050781 0.09375 0.10547 0.13672 0.16016 0.046875 0.050781 0.09375 0.10156 0.13672 0.15625l0.085938 0.09375 0.074219 0.082031 0.042969 0.058594 0.078124 0.085937c0.046876 0.050782 0.09375 0.10156 0.13672 0.15625 0.019531 0.023438 0.039063 0.050782 0.058594 0.074219l0.09375 0.10156 0.066406 0.078125 0.050781 0.058593c0.046876 0.050782 0.09375 0.10156 0.13672 0.16016 0.046875 0.046875 0.09375 0.10156 0.13672 0.15625 0.046875 0.050781 0.09375 0.10547 0.13672 0.16016 0.046875 0.050781 0.09375 0.10156 0.13672 0.16016 0.046874 0.050781 0.09375 0.10547 0.13672 0.16406 0.050782 0.050781 0.09375 0.10547 0.13672 0.16016 0.050781 0.046875 0.09375 0.097656 0.13672 0.14844 0.019531 0.027343 0.039062 0.050781 0.058594 0.074218l0.09375 0.10156 0.066406 0.078125 0.050781 0.066406c0.046875 0.050781 0.09375 0.10156 0.13672 0.15625 0.046875 0.050781 0.09375 0.10547 0.13672 0.16016 0.046876 0.050782 0.09375 0.10547 0.13672 0.16016 0.050781 0.050782 0.09375 0.10156 0.13672 0.15625 0.050781 0.050782 0.09375 0.10547 0.13672 0.16016 0.050781 0.050781 0.09375 0.10547 0.13672 0.16016 0.050781 0.050781 0.097656 0.10156 0.14062 0.15625 0.046876 0.050781 0.09375 0.10547 0.13672 0.16016 0.046875 0.050781 0.09375 0.10547 0.13672 0.16016 0.046875 0.050781 0.09375 0.10156 0.13672 0.15625 0.046875 0.050781 0.09375 0.10547 0.13672 0.16016 0.046874 0.050781 0.09375 0.10547 0.13672 0.16016 0.050782 0.050781 0.09375 0.10156 0.13672 0.15625l0.085937 0.09375 0.074219 0.082031 0.042969 0.058594 0.074219 0.085937c0.050781 0.050782 0.097656 0.10547 0.14453 0.16406 0.046875 0.050781 0.09375 0.10547 0.13672 0.16016 0.046875 0.050781 0.09375 0.10156 0.13672 0.16016l0.085937 0.10156c0.011719 0.011719 0.023437 0.023438 0.035156 0.035156l0.015625 0.019532 0.015625 0.03125v0.070312l-0.007812 0.015625-0.015625 0.027344-0.007813 0.015625-0.019531 0.023437-0.03125 0.019531-0.011719 0.007813-0.015625 0.007813h-0.28125-0.67188-0.042969c-0.39844-0.003907-0.79687-0.003907-1.1914 0l-0.03125 0.007812c-0.011718-0.003906-0.027344-0.007812-0.042968-0.007812h-0.019532l-0.035156-0.015626-0.039062-0.027343-0.007813-0.007813-0.011719-0.015625-0.007812-0.015625-0.015625-0.003906c-0.050781-0.058594-0.097657-0.11328-0.14453-0.17578l-0.078126-0.085938-0.10937-0.12109-0.09375-0.10156c-0.007813-0.011719-0.015626-0.019531-0.019532-0.03125l-0.007812-0.011719c-0.027344-0.027344-0.054688-0.054687-0.082032-0.082031-0.019531-0.023437-0.042968-0.050781-0.0625-0.078125l-0.10937-0.12109-0.09375-0.10156c-0.007813-0.011719-0.015626-0.019532-0.023438-0.03125l-0.003906-0.011719c-0.027344-0.027344-0.054688-0.054687-0.082032-0.082031-0.023437-0.023438-0.042968-0.050782-0.0625-0.078125-0.050781-0.0625-0.10156-0.125-0.16016-0.17969l-0.10938-0.125c-0.078125-0.078125-0.15234-0.16016-0.22266-0.24609-0.070313-0.070313-0.13281-0.14453-0.19531-0.22266-0.023437-0.023437-0.042968-0.050781-0.066406-0.078125-0.058594-0.078125-0.125-0.14844-0.19141-0.21875l-0.023438-0.027344-0.007812-0.015624c-0.027344-0.027344-0.050781-0.050782-0.078125-0.078126-0.023437-0.027343-0.046875-0.050781-0.066406-0.078124l-0.10938-0.125-0.09375-0.10156c-0.003906-0.007813-0.011719-0.019531-0.019532-0.027344l-0.007812-0.015625-0.078125-0.078125c-0.023437-0.027343-0.046875-0.050781-0.066406-0.078125l-0.10938-0.125-0.09375-0.10156c-0.007813-0.007813-0.011719-0.019532-0.019532-0.027344l-0.007812-0.015625c-0.027344-0.027344-0.054688-0.054687-0.078125-0.078125-0.023437-0.027344-0.046875-0.054688-0.066406-0.082031-0.050781-0.0625-0.10156-0.12109-0.16016-0.17969l-0.10547-0.12109c-0.082032-0.078126-0.15625-0.16016-0.22656-0.24609-0.074219-0.078125-0.14844-0.16016-0.21484-0.24609-0.074219-0.078125-0.14453-0.16016-0.21094-0.24609-0.066406-0.066406-0.13281-0.14062-0.19531-0.21484-0.023438-0.027343-0.042969-0.050781-0.0625-0.078124-0.046876-0.058594-0.09375-0.11328-0.14453-0.16797l-0.015625-0.007813-0.015625 0.03125-0.0625 0.0625-0.015625 0.023438c-0.054687 0.066406-0.11328 0.13281-0.17188 0.19531l-0.046875 0.050782-0.11328 0.12891c-0.007812 0.007812-0.015625 0.019531-0.023438 0.027344h-0.011718l-0.015625 0.03125-0.042969 0.042968c-0.007812 0.007813-0.015625 0.019532-0.023438 0.027344l-0.011718 0.007812-0.042969 0.058594c-0.019531 0.015625-0.035156 0.03125-0.050781 0.050782-0.10156 0.10156-0.19531 0.21094-0.28125 0.32422-0.019532 0.015625-0.035156 0.035156-0.050782 0.050782-0.019531 0.019531-0.035156 0.042968-0.050781 0.0625-0.019531 0.023437-0.039062 0.046874-0.058593 0.066406-0.082032 0.082031-0.15625 0.16797-0.23047 0.25391-0.019531 0.023438-0.039063 0.046875-0.058594 0.070312-0.054687 0.070313-0.10938 0.13672-0.17188 0.19531l-0.10938 0.125-0.078125 0.085937c-0.015625 0.011719-0.027344 0.027344-0.039062 0.042969l-0.007813 0.015625c-0.054687 0.066406-0.10938 0.12891-0.17188 0.19141-0.011719 0.015626-0.027344 0.03125-0.042969 0.046876-0.011719 0.007812-0.023437 0.023437-0.035156 0.035156l-0.015625 0.019531c-0.046875 0.058594-0.097656 0.11719-0.15234 0.16797-0.007812 0.011718-0.015625 0.023437-0.019531 0.035156l-0.007813 0.007812h-0.015624l-0.007813 0.015626-0.007813 0.011718-0.042968 0.042969c-0.007813 0.011719-0.011719 0.019531-0.019532 0.03125l-0.015624 0.007813-0.042969 0.054687c-0.015625 0.019531-0.035157 0.035156-0.050781 0.050781-0.023438 0.027344-0.042969 0.054688-0.066407 0.082032l-0.042969 0.050781c-0.019531 0.023437-0.042968 0.046875-0.0625 0.070312l-0.09375 0.10938c-0.011718 0.007813-0.019531 0.019532-0.03125 0.027344l-0.007812 0.015625h-0.011719l-0.007812 0.015625-0.007813 0.011719c-0.015625 0.015625-0.027344 0.03125-0.042968 0.042969l-0.074219 0.089843-0.11328 0.12891c-0.019532 0.019531-0.039063 0.042968-0.058594 0.066406l-0.042969 0.046875c-0.023437 0.023437-0.042969 0.050781-0.066406 0.074219l-0.11328 0.12891c-0.019532 0.023437-0.039063 0.042968-0.058594 0.066406l-0.042969 0.050781c-0.023437 0.023438-0.042969 0.046875-0.066406 0.070313-0.050781 0.070312-0.10938 0.13672-0.17188 0.19531l-0.09375 0.10156-0.074219 0.085937-0.10547 0.125c-0.019531 0.019532-0.039062 0.042969-0.058594 0.0625l-0.050781 0.050782c-0.035156 0.042968-0.074219 0.085937-0.10938 0.13281l-0.042968 0.046875c-0.007813 0.011719-0.015626 0.019531-0.019532 0.03125l-0.039062 0.035156-0.011719 0.015625-0.023437 0.019532-0.027344 0.015624-0.039063 0.015626-0.0625 0.007812h-0.36328-0.12109-0.058594-0.63281-0.37891-0.011719c-0.14844-0.003906-0.30078-0.003906-0.44922 0h-0.019531l-0.042969-0.007812h-0.023437l-0.035157-0.015626-0.023437-0.015624-0.019531-0.019532-0.023438-0.035156-0.007812-0.015625v-0.09375l0.007812-0.015625c0.023438-0.03125 0.050782-0.0625 0.078125-0.09375l0.007813-0.015625c0.046875-0.042969 0.089844-0.09375 0.13281-0.14453l0.046875-0.0625c0.042969-0.054688 0.085938-0.10547 0.13281-0.15234 0.019531-0.023438 0.042968-0.046875 0.0625-0.070312 0.019531-0.027344 0.039062-0.050782 0.058594-0.074219l0.11719-0.13672 0.078125-0.085938 0.042969-0.058593c0.011719-0.011719 0.023437-0.023438 0.035156-0.035157 0.011719-0.011718 0.019531-0.023437 0.03125-0.039062l0.015625-0.019531h0.011719l0.015625-0.027344 0.015625-0.023437h0.011719l0.015624-0.027344c0.007813-0.015625 0.019532-0.027344 0.027344-0.039063l0.066406-0.070312c0.039063-0.050781 0.082032-0.097657 0.12891-0.14453 0.042969-0.054688 0.085937-0.10547 0.13281-0.15234 0.019531-0.027344 0.039063-0.054688 0.0625-0.078125 0.019531-0.023438 0.039063-0.046875 0.058594-0.074219l0.11719-0.13672 0.054688-0.0625 0.039062-0.039062 0.050782-0.0625 0.019531-0.023438 0.042969-0.050781 0.015625-0.015625 0.015625-0.027344c0.050781-0.058593 0.10547-0.12109 0.15625-0.17969l0.085937-0.10156c0.050781-0.054687 0.097657-0.10938 0.14453-0.16797 0.023438-0.027343 0.050782-0.054687 0.074219-0.085937l0.078125-0.085937c0.11719-0.14063 0.23828-0.28125 0.36328-0.41797l0.0625-0.078125 0.050781-0.058594c0.023437-0.023438 0.042969-0.046875 0.066406-0.074219 0.019531-0.027343 0.042969-0.050781 0.0625-0.078125 0.023437-0.023437 0.039063-0.046875 0.058594-0.074218l0.11719-0.13672 0.058594-0.0625 0.011719-0.015625 0.023437-0.023438 0.050782-0.0625 0.019531-0.023437 0.32031-0.36719 0.011719-0.027343c0.054688-0.0625 0.11328-0.12109 0.16797-0.18359 0.035156-0.042969 0.070312-0.089844 0.10938-0.13672l0.054687-0.0625 0.039063-0.039063 0.21484-0.25 0.015624-0.03125c0.16406-0.19531 0.33203-0.38672 0.50391-0.57812l0.066406-0.078126 0.050781-0.058593c0.019532-0.023438 0.042969-0.046875 0.0625-0.070313 0.023438-0.027344 0.042969-0.054687 0.066406-0.078125 0.019532-0.023437 0.039063-0.050781 0.058594-0.074219l0.11328-0.13672 0.066406-0.074219 0.007812-0.003906c0.039063-0.042969 0.078125-0.089844 0.11328-0.13281 0.007812-0.007812 0.015624-0.019531 0.023437-0.027344l0.007813-0.015624 0.015624-0.007813 0.003907-0.011719v-0.015625l-0.019531-0.035156c-0.023438-0.023437-0.042969-0.046875-0.066407-0.066406-0.0625-0.074219-0.12891-0.14844-0.20312-0.21484-0.023437-0.03125-0.046875-0.058594-0.070312-0.089844l-0.078125-0.085937c-0.019531-0.019532-0.035157-0.039063-0.050781-0.054688l-0.066407-0.066406c-0.007812-0.015625-0.019531-0.03125-0.027343-0.042969l-0.074219-0.082031-0.078125-0.085938c-0.023438-0.023437-0.042969-0.046874-0.066406-0.070312l-0.058594-0.058594c-0.023438-0.027344-0.046875-0.058594-0.070313-0.085937l-0.078125-0.085938c-0.019531-0.019531-0.035156-0.039062-0.050781-0.058593l-0.066406-0.066407c-0.007813-0.011719-0.019531-0.027343-0.027344-0.042969l-0.074219-0.078124-0.078125-0.085938c-0.023437-0.023438-0.042969-0.050781-0.066406-0.074219l-0.058594-0.058593c-0.023437-0.027344-0.046875-0.054688-0.070312-0.085938l-0.078125-0.085938c-0.019532-0.019531-0.035156-0.039062-0.050782-0.058593l-0.066406-0.0625c-0.007812-0.015625-0.019531-0.03125-0.027344-0.046875l-0.074218-0.078125-0.078125-0.085938c-0.023438-0.023437-0.042969-0.046875-0.066407-0.074219l-0.058593-0.054687c-0.023438-0.03125-0.046875-0.058594-0.070313-0.085937l-0.078125-0.089844c-0.019531-0.019532-0.035156-0.039063-0.050781-0.054688l-0.13281-0.14062c-0.007813-0.011719-0.019532-0.027343-0.027344-0.042969l-0.074219-0.078124-0.078125-0.085938c-0.023438-0.023438-0.042969-0.046875-0.066406-0.074219-0.035156-0.039062-0.070313-0.082031-0.10547-0.12109l-0.082031-0.085938c-0.019532-0.023438-0.042969-0.050781-0.0625-0.074219l-0.058594-0.058593c-0.023438-0.027344-0.046875-0.054688-0.070312-0.085938l-0.082032-0.085938c-0.015625-0.019531-0.03125-0.039062-0.050781-0.058593l-0.0625-0.0625c-0.011719-0.015625-0.019531-0.03125-0.03125-0.042969l-0.070313-0.082031-0.082031-0.085938c-0.019531-0.023437-0.042969-0.046875-0.0625-0.070312l-0.058593-0.058594c-0.023438-0.027344-0.046876-0.058594-0.074219-0.085937l-0.078125-0.089844c-0.015625-0.015625-0.03125-0.035156-0.050782-0.054688l-0.0625-0.066406c-0.011718-0.015625-0.019531-0.027344-0.03125-0.042969l-0.070312-0.078125-0.082031-0.089844c-0.019531-0.023437-0.042969-0.046874-0.0625-0.070312l-0.058594-0.058594c-0.023437-0.027344-0.046875-0.058594-0.074219-0.085937-0.011718-0.015625-0.027344-0.035157-0.042968-0.050781l-0.019532-0.015626c-0.011718-0.011718-0.019531-0.027343-0.03125-0.042968l-0.046875-0.050782-0.023437-0.027343v-0.015625l-0.007813-0.027344v-0.050781l0.015625-0.03125 0.015625-0.027344 0.019531-0.019531 0.027344-0.023438 0.039063-0.015625h0.41016 0.17969 1.1406 0.15234 0.16016 0.09375c0.042968 0.011719 0.085937 0.023438 0.12891 0.039063l0.074218 0.035156c0.019532 0.015625 0.039063 0.03125 0.054688 0.050781l0.074219 0.0625c0.015625 0.03125 0.042969 0.050781 0.0625 0.074219l0.066406 0.070312 0.085937 0.089844 0.085938 0.09375 0.074219 0.0625 0.085937 0.10156 0.074219 0.066406 0.085937 0.10156 0.070313 0.0625 0.085937 0.10156 0.074219 0.066406 0.085938 0.10156 0.074219 0.0625 0.085937 0.10156 0.070313 0.066407 0.085937 0.10156 0.074219 0.0625 0.085937 0.10156 0.078125 0.078126 0.066406 0.074218 0.085938 0.085938 0.09375 0.10156 0.066406 0.066406 0.070313 0.078126 0.082031 0.078124 0.09375 0.10156 0.0625 0.066406 0.074219 0.078125 0.078125 0.078125 0.09375 0.10156 0.074218 0.066406 0.085938 0.10156 0.070312 0.066407 0.089844 0.097656 0.070313 0.066406 0.078125 0.085938 0.050781 0.058593 0.066406 0.066407 0.015625 0.011719 0.011719 0.015624 0.007813 0.015626h0.015624l0.015626-0.007813c0.011718-0.015625 0.027343-0.027344 0.042968-0.042969l0.019532-0.023437 0.007812-0.011719 0.058594-0.058594c0.10938-0.11719 0.21484-0.23438 0.31641-0.35547 0.17578-0.18359 0.34766-0.37109 0.51172-0.5625 0.125-0.14453 0.25391-0.28516 0.38281-0.41797 0.082031-0.089844 0.16406-0.17578 0.24609-0.26172 0.023437-0.023438 0.042969-0.046875 0.066406-0.070312l0.085938-0.09375c0.023437-0.023438 0.042969-0.050782 0.066406-0.074219 0.10547-0.12109 0.21094-0.24219 0.32422-0.35938 0.050781-0.054688 0.10156-0.10547 0.15234-0.16016l0.070312-0.085938c0.015625-0.015625 0.03125-0.035156 0.042969-0.050781 0.0625-0.074219 0.12891-0.14453 0.19531-0.21094 0.015625-0.015625 0.035157-0.035156 0.050781-0.054687l0.015626-0.007813 0.035156-0.050781 0.16797-0.17188c0.015626-0.023437 0.03125-0.042969 0.046876-0.058594l0.058593-0.066406 0.074219-0.078125c0.007812-0.011719 0.019531-0.019531 0.027344-0.03125l0.007812-0.011719h0.015625l0.011719-0.03125 0.023438-0.019531 0.050781-0.058594c0.046875-0.054687 0.09375-0.10938 0.14453-0.16016l0.10938-0.10547 0.035156-0.046875 0.11328-0.097656 0.066407-0.058594 0.10938-0.10156c0.019531-0.015625 0.039062-0.027344 0.058594-0.042969l0.14844-0.125 0.015625-0.011719 0.40625-0.27734 0.011719-0.011718c0.44141-0.26172 0.91406-0.46484 1.4102-0.60156 0.12891-0.042969 0.25781-0.070312 0.39062-0.09375l0.019531-0.007812c0.16016-0.027344 0.3125-0.042969 0.47656-0.054688h0.015625c0.11328-0.015625 0.23828-0.023437 0.35156-0.023437z'; + +function round4(value: number) { + 'worklet' + return Math.round(value * 10000) / 10000; +} + +function round3(value: number) { + 'worklet' + return Math.round(value * 1000) / 1000; +} + +export const RPCSXBackground = () => { + const progressValue = useSharedValue(0); + const { width, height } = useWindowDimensions(); + + useEffect(() => { + progressValue.value = withSequence( + withTiming(1, { duration: 6000, easing: Easing.bezier(0, 0.4, 0.2, 1.0) }), + withRepeat( + withSequence( + withTiming(0, { duration: 0 }), + withTiming(1, { duration: 120000, easing: Easing.linear }), + ), + -1, + true + )); + }, []); + + const progress = useDerivedValue(() => round4(progressValue.value), [progressValue]) + + const particleCount = 30; + const particles = React.useMemo(() => + Array.from({ length: particleCount }).map((_, i) => { + const angle = (i / particleCount) * Math.PI * 2; + const radius = Math.random() * (width / 2) + (height / 3); + return { + id: i, + startX: width / 2 + Math.cos(angle) * radius, + startY: height / 2 + Math.sin(angle) * radius, + endX: width / 2, + endY: height / 2, + size: Math.random() * 10 + 2, + delay: Math.random(), + orbitRadius: Math.random() * 150 + 50, + orbitSpeed: Math.random() * 2 + 1, + }; + }), [width, height, particleCount] + ); + + const waveCount = 2; + const waves = React.useMemo(() => + Array.from({ length: waveCount }).map((_, i) => ({ + id: i, + amplitude: 40 + i * 15, + frequency: 0.008 - i * 0.002, + phase: i * Math.PI / 3, + opacity: 0.4 - i * 0.03, + })), [waveCount] + ); + + const crossPath = React.useMemo(() => { + const path = Skia.Path.MakeFromSVGString(X_ICON_PATH); + if (!path) return null; + + const targetSize = Math.min(width, height) * 0.9; + const scale = targetSize / 24; + path.transform(Skia.Matrix().scale(scale, scale)); + const bounds = path.getBounds(); + path.transform(Skia.Matrix().translate( + width / 2 - (bounds.x + bounds.width / 2), + height / 2 - (bounds.y + bounds.height / 2), + )); + return path; + }, [width, height]); + + return ( + + + + + + + {waves.map((wave) => { + const animatedPath = useDerivedValue(() => { + const path = Skia.Path.Make(); + + const phase = progress.value * Math.PI * 2 + wave.phase; + let i = -100; + const y = height / 2; + path.moveTo(i, y + Math.sin(i * wave.frequency + phase) * wave.amplitude); + + i += 30; + + for (; i <= width + 100; i += 30) { + path.lineTo(i, y + Math.sin(i * wave.frequency + phase) * wave.amplitude); + } + return path; + }, [width, height, progress]); + + return ( + + + + ); + })} + + + {particles.map((p) => { + const animatedT = useDerivedValue(() => { + return round4((progress.value + p.delay) % 1); + }, [progress]); + + const animatedOrbitAngle = useDerivedValue(() => { + return round3(animatedT.value * Math.PI * 2 * p.orbitSpeed); + }, [animatedT]); + + const animatedOrbitAngleSin = useDerivedValue(() => { + return round3(Math.sin(animatedOrbitAngle.value)); + }, [animatedOrbitAngle]); + + const animatedOrbitRadius = useDerivedValue(() => { + return round3(p.orbitRadius * (1 - animatedT.value)); + }, [animatedT]); + + const animatedX = useDerivedValue(() => { + const baseX = p.startX + (p.endX - p.startX) * animatedT.value; + return baseX + Math.cos(animatedOrbitAngle.value) * animatedOrbitRadius.value; + }, [animatedOrbitAngle, animatedT, animatedOrbitRadius]); + + const animatedY = useDerivedValue(() => { + const baseY = p.startY + (p.endY - p.startY) * animatedT.value; + return baseY + animatedOrbitAngleSin.value * animatedOrbitRadius.value; + }, [animatedOrbitAngleSin, animatedT]); + + const animatedSize = useDerivedValue(() => { + return animatedOrbitAngleSin.value * p.size * (1 - animatedT.value) + 4; + }, [animatedOrbitAngleSin, animatedT]); + + const animatedOpacity = useDerivedValue(() => { + // Fade in and out + if (animatedT.value < 0.1) return animatedT.value * 10; + if (animatedT.value > 0.9) return (1 - animatedT.value) * 10; + return 1; + }, [animatedT]); + + const animatedBlur = useDerivedValue(() => { + return (1 - animatedT.value) * 6; + }, [animatedT]); + + const animatedColor = useDerivedValue(() => { + return interpolateColors( + (0.5 - (animatedT.value * p.orbitSpeed) % 1), + [-0.5, 0.5], + ["#12a0f3b8", "#fff"] + ); + }, [animatedT]); + + + return ( + + ); + })} + + {crossPath && ( + + + + )} + + ); +}; \ No newline at end of file diff --git a/rpcsx-ui/src/core/renderer/main.tsx b/rpcsx-ui/src/core/renderer/main.tsx index 2019969..5eae306 100644 --- a/rpcsx-ui/src/core/renderer/main.tsx +++ b/rpcsx-ui/src/core/renderer/main.tsx @@ -67,6 +67,7 @@ export function main( function App() { const [renderItem, setRenderItem] = useState(viewStack.length - 1); + const [updateId, setUpdateId] = useState(0); useEffect(() => { console.log("app entered"); @@ -85,6 +86,7 @@ function App() { setRenderItem(item); } } + setUpdateId(updateId + 1); }; }); @@ -103,7 +105,7 @@ function App() { return ( - + ) } diff --git a/rpcsx-ui/src/core/renderer/useThemeColor.ts b/rpcsx-ui/src/core/renderer/useThemeColor.ts index c296f4e..66fb9a6 100644 --- a/rpcsx-ui/src/core/renderer/useThemeColor.ts +++ b/rpcsx-ui/src/core/renderer/useThemeColor.ts @@ -7,6 +7,24 @@ export function useThemeColor( return Colors[useColorScheme()][colorName]; } +export function withAlpha( + color: string, + alpha: number +) { + if (color.startsWith('#')) { + const r = parseInt(color.slice(1, 3), 16); + const g = parseInt(color.slice(3, 5), 16); + const b = parseInt(color.slice(5, 7), 16); + return `rgba(${r}, ${g}, ${b}, ${alpha})`; + } + + if (color.startsWith('rgb(')) { + return color.replace('rgb(', 'rgba(').replace(')', `, ${alpha})`); + } + + return color; +} + export function useThemeColorOr( props: { light?: string; dark?: string }, colorName: keyof typeof Colors.light & keyof typeof Colors.dark diff --git a/rpcsx-ui/src/setup/component.json b/rpcsx-ui/src/setup/component.json new file mode 100644 index 0000000..69ce243 --- /dev/null +++ b/rpcsx-ui/src/setup/component.json @@ -0,0 +1,37 @@ +{ + "name": "setup", + "version": "0.1.0", + "contributions": { + "settings": { + "show-initial-setup-screen": { + "type": "boolean", + "defaultValue": true + } + }, + "methods": { + "should-show": { + "handler": "handleShouldShow", + "params": {}, + "returns": { + "value": { + "type": "boolean" + } + } + }, + "set-show-initial-setup-screen": { + "handler": "setShowInitialSetupScreen", + "params": { + "value": { + "type": "boolean" + } + }, + "returns": {} + } + } + }, + "dependencies": [ + { + "name": "explorer" + } + ] +} \ No newline at end of file diff --git a/rpcsx-ui/src/setup/renderer/views/InitialSetup.tsx b/rpcsx-ui/src/setup/renderer/views/InitialSetup.tsx new file mode 100644 index 0000000..c9592d8 --- /dev/null +++ b/rpcsx-ui/src/setup/renderer/views/InitialSetup.tsx @@ -0,0 +1,317 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { + ScrollView, + View, + Text, +} from 'react-native'; +import Animated, { + FadeOut, + SlideInRight, +} from 'react-native-reanimated'; +import * as explorer from '$explorer'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import * as self from '$'; +import { useWindowDimensions } from 'react-native'; +import { ThemedText } from '$core/ThemedText'; +import { ThemedView } from '$core/ThemedView'; +import { LanguageItem } from '$core/LanguageItem'; +import { FocusableText } from '$core/FocusableText'; +import { useThemeColor, withAlpha } from '$core/useThemeColor' +import { RPCSXBackground } from '$core/RPCSXBackground'; +import * as RNLocalize from 'react-native-localize'; + +export function InitialSetup() { + const { width, height } = useWindowDimensions(); + const scrollRef = useRef(null); + const [step, setStep] = useState(0); + const insets = useSafeAreaInsets(); + const primaryColor = useThemeColor("primary"); + const secondaryColor = useThemeColor("secondary"); + + const LANGUAGES = [ + { id: 'en', label: 'English' }, + { id: 'ja', label: '日本語' }, + { id: 'fr', label: 'Français' }, + { id: 'de', label: 'Deutsch' }, + { id: 'es', label: 'Español' }, + { id: 'it', label: 'Italiano' }, + ]; + + const [language, setLanguage] = useState(null); + + const handleSelect = useCallback((id: string) => { + setLanguage(id); + }, []); + + useEffect(() => { + if (language !== null) return; + + const availableIds = LANGUAGES.map(l => l.id); + + const locales = RNLocalize.getLocales(); + + if (!locales.length) { + setLanguage('en'); + return; + } + + const deviceLang = locales[0].languageCode.toLowerCase(); + + const matched = availableIds.includes(deviceLang) + ? deviceLang + : 'en'; + + setLanguage(matched); + }, []); + + useEffect(() => { + scrollRef.current?.scrollTo({ + x: step * width, + animated: false, + }); + }, [width]); + + const steps = [ + { + title: 'Welcome to RPCSX', + subtitle: "Let's get things set up", + content: 'RPCSX will help you configure everything.', + confirmText: 'Start (X)', + }, + { + title: 'Choose Language', + subtitle: 'Select your preferred language', + content: 'You can change this later in settings.', + confirmText: 'Select (X)', + }, + { + title: 'Scan Games', + subtitle: 'Find your games', + content: 'We will scan your folders for supported games.', + confirmText: 'Scan (X)', + }, + { + title: 'Ready to Go', + subtitle: 'Setup complete', + content: 'You\'re all set. Enjoy playing!', + confirmText: 'Finish (X)', + }, + ]; + + const goToStep = (index: number) => { + scrollRef.current?.scrollTo({ + x: index * width, + animated: true, + }); + setStep(index); + }; + + const next = () => { + if (step < steps.length - 1) { + goToStep(step + 1); + } else { + self.setupSetShowInitialSetupScreen({ value: false }); + explorer.setExplorerView({ + filter: { type: 'game' }, + }); + } + }; + + const back = () => { + if (step > 0) { + goToStep(step - 1); + } + }; + + return ( + + + + {/* Pager */} + + {steps.map((s, index) => + index === 1 ? ( + + + {s.title} + + + + {s.content} + + + {/* Language Grid */} + + {LANGUAGES.map(lang => ( + handleSelect(lang.id)} + /> + ))} + + + ) : ( + + + {s.title} + + + + {s.subtitle} + + + ))} + + + + + {steps[step].content} + + + + {/* Step indicators */} + + {steps.map((_, i) => ( + + ))} + + + {/* Bottom buttons */} + + {step > 0 && ( + + + Back (O) + + + )} + + + + {steps[step].confirmText} + + + + + ); +} + diff --git a/rpcsx-ui/src/setup/server/main.ts b/rpcsx-ui/src/setup/server/main.ts new file mode 100644 index 0000000..afd0b3c --- /dev/null +++ b/rpcsx-ui/src/setup/server/main.ts @@ -0,0 +1,9 @@ +import * as self from '$'; + +export async function setShowInitialSetupScreen(caller: ComponentRef, value: boolean) { + // TODO(DH): Implement this function +} + +export async function handleShouldShow() { + return (await self.settings.getShowInitialSetupScreen()).value; +}