Initial setup view (#32)

* test

* Initial setup view

* exp

* exp2

* proper setup screen ui
This commit is contained in:
Ishan09811
2025-12-28 23:21:03 +05:30
committed by GitHub
parent f4158a1ceb
commit 88e5dc02f9
15 changed files with 1072 additions and 124 deletions

View File

@@ -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 <a href="https://developer.android.com/reference/android/app/Activity#onBackPressed()">onBackPressed</a>
*/
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()
}
}

273
package-lock.json generated
View File

@@ -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"

View File

@@ -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",

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 KiB

View File

@@ -4,12 +4,15 @@
"dependencies": [
{
"name": "settings"
},
} ,
{
"name": "explorer"
},
{
"name": "fs"
},
{
"name": "setup"
}
]
}

View File

@@ -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' },
});
}
}

View File

@@ -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', () => {

View File

@@ -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 (
<Pressable
focusable
onPress={() => {
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();
}}
>
<AnimatedText
style={[
{
fontSize: 16,
fontWeight: '500',
color: '#fff',
},
animatedStyle,
]}
>
{children}
</AnimatedText>
</Pressable>
);
}

View File

@@ -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 (
<AnimatedPressable
focusable
onFocus={() => 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',
}}
>
<Animated.View
style={[
{
width: ITEM_WIDTH,
height: ITEM_HEIGHT,
borderRadius: 10,
alignItems: 'center',
justifyContent: 'center',
},
animatedStyle,
]}
>
{/* Border overlay */}
<Animated.View
pointerEvents="none"
style={[
{
position: 'absolute',
inset: 0,
borderRadius: 10,
borderWidth: BORDER_SIZE,
},
borderStyle,
]}
/>
<ThemedText
style={{
fontSize: 18,
letterSpacing: 0.3,
color:
selected || focused || hovered
? primaryColor
: '#fff',
}}
>
{lang.label}
</ThemedText>
</Animated.View>
</AnimatedPressable>
);
});

File diff suppressed because one or more lines are too long

View File

@@ -67,6 +67,7 @@ export function main(
function App() {
const [renderItem, setRenderItem] = useState<number>(viewStack.length - 1);
const [updateId, setUpdateId] = useState<number>(0);
useEffect(() => {
console.log("app entered");
@@ -85,6 +86,7 @@ function App() {
setRenderItem(item);
}
}
setUpdateId(updateId + 1);
};
});
@@ -103,7 +105,7 @@ function App() {
return (
<SafeAreaProvider>
<TopViewSelector stack={viewStack} index={viewStack.length - 1} />
<TopViewSelector stack={viewStack} index={viewStack.length - 1} key={updateId} />
</SafeAreaProvider>
)
}

View File

@@ -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

View File

@@ -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"
}
]
}

View File

@@ -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<ScrollView>(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<string | null>(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 (
<View
style={{
flex: 1,
}}
>
<RPCSXBackground />
{/* Pager */}
<ScrollView
ref={scrollRef}
horizontal
pagingEnabled
scrollEnabled={false}
showsHorizontalScrollIndicator={false}
>
{steps.map((s, index) =>
index === 1 ? (
<ThemedView
style={{
width,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'transparent',
padding: 24,
}}
>
<ThemedText
style={{
color: '#fff',
fontSize: 36,
fontWeight: '500',
letterSpacing: 0.4,
marginBottom: 8,
}}
>
{s.title}
</ThemedText>
<ThemedText
style={{
fontSize: 14,
marginBottom: 32,
color: withAlpha(secondaryColor, 0.75),
}}
>
{s.content}
</ThemedText>
{/* Language Grid */}
<ThemedView
style={{
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
maxWidth: 520,
backgroundColor: 'transparent',
}}
>
{LANGUAGES.map(lang => (
<LanguageItem
key={lang.id}
lang={lang}
selected={language === lang.id}
primaryColor={primaryColor}
onPress={() => handleSelect(lang.id)}
/>
))}
</ThemedView>
</ThemedView>
) : (
<Animated.View
key={index}
entering={SlideInRight.duration(500)}
exiting={FadeOut.duration(200)}
style={{
width,
padding: 24,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text
style={{
color: '#fff',
fontSize: 34,
fontWeight: '500',
textAlign: 'center',
letterSpacing: 0.4,
textShadowColor: 'rgba(120,180,255,0.45)',
textShadowOffset: { width: 0, height: 0 },
textShadowRadius: 10,
}}
>
{s.title}
</Text>
<ThemedText
style={{
marginTop: 0,
fontSize: 14,
color: secondaryColor,
textAlign: 'center',
textShadowColor: 'rgba(23, 25, 27, 0.27)',
textShadowOffset: { width: 0, height: 0 },
textShadowRadius: 10,
}}
>
{s.subtitle}
</ThemedText>
</Animated.View>
))}
</ScrollView>
<ThemedView
style={{
flexDirection: 'row',
justifyContent: 'center',
padding: 16,
paddingBottom: insets.bottom + 16,
gap: 12,
backgroundColor: 'transparent',
}}
>
<ThemedText
style={{
marginTop: 0,
fontSize: 16,
textAlign: 'center',
maxWidth: 420,
color: withAlpha(secondaryColor, 0.95),
lineHeight: 22,
backgroundColor: 'rgba(0, 0, 0, 0.4)',
}}
>
{steps[step].content}
</ThemedText>
</ThemedView>
{/* Step indicators */}
<ThemedView
style={{
flexDirection: 'row',
justifyContent: 'center',
marginBottom: 16,
backgroundColor: 'transparent',
}}
>
{steps.map((_, i) => (
<ThemedView
key={i}
style={{
width: 26,
height: 2,
marginHorizontal: 4,
backgroundColor:
i === step ? '#4da3ff' : 'rgba(255,255,255,0.25)',
}}
/>
))}
</ThemedView>
{/* Bottom buttons */}
<ThemedView
style={{
flexDirection: 'row',
padding: 16,
paddingBottom: insets.bottom + 16,
gap: 12,
backgroundColor: 'transparent',
}}
>
{step > 0 && (
<ThemedView style={{ flex: 1, backgroundColor: 'transparent', alignItems: 'center' }}>
<FocusableText onPress={back}>
Back (O)
</FocusableText>
</ThemedView>
)}
<ThemedView style={{ flex: 1, backgroundColor: 'transparent', alignItems: 'center' }}>
<FocusableText
onPress={next}
>
{steps[step].confirmText}
</FocusableText>
</ThemedView>
</ThemedView>
</View>
);
}

View File

@@ -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;
}