mirror of
https://github.com/aliasvault/aliasvault.git
synced 2025-12-23 22:28:22 -05:00
Merge branch 'main' of https://github.com/aliasvault/aliasvault
* 'main' of https://github.com/aliasvault/aliasvault: New Crowdin updates (#1397) Tweak native QR code scanner to only react on AliasVault prefixes (#1405) Add native iOS QR code scanner (#1405) Update net.aliasvault.app.yml.template (#1405) Add native Android QR code scanner ZXing implementation (#1405) Update run.sh to generate net.aliasvault.app.yml with latest version and branch for proper F-Droid build (#1405) Update package.json (#1405) Update F-Droid local build scripts (#1405) Replace expo-camera which uses non-FOSS libs with react-native-vision-camera (#1405) Add expo-camera to scanignore to prevent it being deleted by F-Droid (#1405) Add sign-apk.sh helper script (#1405) Update F-Droid local build flow to capture APK outputs (#1405)
This commit is contained in:
@@ -57,7 +57,7 @@
|
||||
"next": "Seuraava",
|
||||
"use": "Käytä",
|
||||
"delete": "Poista",
|
||||
"save": "Save",
|
||||
"save": "Tallenna",
|
||||
"or": "Tai",
|
||||
"close": "Sulje",
|
||||
"copied": "Kopioitu!",
|
||||
@@ -242,13 +242,13 @@
|
||||
"enterEmailPrefix": "Syötä sähköpostin etuliite"
|
||||
},
|
||||
"totp": {
|
||||
"addCode": "Add 2FA Code",
|
||||
"instructions": "Enter the secret key shown by the website where you want to add two-factor authentication.",
|
||||
"nameOptional": "Name (optional)",
|
||||
"secretKey": "Secret Key",
|
||||
"saveToViewCode": "Save to view code",
|
||||
"addCode": "Lisää 2FA TOTP -koodi",
|
||||
"instructions": "Syötä salainen avain, joka näkyy sivustossa, jossa haluat lisätä kaksivaiheisen tunnistautumisen",
|
||||
"nameOptional": "Nimi (valinnainen)",
|
||||
"secretKey": "Salainen avain",
|
||||
"saveToViewCode": "Tallenna nähdäksesi koodin",
|
||||
"errors": {
|
||||
"invalidSecretKey": "Invalid secret key format."
|
||||
"invalidSecretKey": "Virheellinen salatun avaimen muoto."
|
||||
}
|
||||
},
|
||||
"emails": {
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"next": "Dalej",
|
||||
"use": "Użyj",
|
||||
"delete": "Usuń",
|
||||
"save": "Save",
|
||||
"save": "Zapisz",
|
||||
"or": "lub",
|
||||
"close": "Zamknąć",
|
||||
"copied": "Skopiowano",
|
||||
@@ -238,22 +238,22 @@
|
||||
"publicEmailDescription": "Anonimowa, ale ograniczona prywatność. Treści e-mail są czytelne dla każdego, kto zna adres.",
|
||||
"useDomainChooser": "Użyj wybierania domen",
|
||||
"enterCustomDomain": "Wprowadź własną domenę",
|
||||
"enterFullEmail": "Wprowadź pełny adres e-mail",
|
||||
"enterFullEmail": "Wprowadź adres e-mail",
|
||||
"enterEmailPrefix": "Wprowadź prefiks e-mail"
|
||||
},
|
||||
"totp": {
|
||||
"addCode": "Add 2FA Code",
|
||||
"instructions": "Enter the secret key shown by the website where you want to add two-factor authentication.",
|
||||
"nameOptional": "Name (optional)",
|
||||
"secretKey": "Secret Key",
|
||||
"saveToViewCode": "Save to view code",
|
||||
"addCode": "Dodaj kod 2FA",
|
||||
"instructions": "Wprowadź tajny klucz wyświetlony na stronie internetowej, na której chcesz dodać uwierzytelnianie dwuskładnikowe.",
|
||||
"nameOptional": "Nazwa (opcjonalnie)",
|
||||
"secretKey": "Tajny klucz",
|
||||
"saveToViewCode": "Zapisz, aby wyświetlić kod",
|
||||
"errors": {
|
||||
"invalidSecretKey": "Invalid secret key format."
|
||||
"invalidSecretKey": "Nieprawidłowy format tajnego klucza."
|
||||
}
|
||||
},
|
||||
"emails": {
|
||||
"title": "Skrzynka odbiorcza",
|
||||
"deleteEmailTitle": "Usuń adres e-mail",
|
||||
"deleteEmailTitle": "Usuń e-mail",
|
||||
"deleteEmailConfirm": "Czy na pewno chcesz trwale usunąć ten e-mail?",
|
||||
"from": "Od",
|
||||
"to": "Do",
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"next": "Далее",
|
||||
"use": "Использовать",
|
||||
"delete": "Удалить",
|
||||
"save": "Save",
|
||||
"save": "Сохранить",
|
||||
"or": "Или",
|
||||
"close": "Закрыть",
|
||||
"copied": "Скопировано!",
|
||||
@@ -242,13 +242,13 @@
|
||||
"enterEmailPrefix": "Введите префикс электронной почты"
|
||||
},
|
||||
"totp": {
|
||||
"addCode": "Add 2FA Code",
|
||||
"instructions": "Enter the secret key shown by the website where you want to add two-factor authentication.",
|
||||
"nameOptional": "Name (optional)",
|
||||
"secretKey": "Secret Key",
|
||||
"saveToViewCode": "Save to view code",
|
||||
"addCode": "Добавить код 2FA",
|
||||
"instructions": "Введите секретный ключ, указанный на веб-сайте, где вы хотите добавить двухфакторную аутентификацию.",
|
||||
"nameOptional": "Имя (необязательно)",
|
||||
"secretKey": "Секретный ключ",
|
||||
"saveToViewCode": "Сохранить для просмотра кода",
|
||||
"errors": {
|
||||
"invalidSecretKey": "Invalid secret key format."
|
||||
"invalidSecretKey": "Неверный формат секретного ключа."
|
||||
}
|
||||
},
|
||||
"emails": {
|
||||
|
||||
@@ -202,6 +202,9 @@ dependencies {
|
||||
// Add modern SQLite library with VACUUM INTO and backup API support
|
||||
implementation("com.github.requery:sqlite-android:3.49.0")
|
||||
|
||||
// Add ZXing library for QR code scanning (F-Droid compatible)
|
||||
implementation("com.journeyapps:zxing-android-embedded:4.3.0")
|
||||
|
||||
// Test dependencies
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.mockito:mockito-core:4.0.0'
|
||||
|
||||
@@ -58,6 +58,13 @@
|
||||
android:theme="@style/PasskeyRegistrationTheme"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<!-- QR Scanner Activity -->
|
||||
<activity
|
||||
android:name=".qrscanner.QRScannerActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/zxing_CaptureTheme"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
@@ -113,6 +113,8 @@ class MainActivity : ReactActivity() {
|
||||
handlePinUnlockResult(resultCode, data)
|
||||
} else if (requestCode == net.aliasvault.app.nativevaultmanager.NativeVaultManager.PIN_SETUP_REQUEST_CODE) {
|
||||
handlePinSetupResult(resultCode, data)
|
||||
} else if (requestCode == net.aliasvault.app.nativevaultmanager.NativeVaultManager.QR_SCANNER_REQUEST_CODE) {
|
||||
handleQRScannerResult(resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,4 +196,31 @@ class MainActivity : ReactActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle QR scanner result.
|
||||
* @param resultCode The result code from the QR scanner activity.
|
||||
* @param data The intent data containing the scanned QR code.
|
||||
*/
|
||||
private fun handleQRScannerResult(resultCode: Int, data: Intent?) {
|
||||
val promise = net.aliasvault.app.nativevaultmanager.NativeVaultManager.pendingActivityResultPromise
|
||||
net.aliasvault.app.nativevaultmanager.NativeVaultManager.pendingActivityResultPromise = null
|
||||
|
||||
if (promise == null) {
|
||||
return
|
||||
}
|
||||
|
||||
when (resultCode) {
|
||||
RESULT_OK -> {
|
||||
val scannedData = data?.getStringExtra("SCAN_RESULT")
|
||||
promise.resolve(scannedData)
|
||||
}
|
||||
RESULT_CANCELED -> {
|
||||
promise.resolve(null)
|
||||
}
|
||||
else -> {
|
||||
promise.resolve(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.aliasvault.app.qrscanner.QRScannerActivity
|
||||
import net.aliasvault.app.vaultstore.VaultStore
|
||||
import net.aliasvault.app.vaultstore.VaultSyncError
|
||||
import net.aliasvault.app.vaultstore.keystoreprovider.AndroidKeystoreProvider
|
||||
@@ -63,6 +64,11 @@ class NativeVaultManager(reactContext: ReactApplicationContext) :
|
||||
*/
|
||||
const val PIN_SETUP_REQUEST_CODE = 1002
|
||||
|
||||
/**
|
||||
* Request code for QR scanner activity.
|
||||
*/
|
||||
const val QR_SCANNER_REQUEST_CODE = 1003
|
||||
|
||||
/**
|
||||
* Static holder for the pending promise from showPinUnlock.
|
||||
* This allows MainActivity to resolve/reject the promise directly without
|
||||
@@ -1436,6 +1442,42 @@ class NativeVaultManager(reactContext: ReactApplicationContext) :
|
||||
* @param subtitle The subtitle for authentication. If null or empty, uses default.
|
||||
* @param promise The promise to resolve with authentication result.
|
||||
*/
|
||||
@ReactMethod
|
||||
override fun scanQRCode(prefixes: ReadableArray?, statusText: String?, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
try {
|
||||
val activity = currentActivity
|
||||
if (activity == null) {
|
||||
promise.reject("NO_ACTIVITY", "No activity available", null)
|
||||
return@launch
|
||||
}
|
||||
|
||||
// Store promise for later resolution by MainActivity
|
||||
pendingActivityResultPromise = promise
|
||||
|
||||
// Launch QR scanner activity with optional prefixes and status text
|
||||
val intent = Intent(activity, QRScannerActivity::class.java)
|
||||
if (prefixes != null && prefixes.size() > 0) {
|
||||
val prefixList = ArrayList<String>()
|
||||
for (i in 0 until prefixes.size()) {
|
||||
val prefix = prefixes.getString(i)
|
||||
if (prefix != null) {
|
||||
prefixList.add(prefix)
|
||||
}
|
||||
}
|
||||
intent.putStringArrayListExtra(QRScannerActivity.EXTRA_PREFIXES, prefixList)
|
||||
}
|
||||
if (statusText != null && statusText.isNotEmpty()) {
|
||||
intent.putExtra(QRScannerActivity.EXTRA_STATUS_TEXT, statusText)
|
||||
}
|
||||
activity.startActivityForResult(intent, QR_SCANNER_REQUEST_CODE)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to launch QR scanner", e)
|
||||
promise.reject("SCANNER_ERROR", "Failed to launch QR scanner: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
override fun authenticateUser(title: String?, subtitle: String?, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package net.aliasvault.app.qrscanner
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.google.zxing.ResultPoint
|
||||
import com.journeyapps.barcodescanner.BarcodeCallback
|
||||
import com.journeyapps.barcodescanner.BarcodeResult
|
||||
import com.journeyapps.barcodescanner.CaptureManager
|
||||
import com.journeyapps.barcodescanner.DecoratedBarcodeView
|
||||
|
||||
/**
|
||||
* Activity for scanning QR codes using ZXing.
|
||||
*/
|
||||
class QRScannerActivity : Activity() {
|
||||
private lateinit var barcodeView: DecoratedBarcodeView
|
||||
private lateinit var capture: CaptureManager
|
||||
private var hasScanned = false
|
||||
private var prefixes: List<String>? = null
|
||||
|
||||
companion object {
|
||||
/** Intent extra key for prefixes. */
|
||||
const val EXTRA_PREFIXES = "EXTRA_PREFIXES"
|
||||
|
||||
/** Intent extra key for status text. */
|
||||
const val EXTRA_STATUS_TEXT = "EXTRA_STATUS_TEXT"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Get prefixes from intent if provided
|
||||
prefixes = intent.getStringArrayListExtra(EXTRA_PREFIXES)
|
||||
|
||||
// Get status text from intent, default to "Scan QR code" if not provided
|
||||
val statusText = intent.getStringExtra(EXTRA_STATUS_TEXT)?.takeIf { it.isNotEmpty() } ?: "Scan QR code"
|
||||
|
||||
// Create and configure barcode view
|
||||
barcodeView = DecoratedBarcodeView(this)
|
||||
barcodeView.setStatusText(statusText)
|
||||
setContentView(barcodeView)
|
||||
|
||||
// Initialize capture manager
|
||||
capture = CaptureManager(this, barcodeView)
|
||||
capture.initializeFromIntent(intent, savedInstanceState)
|
||||
|
||||
// Set custom callback to add visual feedback
|
||||
barcodeView.decodeContinuous(object : BarcodeCallback {
|
||||
override fun barcodeResult(result: BarcodeResult?) {
|
||||
if (result != null && !hasScanned) {
|
||||
val scannedText = result.text
|
||||
|
||||
// Check if prefixes filter is enabled
|
||||
if (prefixes != null && prefixes!!.isNotEmpty()) {
|
||||
// Check if the scanned code starts with any of the accepted prefixes
|
||||
val hasValidPrefix = prefixes!!.any { prefix ->
|
||||
scannedText.startsWith(prefix)
|
||||
}
|
||||
|
||||
if (!hasValidPrefix) {
|
||||
// Invalid QR code - continue scanning without setting hasScanned
|
||||
// Note: ZXing library continues scanning automatically
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Valid QR code
|
||||
hasScanned = true
|
||||
|
||||
// Show success animation
|
||||
showScanSuccessAnimation()
|
||||
|
||||
// Pause scanning
|
||||
barcodeView.pause()
|
||||
|
||||
// Set result and finish after animation
|
||||
val resultIntent = Intent()
|
||||
resultIntent.putExtra("SCAN_RESULT", scannedText)
|
||||
setResult(RESULT_OK, resultIntent)
|
||||
|
||||
// Delay finish to allow animation to complete
|
||||
barcodeView.postDelayed({
|
||||
finish()
|
||||
}, 400) // 400ms delay for animation
|
||||
}
|
||||
}
|
||||
|
||||
override fun possibleResultPoints(resultPoints: List<ResultPoint>) {
|
||||
// No visualization needed
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a success animation when QR code is scanned.
|
||||
*/
|
||||
private fun showScanSuccessAnimation() {
|
||||
// Flash animation - fade viewfinder quickly
|
||||
val viewFinder: View? = barcodeView.viewFinder
|
||||
if (viewFinder != null) {
|
||||
// Create flash effect by animating alpha
|
||||
val fadeOut = ObjectAnimator.ofFloat(viewFinder, "alpha", 1f, 0.3f)
|
||||
fadeOut.duration = 100
|
||||
|
||||
val fadeIn = ObjectAnimator.ofFloat(viewFinder, "alpha", 0.3f, 1f)
|
||||
fadeIn.duration = 100
|
||||
|
||||
fadeOut.start()
|
||||
fadeOut.addListener(object : android.animation.AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: android.animation.Animator) {
|
||||
fadeIn.start()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
capture.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
capture.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
capture.onDestroy()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
capture.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray,
|
||||
) {
|
||||
capture.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
}
|
||||
}
|
||||
1
apps/mobile-app/android/fdroid/.gitignore
vendored
1
apps/mobile-app/android/fdroid/.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
fdroiddata
|
||||
fdroidserver
|
||||
net.aliasvault.app.yml
|
||||
|
||||
@@ -14,6 +14,8 @@ services:
|
||||
- ./net.aliasvault.app.yml:/net.aliasvault.app.yml
|
||||
# Add build script to the container
|
||||
- ./scripts/build.sh:/build.sh:Z
|
||||
# Bind the outputs directory to capture APK build output
|
||||
- ./outputs:/outputs:rw
|
||||
# Increase memory limits for Gradle builds
|
||||
shm_size: '2gb'
|
||||
mem_limit: 12g
|
||||
|
||||
@@ -13,9 +13,9 @@ RepoType: git
|
||||
Repo: https://github.com/aliasvault/aliasvault.git
|
||||
|
||||
Builds:
|
||||
- versionName: 0.1.0
|
||||
versionCode: 1
|
||||
commit: main
|
||||
- versionName: __VERSION_NAME__
|
||||
versionCode: __VERSION_CODE__
|
||||
commit: __COMMIT__
|
||||
subdir: apps/mobile-app/android/app/
|
||||
sudo:
|
||||
- sysctl fs.inotify.max_user_watches=524288 || true
|
||||
@@ -26,7 +26,7 @@ Builds:
|
||||
init:
|
||||
- cd ../..
|
||||
- sed -i -e '/signingConfig /d' android/app/build.gradle
|
||||
- npm install --build-from-source
|
||||
- npm install --production --build-from-source
|
||||
gradle:
|
||||
- yes
|
||||
scanignore:
|
||||
@@ -44,8 +44,6 @@ Builds:
|
||||
- apps/mobile-app/node_modules/react-native-context-menu-view/android/build.gradle
|
||||
- apps/mobile-app/node_modules/react-native-get-random-values/android/build.gradle
|
||||
- apps/mobile-app/node_modules/react-native-svg/android/build.gradle
|
||||
- apps/mobile-app/node_modules/expo-dev-launcher/android/build.gradle
|
||||
- apps/mobile-app/node_modules/expo-dev-menu/android/build.gradle
|
||||
scandelete:
|
||||
- apps/mobile-app/node_modules/
|
||||
|
||||
@@ -2,11 +2,64 @@
|
||||
set -e # Exit on any error, except where explicitly ignored
|
||||
trap 'echo "🛑 Interrupted. Exiting..."; exit 130' INT # Handle Ctrl+C cleanly
|
||||
|
||||
# Get the directory where this script is located
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BUILD_GRADLE="${SCRIPT_DIR}/../app/build.gradle"
|
||||
TEMPLATE_FILE="${SCRIPT_DIR}/net.aliasvault.app.yml.template"
|
||||
OUTPUT_FILE="${SCRIPT_DIR}/net.aliasvault.app.yml"
|
||||
|
||||
# Check if template exists
|
||||
if [ ! -f "$TEMPLATE_FILE" ]; then
|
||||
echo "❌ Error: Template file not found: $TEMPLATE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if build.gradle exists
|
||||
if [ ! -f "$BUILD_GRADLE" ]; then
|
||||
echo "❌ Error: build.gradle not found: $BUILD_GRADLE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract version information from build.gradle
|
||||
echo "📱 Extracting version information from build.gradle..."
|
||||
VERSION_CODE=$(grep -E '^\s*versionCode\s+' "$BUILD_GRADLE" | sed -E 's/.*versionCode\s+([0-9]+).*/\1/')
|
||||
VERSION_NAME=$(grep -E '^\s*versionName\s+' "$BUILD_GRADLE" | sed -E 's/.*versionName\s+"([^"]+)".*/\1/')
|
||||
|
||||
if [ -z "$VERSION_CODE" ] || [ -z "$VERSION_NAME" ]; then
|
||||
echo "❌ Error: Could not extract version information from build.gradle"
|
||||
echo " versionCode: ${VERSION_CODE:-not found}"
|
||||
echo " versionName: ${VERSION_NAME:-not found}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get current git branch
|
||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")
|
||||
|
||||
echo "✅ Version information extracted:"
|
||||
echo " versionCode: $VERSION_CODE"
|
||||
echo " versionName: $VERSION_NAME"
|
||||
echo " commit: $CURRENT_BRANCH"
|
||||
|
||||
# Generate the F-Droid metadata file from template
|
||||
echo "📝 Generating F-Droid metadata file..."
|
||||
sed -e "s/__VERSION_NAME__/$VERSION_NAME/g" \
|
||||
-e "s/__VERSION_CODE__/$VERSION_CODE/g" \
|
||||
-e "s/__COMMIT__/$CURRENT_BRANCH/g" \
|
||||
"$TEMPLATE_FILE" > "$OUTPUT_FILE"
|
||||
|
||||
echo "✅ Generated: $OUTPUT_FILE"
|
||||
|
||||
# Create outputs bind dir and set correct permissions
|
||||
mkdir -p outputs
|
||||
sudo chown -R 1000:1000 outputs
|
||||
|
||||
# Build and run the Docker environment
|
||||
echo "Building Docker images..."
|
||||
echo "🐳 Building Docker images..."
|
||||
if ! docker compose build; then
|
||||
echo "⚠️ Warning: Docker build failed, continuing..."
|
||||
fi
|
||||
|
||||
echo "Running fdroid-buildserver..."
|
||||
docker compose run --rm fdroid-buildserver
|
||||
echo "🚀 Running fdroid-buildserver..."
|
||||
docker compose run --rm fdroid-buildserver
|
||||
|
||||
echo "✅ F-Droid build completed!"
|
||||
|
||||
@@ -22,5 +22,9 @@ cd /home/vagrant/build
|
||||
fdroid fetchsrclibs net.aliasvault.app --verbose
|
||||
# Format build receipe
|
||||
fdroid rewritemeta net.aliasvault.app
|
||||
# Lint app
|
||||
fdroid lint --verbose net.aliasvault.app
|
||||
# Build app and scan for any binary files that are prohibited
|
||||
fdroid build --verbose --latest --scan-binary --on-server --no-tarball net.aliasvault.app
|
||||
fdroid build --verbose --test --latest --scan-binary --on-server --no-tarball net.aliasvault.app
|
||||
# Copy any outputs to the bind mount folder
|
||||
rsync -avh /home/vagrant/build/build/net.aliasvault.app/apps/mobile-app/android/app/build/outputs/ /outputs/
|
||||
|
||||
124
apps/mobile-app/android/fdroid/scripts/sign-apk.sh
Executable file
124
apps/mobile-app/android/fdroid/scripts/sign-apk.sh
Executable file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ================================
|
||||
# This script is used to sign an unsigned F-Droid APK file with the local debug keystore (on MacOS) for testing purposes.
|
||||
# ================================
|
||||
# Flow:
|
||||
# 1. First do the run.sh / build.sh flow to build the F-Droid APK file on a (Linux) machine with enough memory and CPU power.
|
||||
# 2. Extract the unsigned APK file from the local (bind-mounted) outputs directory
|
||||
# 3. Then use this script to sign the APK file with the local debug keystore (on MacOS).
|
||||
#
|
||||
# ================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# --- Colors ---
|
||||
RED="\033[0;31m"
|
||||
GREEN="\033[0;32m"
|
||||
YELLOW="\033[1;33m"
|
||||
CYAN="\033[0;36m"
|
||||
RESET="\033[0m"
|
||||
|
||||
info() { echo -e "${CYAN}[INFO]${RESET} $1"; }
|
||||
ok() { echo -e "${GREEN}[OK]${RESET} $1"; }
|
||||
error() { echo -e "${RED}[ERROR]${RESET} $1"; }
|
||||
|
||||
echo -e "${YELLOW}=== APK Debug Signer (macOS) ===${RESET}"
|
||||
|
||||
# --- Ask for unsigned APK ---
|
||||
read -rp "Enter unsigned APK filename (example: app-release-unsigned.apk): " APK_IN
|
||||
|
||||
if [[ ! -f "$APK_IN" ]]; then
|
||||
error "File not found: $APK_IN"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
info "Input APK: $APK_IN"
|
||||
|
||||
# --- Detect SDK and build-tools ---
|
||||
SDK_ROOT="${ANDROID_SDK_ROOT:-$HOME/Library/Android/sdk}"
|
||||
BT_DIR="$SDK_ROOT/build-tools"
|
||||
|
||||
if [[ ! -d "$BT_DIR" ]]; then
|
||||
error "build-tools not found in: $BT_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
info "Scanning build-tools..."
|
||||
|
||||
LATEST_BT="$(ls "$BT_DIR" | sort -V | tail -n 1)"
|
||||
|
||||
if [[ -z "$LATEST_BT" ]]; then
|
||||
error "No build-tools found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
info "Using build-tools version: ${YELLOW}${LATEST_BT}${RESET}"
|
||||
|
||||
ZIPALIGN="$BT_DIR/$LATEST_BT/zipalign"
|
||||
APKSIGNER="$BT_DIR/$LATEST_BT/apksigner"
|
||||
|
||||
[[ -x "$ZIPALIGN" ]] || { error "zipalign missing: $ZIPALIGN"; exit 1; }
|
||||
[[ -x "$APKSIGNER" ]] || { error "apksigner missing: $APKSIGNER"; exit 1; }
|
||||
|
||||
# --- Filenames ---
|
||||
APK_ALIGNED="${APK_IN%.apk}-aligned-temp.apk"
|
||||
APK_SIGNED="${APK_IN%.apk}-signed.apk"
|
||||
|
||||
info "Temporary aligned APK: $APK_ALIGNED"
|
||||
info "Final signed APK: $APK_SIGNED"
|
||||
|
||||
# --- Debug keystore ---
|
||||
DEBUG_KEYSTORE="$HOME/.android/debug.keystore"
|
||||
DEBUG_ALIAS="androiddebugkey"
|
||||
DEBUG_PASS="android"
|
||||
|
||||
[[ -f "$DEBUG_KEYSTORE" ]] || {
|
||||
error "Debug keystore missing: $DEBUG_KEYSTORE"
|
||||
exit 1
|
||||
}
|
||||
|
||||
info "Using debug keystore: $DEBUG_KEYSTORE"
|
||||
|
||||
# --- Step 1: zipalign ---
|
||||
echo -e "${YELLOW}=== Step 1: zipalign ===${RESET}"
|
||||
echo -e "[CMD] \"$ZIPALIGN\" -p -f 4 \"$APK_IN\" \"$APK_ALIGNED\""
|
||||
|
||||
"$ZIPALIGN" -p -f 4 "$APK_IN" "$APK_ALIGNED"
|
||||
ok "zipalign complete"
|
||||
|
||||
# --- Step 2: sign ---
|
||||
echo -e "${YELLOW}=== Step 2: apksigner ===${RESET}"
|
||||
echo -e "[CMD] \"$APKSIGNER\" sign --ks \"$DEBUG_KEYSTORE\" --out \"$APK_SIGNED\" \"$APK_ALIGNED\""
|
||||
|
||||
"$APKSIGNER" sign \
|
||||
--ks "$DEBUG_KEYSTORE" \
|
||||
--ks-key-alias "$DEBUG_ALIAS" \
|
||||
--ks-pass "pass:$DEBUG_PASS" \
|
||||
--key-pass "pass:$DEBUG_PASS" \
|
||||
--out "$APK_SIGNED" \
|
||||
"$APK_ALIGNED"
|
||||
|
||||
ok "Signing complete"
|
||||
|
||||
# --- Step 3: verify ---
|
||||
echo -e "${YELLOW}=== Step 3: Verify ===${RESET}"
|
||||
|
||||
"$APKSIGNER" verify --verbose "$APK_SIGNED"
|
||||
ok "APK verified"
|
||||
|
||||
# --- Step 4: Cleanup ---
|
||||
echo -e "${YELLOW}=== Cleanup ===${RESET}"
|
||||
|
||||
if [[ -f "$APK_ALIGNED" ]]; then
|
||||
rm -f "$APK_ALIGNED"
|
||||
ok "Removed temporary file: $APK_ALIGNED"
|
||||
fi
|
||||
|
||||
ok "Cleanup complete"
|
||||
|
||||
echo -e "${GREEN}=== DONE ===${RESET}"
|
||||
echo -e "Signed APK created → ${YELLOW}$APK_SIGNED${RESET}"
|
||||
echo -e "Install with:"
|
||||
echo -e " ${CYAN}adb install -r \"$APK_SIGNED\"${RESET}"
|
||||
|
||||
@@ -52,6 +52,9 @@ expo.webp.animated=false
|
||||
# Enable network inspector
|
||||
EX_DEV_CLIENT_NETWORK_INSPECTOR=true
|
||||
|
||||
# Enable VisionCamera code scanner
|
||||
VisionCamera_enableCodeScanner=true
|
||||
|
||||
# Use legacy packaging to compress native libraries in the resulting APK.
|
||||
expo.useLegacyPackaging=false
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { CameraView, useCameraPermissions } from 'expo-camera';
|
||||
import { Href, router, useLocalSearchParams } from 'expo-router';
|
||||
import { useEffect, useCallback, useRef } from 'react';
|
||||
import { View, Alert, StyleSheet } from 'react-native';
|
||||
import { View, StyleSheet, Platform, Alert } from 'react-native';
|
||||
|
||||
import { useColors } from '@/hooks/useColorScheme';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
@@ -10,6 +8,7 @@ import { useTranslation } from '@/hooks/useTranslation';
|
||||
import LoadingIndicator from '@/components/LoadingIndicator';
|
||||
import { ThemedContainer } from '@/components/themed/ThemedContainer';
|
||||
import { ThemedText } from '@/components/themed/ThemedText';
|
||||
import NativeVaultManager from '@/specs/NativeVaultManager';
|
||||
|
||||
// QR Code type prefixes
|
||||
const QR_CODE_PREFIXES = {
|
||||
@@ -54,71 +53,73 @@ function parseQRCode(data: string): ScannedQRCode {
|
||||
export default function QRScannerScreen() : React.ReactNode {
|
||||
const colors = useColors();
|
||||
const { t } = useTranslation();
|
||||
const [permission, requestPermission] = useCameraPermissions();
|
||||
const { url } = useLocalSearchParams<{ url?: string }>();
|
||||
const hasProcessedUrl = useRef(false);
|
||||
const processedUrls = useRef(new Set<string>());
|
||||
|
||||
// Request camera permission on mount
|
||||
useEffect(() => {
|
||||
/**
|
||||
* Request camera permission.
|
||||
*/
|
||||
const requestCameraPermission = async () : Promise<void> => {
|
||||
if (!permission) {
|
||||
return; // Still loading permission status
|
||||
}
|
||||
|
||||
if (!permission.granted && permission.canAskAgain) {
|
||||
// Request permission
|
||||
await requestPermission();
|
||||
} else if (!permission.granted && !permission.canAskAgain) {
|
||||
// Permission was permanently denied
|
||||
Alert.alert(
|
||||
t('settings.qrScanner.cameraPermissionTitle'),
|
||||
t('settings.qrScanner.cameraPermissionMessage'),
|
||||
[{ text: t('common.ok'), /**
|
||||
* Go back to the settings tab.
|
||||
*/
|
||||
onPress: (): void => router.back() }]
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
requestCameraPermission();
|
||||
}, [permission, requestPermission, t]);
|
||||
const hasLaunchedScanner = useRef(false);
|
||||
|
||||
/*
|
||||
* Handle barcode scanned - parse and navigate to appropriate page.
|
||||
* Only processes AliasVault QR codes, silently ignores others.
|
||||
* Native scanner already filters by prefix, so we only get AliasVault QR codes here.
|
||||
* Validation is handled by the destination page.
|
||||
*/
|
||||
const handleBarcodeScanned = useCallback(({ data }: { data: string }) : void => {
|
||||
const handleQRCodeScanned = useCallback((data: string) : void => {
|
||||
// Prevent processing the same URL multiple times
|
||||
if (processedUrls.current.has(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the QR code to determine its type
|
||||
const parsedData = parseQRCode(data);
|
||||
|
||||
// Silently ignore non-AliasVault QR codes
|
||||
if (!parsedData.type) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark this URL as processed
|
||||
processedUrls.current.add(data);
|
||||
|
||||
// Parse the QR code to determine its type
|
||||
const parsedData = parseQRCode(data);
|
||||
|
||||
/*
|
||||
* Navigate to the appropriate page based on QR code type
|
||||
* Validation will be handled by the destination page
|
||||
* Use push instead of replace to navigate while scanner is still dismissing
|
||||
* This creates a smoother transition without returning to settings first
|
||||
*/
|
||||
if (parsedData.type === 'MOBILE_UNLOCK') {
|
||||
router.replace(`/(tabs)/settings/mobile-unlock/${parsedData.payload}` as Href);
|
||||
router.push(`/(tabs)/settings/mobile-unlock/${parsedData.payload}` as Href);
|
||||
}
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Launch the native QR scanner.
|
||||
*/
|
||||
const launchScanner = useCallback(async () => {
|
||||
if (hasLaunchedScanner.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasLaunchedScanner.current = true;
|
||||
|
||||
try {
|
||||
// Pass prefixes to native scanner for filtering and translated status text
|
||||
const prefixes = Object.values(QR_CODE_PREFIXES);
|
||||
const statusText = t('settings.qrScanner.scanningMessage');
|
||||
const scannedData = await NativeVaultManager.scanQRCode(prefixes, statusText);
|
||||
|
||||
if (scannedData) {
|
||||
handleQRCodeScanned(scannedData);
|
||||
} else {
|
||||
// User cancelled or scan failed, go back
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('QR scan error:', error);
|
||||
Alert.alert(
|
||||
t('common.error'),
|
||||
'Failed to scan QR code',
|
||||
[{ text: t('common.ok'), /**
|
||||
* Navigate back.
|
||||
*/
|
||||
onPress: (): void => router.back() }]
|
||||
);
|
||||
}
|
||||
}, [handleQRCodeScanned, t]);
|
||||
|
||||
/**
|
||||
* Reset hasProcessedUrl when URL changes to allow processing new URLs.
|
||||
*/
|
||||
@@ -132,45 +133,24 @@ export default function QRScannerScreen() : React.ReactNode {
|
||||
useEffect(() => {
|
||||
if (url && typeof url === 'string' && !hasProcessedUrl.current) {
|
||||
hasProcessedUrl.current = true;
|
||||
handleBarcodeScanned({ data: url });
|
||||
handleQRCodeScanned(url);
|
||||
}
|
||||
}, [url, handleBarcodeScanned]);
|
||||
}, [url, handleQRCodeScanned]);
|
||||
|
||||
/**
|
||||
* Launch scanner when component mounts (Android/iOS only).
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (Platform.OS === 'android' || Platform.OS === 'ios') {
|
||||
launchScanner();
|
||||
}
|
||||
}, [launchScanner]);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingHorizontal: 0,
|
||||
},
|
||||
camera: {
|
||||
flex: 1,
|
||||
},
|
||||
cameraContainer: {
|
||||
backgroundColor: colors.black,
|
||||
flex: 1,
|
||||
},
|
||||
cameraOverlay: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
bottom: 0,
|
||||
justifyContent: 'center',
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
},
|
||||
cameraOverlayText: {
|
||||
color: colors.white,
|
||||
fontSize: 16,
|
||||
marginTop: 20,
|
||||
paddingHorizontal: 40,
|
||||
textAlign: 'center',
|
||||
},
|
||||
closeButton: {
|
||||
position: 'absolute',
|
||||
right: 16,
|
||||
top: 16,
|
||||
zIndex: 10,
|
||||
},
|
||||
loadingContainer: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
@@ -179,35 +159,14 @@ export default function QRScannerScreen() : React.ReactNode {
|
||||
},
|
||||
});
|
||||
|
||||
// Show permission request screen
|
||||
if (!permission || !permission.granted) {
|
||||
return (
|
||||
<ThemedContainer>
|
||||
<View style={styles.loadingContainer}>
|
||||
<LoadingIndicator />
|
||||
</View>
|
||||
</ThemedContainer>
|
||||
);
|
||||
}
|
||||
|
||||
// Show loading while scanner is launching
|
||||
return (
|
||||
<ThemedContainer style={styles.container}>
|
||||
<View style={styles.cameraContainer}>
|
||||
<CameraView
|
||||
style={styles.camera}
|
||||
facing="back"
|
||||
barcodeScannerSettings={{
|
||||
barcodeTypes: ['qr'],
|
||||
}}
|
||||
onBarcodeScanned={handleBarcodeScanned}
|
||||
>
|
||||
<View style={styles.cameraOverlay}>
|
||||
<Ionicons name="qr-code-outline" size={100} color={colors.white} />
|
||||
<ThemedText style={styles.cameraOverlayText}>
|
||||
{t('settings.qrScanner.scanningMessage')}
|
||||
</ThemedText>
|
||||
</View>
|
||||
</CameraView>
|
||||
<View style={styles.loadingContainer}>
|
||||
<LoadingIndicator />
|
||||
<ThemedText style={{ marginTop: 20, color: colors.textMuted }}>
|
||||
{t('settings.qrScanner.scanningMessage')}
|
||||
</ThemedText>
|
||||
</View>
|
||||
</ThemedContainer>
|
||||
);
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
"notice": "Huomautus",
|
||||
"enabled": "Otettu käyttöön",
|
||||
"disabled": "Pois käytöstä",
|
||||
"twoFactorAuthentication": "Two-Factor Authentication",
|
||||
"deleteItemConfirmTitle": "Delete Item",
|
||||
"deleteItemConfirmDescription": "Are you sure you want to delete this item?",
|
||||
"twoFactorAuthentication": "Kaksivaiheinen tunnistautuminen",
|
||||
"deleteItemConfirmTitle": "Poista kohde",
|
||||
"deleteItemConfirmDescription": "Haluatko varmasti poistaa tämän kohteen?",
|
||||
"errors": {
|
||||
"unknownError": "Tapahtui tuntematon virhe. Yritä uudelleen.",
|
||||
"unknownErrorTryAgain": "Tapahtui tuntematon virhe. Yritä uudelleen.",
|
||||
@@ -207,13 +207,13 @@
|
||||
"passkeyWillBeDeleted": "Tämä todennusavain poistetaan, kun tallennat tämän käyttäjätiedon."
|
||||
},
|
||||
"totp": {
|
||||
"addCode": "Add 2FA Code",
|
||||
"nameOptional": "Name (optional)",
|
||||
"secretKey": "Secret Key",
|
||||
"instructions": "Enter the secret key shown by the website where you want to add two-factor authentication.",
|
||||
"saveToViewCode": "Save to view code",
|
||||
"addCode": "Lisää 2FA TOTP -koodi",
|
||||
"nameOptional": "Nimi (valinnainen)",
|
||||
"secretKey": "Salainen avain",
|
||||
"instructions": "Syötä salainen avain, joka näkyy sivustossa, jossa haluat lisätä kaksivaiheisen tunnistautumisen",
|
||||
"saveToViewCode": "Tallenna nähdäksesi koodin",
|
||||
"errors": {
|
||||
"invalidSecretKey": "Invalid secret key format."
|
||||
"invalidSecretKey": "Virheellinen salatun avaimen muoto."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@@ -328,8 +328,8 @@
|
||||
"languageDescription": "Aseta kieli, jota käytetään luotaessa uusia henkilöllisyyksiä.",
|
||||
"genderSection": "Sukupuoli",
|
||||
"genderDescription": "Aseta oletussukupuoli uusien henkilöllisyyksien luomiseksi. ",
|
||||
"ageRangeSection": "Age Range",
|
||||
"ageRangeDescription": "Set the age range for generating new identities.",
|
||||
"ageRangeSection": "Ikähaarukka",
|
||||
"ageRangeDescription": "Aseta ikähaarukka uusia henkilöllisyyksien luomisessa",
|
||||
"genderOptions": {
|
||||
"random": "Satunnainen",
|
||||
"male": "Mies",
|
||||
|
||||
@@ -7,22 +7,22 @@
|
||||
"yes": "Oui",
|
||||
"no": "Non",
|
||||
"ok": "OK",
|
||||
"continue": "Continue",
|
||||
"loading": "Loading",
|
||||
"error": "Error",
|
||||
"success": "Success",
|
||||
"never": "Never",
|
||||
"copied": "Copied to clipboard",
|
||||
"continue": "Continuer",
|
||||
"loading": "Chargement",
|
||||
"error": "Erreur",
|
||||
"success": "Succès",
|
||||
"never": "Jamais",
|
||||
"copied": "Copier dans le presse-papiers",
|
||||
"loadMore": "Voir plus",
|
||||
"use": "Use",
|
||||
"confirm": "Confirm",
|
||||
"next": "Next",
|
||||
"notice": "Notice",
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled",
|
||||
"twoFactorAuthentication": "Two-Factor Authentication",
|
||||
"deleteItemConfirmTitle": "Delete Item",
|
||||
"deleteItemConfirmDescription": "Are you sure you want to delete this item?",
|
||||
"use": "Utiliser",
|
||||
"confirm": "Confirmer",
|
||||
"next": "Suivant",
|
||||
"notice": "Notification",
|
||||
"enabled": "Activé",
|
||||
"disabled": "Désactivé",
|
||||
"twoFactorAuthentication": "Authentification à deux facteurs",
|
||||
"deleteItemConfirmTitle": "Supprimer l'élement",
|
||||
"deleteItemConfirmDescription": "Êtes-vous certain de vouloir supprimer cet élément?",
|
||||
"errors": {
|
||||
"unknownError": "An unknown error occurred. Please try again.",
|
||||
"unknownErrorTryAgain": "An unknown error occurred. Please try again.",
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
"notice": "Uwaga",
|
||||
"enabled": "Włączone",
|
||||
"disabled": "Wyłączone",
|
||||
"twoFactorAuthentication": "Two-Factor Authentication",
|
||||
"deleteItemConfirmTitle": "Delete Item",
|
||||
"deleteItemConfirmDescription": "Are you sure you want to delete this item?",
|
||||
"twoFactorAuthentication": "Uwierzytelnianie dwuskładnikowe",
|
||||
"deleteItemConfirmTitle": "Usuń element",
|
||||
"deleteItemConfirmDescription": "Czy na pewno chcesz usunąć ten element?",
|
||||
"errors": {
|
||||
"unknownError": "Wystąpił nieznany błąd. Spróbuj ponownie.",
|
||||
"unknownErrorTryAgain": "Wystąpił nieznany błąd. Spróbuj ponownie.",
|
||||
@@ -207,13 +207,13 @@
|
||||
"passkeyWillBeDeleted": "Ten klucz dostępu zostanie usunięty po zapisaniu tych danych."
|
||||
},
|
||||
"totp": {
|
||||
"addCode": "Add 2FA Code",
|
||||
"nameOptional": "Name (optional)",
|
||||
"secretKey": "Secret Key",
|
||||
"instructions": "Enter the secret key shown by the website where you want to add two-factor authentication.",
|
||||
"saveToViewCode": "Save to view code",
|
||||
"addCode": "Dodaj kod 2FA",
|
||||
"nameOptional": "Nazwa (opcjonalnie)",
|
||||
"secretKey": "Tajny klucz",
|
||||
"instructions": "Wprowadź tajny klucz wyświetlony na stronie internetowej, na której chcesz dodać uwierzytelnianie dwuskładnikowe.",
|
||||
"saveToViewCode": "Zapisz, aby wyświetlić kod",
|
||||
"errors": {
|
||||
"invalidSecretKey": "Invalid secret key format."
|
||||
"invalidSecretKey": "Nieprawidłowy format tajnego klucza."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
"notice": "Примечание",
|
||||
"enabled": "Включено",
|
||||
"disabled": "Отключено",
|
||||
"twoFactorAuthentication": "Two-Factor Authentication",
|
||||
"deleteItemConfirmTitle": "Delete Item",
|
||||
"deleteItemConfirmDescription": "Are you sure you want to delete this item?",
|
||||
"twoFactorAuthentication": "Двухфакторная аутентификация",
|
||||
"deleteItemConfirmTitle": "Удалить элемент",
|
||||
"deleteItemConfirmDescription": "Вы уверены, что хотите удалить этот элемент?",
|
||||
"errors": {
|
||||
"unknownError": "Произошла неизвестная ошибка. Пожалуйста, попробуйте снова.",
|
||||
"unknownErrorTryAgain": "Произошла неизвестная ошибка. Попробуйте снова.",
|
||||
@@ -207,13 +207,13 @@
|
||||
"passkeyWillBeDeleted": "Этот ключ доступа будет удален при сохранении этой учетной записи."
|
||||
},
|
||||
"totp": {
|
||||
"addCode": "Add 2FA Code",
|
||||
"nameOptional": "Name (optional)",
|
||||
"secretKey": "Secret Key",
|
||||
"instructions": "Enter the secret key shown by the website where you want to add two-factor authentication.",
|
||||
"saveToViewCode": "Save to view code",
|
||||
"addCode": "Добавить код 2FA",
|
||||
"nameOptional": "Имя (необязательно)",
|
||||
"secretKey": "Секретный ключ",
|
||||
"instructions": "Введите секретный ключ, указанный на веб-сайте, где вы хотите добавить двухфакторную аутентификацию.",
|
||||
"saveToViewCode": "Сохранить для просмотра кода",
|
||||
"errors": {
|
||||
"invalidSecretKey": "Invalid secret key format."
|
||||
"invalidSecretKey": "Неверный формат секретного ключа."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 60;
|
||||
objectVersion = 70;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@@ -212,7 +212,7 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
CEE9098F2DA548C7008D568F /* Exceptions for "Autofill" folder in "Autofill" target */ = {
|
||||
CEE9098F2DA548C7008D568F /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
@@ -222,84 +222,13 @@
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
CE59C7602E4F47FD0024A246 /* VaultUITests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CE77825E2EA1822400A75E6F /* VaultUtils */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultUtils;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CEE480882DBE86DC00F4A367 /* VaultStoreKit */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultStoreKit;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CEE480972DBE86DD00F4A367 /* VaultStoreKitTests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultStoreKitTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CEE4816B2DBE8AC800F4A367 /* VaultUI */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultUI;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CEE482AB2DBE8EFE00F4A367 /* VaultModels */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultModels;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CEE909812DA548C7008D568F /* Autofill */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
CEE9098F2DA548C7008D568F /* Exceptions for "Autofill" folder in "Autofill" target */,
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = Autofill;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CE59C7602E4F47FD0024A246 /* VaultUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultUITests; sourceTree = "<group>"; };
|
||||
CE77825E2EA1822400A75E6F /* VaultUtils */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultUtils; sourceTree = "<group>"; };
|
||||
CEE480882DBE86DC00F4A367 /* VaultStoreKit */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultStoreKit; sourceTree = "<group>"; };
|
||||
CEE480972DBE86DD00F4A367 /* VaultStoreKitTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultStoreKitTests; sourceTree = "<group>"; };
|
||||
CEE4816B2DBE8AC800F4A367 /* VaultUI */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultUI; sourceTree = "<group>"; };
|
||||
CEE482AB2DBE8EFE00F4A367 /* VaultModels */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultModels; sourceTree = "<group>"; };
|
||||
CEE909812DA548C7008D568F /* Autofill */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (CEE9098F2DA548C7008D568F /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Autofill; sourceTree = "<group>"; };
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -1418,10 +1347,7 @@
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
@@ -1475,10 +1401,7 @@
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
|
||||
@@ -291,4 +291,10 @@
|
||||
[vaultManager authenticateUser:title subtitle:subtitle resolver:resolve rejecter:reject];
|
||||
}
|
||||
|
||||
// MARK: - QR Code Scanner
|
||||
|
||||
- (void)scanQRCode:(NSArray<NSString *> *)prefixes statusText:(NSString *)statusText resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
||||
[vaultManager scanQRCode:prefixes statusText:statusText resolver:resolve rejecter:reject];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -5,6 +5,7 @@ import VaultStoreKit
|
||||
import VaultModels
|
||||
import SwiftUI
|
||||
import VaultUI
|
||||
import AVFoundation
|
||||
|
||||
/**
|
||||
* This class is used as a bridge to allow React Native to interact with the VaultStoreKit class.
|
||||
@@ -913,6 +914,42 @@ public class VaultManager: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func scanQRCode(_ prefixes: [String]?,
|
||||
statusText: String?,
|
||||
resolver resolve: @escaping RCTPromiseResolveBlock,
|
||||
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
DispatchQueue.main.async {
|
||||
// Get the root view controller from React Native
|
||||
guard let rootVC = RCTPresentedViewController() else {
|
||||
reject("NO_VIEW_CONTROLLER", "No view controller available", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Create QR scanner view with optional prefix filtering and custom status text
|
||||
let scannerView = QRScannerView(
|
||||
prefixes: prefixes,
|
||||
statusText: statusText,
|
||||
onCodeScanned: { code in
|
||||
// Resolve immediately and dismiss without waiting (matches Android behavior)
|
||||
resolve(code)
|
||||
rootVC.dismiss(animated: true)
|
||||
},
|
||||
onCancel: {
|
||||
// Cancel resolves nil and dismisses
|
||||
resolve(nil)
|
||||
rootVC.dismiss(animated: true)
|
||||
}
|
||||
)
|
||||
|
||||
let hostingController = UIHostingController(rootView: scannerView)
|
||||
|
||||
// Present modally as full screen
|
||||
hostingController.modalPresentationStyle = .fullScreen
|
||||
rootVC.present(hostingController, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func authenticateUser(_ title: String?,
|
||||
subtitle: String?,
|
||||
|
||||
@@ -272,10 +272,6 @@ PODS:
|
||||
- ExpoModulesCore
|
||||
- ExpoBlur (14.1.5):
|
||||
- ExpoModulesCore
|
||||
- ExpoCamera (16.1.11):
|
||||
- ExpoModulesCore
|
||||
- ZXingObjC/OneD
|
||||
- ZXingObjC/PDF417
|
||||
- ExpoClipboard (7.1.5):
|
||||
- ExpoModulesCore
|
||||
- ExpoDocumentPicker (13.1.6):
|
||||
@@ -2449,11 +2445,6 @@ PODS:
|
||||
- SwiftLint (0.59.1)
|
||||
- SWXMLHash (7.0.2)
|
||||
- Yoga (0.0.0)
|
||||
- ZXingObjC/Core (3.6.9)
|
||||
- ZXingObjC/OneD (3.6.9):
|
||||
- ZXingObjC/Core
|
||||
- ZXingObjC/PDF417 (3.6.9):
|
||||
- ZXingObjC/Core
|
||||
|
||||
DEPENDENCIES:
|
||||
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
||||
@@ -2468,7 +2459,6 @@ DEPENDENCIES:
|
||||
- expo-dev-menu-interface (from `../node_modules/expo-dev-menu-interface/ios`)
|
||||
- ExpoAsset (from `../node_modules/expo-asset/ios`)
|
||||
- ExpoBlur (from `../node_modules/expo-blur/ios`)
|
||||
- ExpoCamera (from `../node_modules/expo-camera/ios`)
|
||||
- ExpoClipboard (from `../node_modules/expo-clipboard/ios`)
|
||||
- ExpoDocumentPicker (from `../node_modules/expo-document-picker/ios`)
|
||||
- ExpoFileSystem (from `../node_modules/expo-file-system/ios`)
|
||||
@@ -2582,7 +2572,6 @@ SPEC REPOS:
|
||||
- SQLite.swift
|
||||
- SwiftLint
|
||||
- SWXMLHash
|
||||
- ZXingObjC
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
boost:
|
||||
@@ -2609,8 +2598,6 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/expo-asset/ios"
|
||||
ExpoBlur:
|
||||
:path: "../node_modules/expo-blur/ios"
|
||||
ExpoCamera:
|
||||
:path: "../node_modules/expo-camera/ios"
|
||||
ExpoClipboard:
|
||||
:path: "../node_modules/expo-clipboard/ios"
|
||||
ExpoDocumentPicker:
|
||||
@@ -2820,7 +2807,6 @@ SPEC CHECKSUMS:
|
||||
expo-dev-menu-interface: 609c35ae8b97479cdd4c9e23c8cf6adc44beea0e
|
||||
ExpoAsset: ef06e880126c375f580d4923fdd1cdf4ee6ee7d6
|
||||
ExpoBlur: 3c8885b9bf9eef4309041ec87adec48b5f1986a9
|
||||
ExpoCamera: e1879906d41184e84b57d7643119f8509414e318
|
||||
ExpoClipboard: 436f6de6971f14eb75ae160e076d9cb3b19eb795
|
||||
ExpoDocumentPicker: b263a279685b6640b8c8bc70d71c83067aeaae55
|
||||
ExpoFileSystem: 7f92f7be2f5c5ed40a7c9efc8fa30821181d9d63
|
||||
@@ -2925,7 +2911,6 @@ SPEC CHECKSUMS:
|
||||
SwiftLint: 3d48e2fb2a3468fdaccf049e5e755df22fb40c2c
|
||||
SWXMLHash: dd733a457e9c4fe93b1538654057aefae4acb382
|
||||
Yoga: dc7c21200195acacb62fa920c588e7c2106de45e
|
||||
ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5
|
||||
|
||||
PODFILE CHECKSUM: ac288e273086bafdd610cafff08ccca0d164f7c3
|
||||
|
||||
|
||||
240
apps/mobile-app/ios/VaultUI/QRScanner/QRScannerView.swift
Normal file
240
apps/mobile-app/ios/VaultUI/QRScanner/QRScannerView.swift
Normal file
@@ -0,0 +1,240 @@
|
||||
import SwiftUI
|
||||
import AVFoundation
|
||||
|
||||
private let locBundle = Bundle.vaultUI
|
||||
|
||||
/// SwiftUI view for scanning QR codes using AVFoundation
|
||||
public struct QRScannerView: View {
|
||||
let onCodeScanned: (String) -> Void
|
||||
let onCancel: () -> Void
|
||||
let prefixes: [String]?
|
||||
let statusText: String
|
||||
|
||||
@State private var hasScanned = false
|
||||
@State private var showFlash = false
|
||||
|
||||
public init(
|
||||
prefixes: [String]? = nil,
|
||||
statusText: String? = nil,
|
||||
onCodeScanned: @escaping (String) -> Void,
|
||||
onCancel: @escaping () -> Void
|
||||
) {
|
||||
self.prefixes = prefixes
|
||||
self.statusText = statusText?.isEmpty == false ? statusText! : "Scan QR code"
|
||||
self.onCodeScanned = onCodeScanned
|
||||
self.onCancel = onCancel
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
ZStack {
|
||||
// Camera preview
|
||||
QRScannerRepresentable(
|
||||
prefixes: prefixes,
|
||||
onCodeScanned: { code in
|
||||
if !hasScanned {
|
||||
hasScanned = true
|
||||
showFlash = true
|
||||
|
||||
// Flash animation then callback
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
|
||||
onCodeScanned(code)
|
||||
}
|
||||
}
|
||||
},
|
||||
onCodeRejected: {
|
||||
// Reset hasScanned to allow scanning again
|
||||
hasScanned = false
|
||||
}
|
||||
)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
|
||||
// Overlay with viewfinder
|
||||
VStack {
|
||||
Spacer()
|
||||
|
||||
// Viewfinder frame
|
||||
Rectangle()
|
||||
.stroke(Color.white, lineWidth: 3)
|
||||
.frame(width: 280, height: 280)
|
||||
.overlay(
|
||||
// Flash effect
|
||||
Rectangle()
|
||||
.fill(Color.white)
|
||||
.opacity(showFlash ? 0.7 : 0)
|
||||
.animation(.easeInOut(duration: 0.2), value: showFlash)
|
||||
)
|
||||
|
||||
Spacer()
|
||||
|
||||
// Status text
|
||||
Text(statusText)
|
||||
.foregroundColor(.white)
|
||||
.padding()
|
||||
.background(Color.black.opacity(0.7))
|
||||
.cornerRadius(10)
|
||||
.padding(.bottom, 50)
|
||||
}
|
||||
|
||||
// Cancel button
|
||||
VStack {
|
||||
HStack {
|
||||
Spacer()
|
||||
Button(action: onCancel) {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.font(.system(size: 32))
|
||||
.foregroundColor(.white)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.background(Color.black)
|
||||
}
|
||||
}
|
||||
|
||||
/// UIViewControllerRepresentable wrapper for AVFoundation camera
|
||||
struct QRScannerRepresentable: UIViewControllerRepresentable {
|
||||
let prefixes: [String]?
|
||||
let onCodeScanned: (String) -> Void
|
||||
let onCodeRejected: () -> Void
|
||||
|
||||
func makeUIViewController(context: Context) -> QRScannerViewController {
|
||||
let controller = QRScannerViewController()
|
||||
controller.prefixes = prefixes
|
||||
controller.onCodeScanned = onCodeScanned
|
||||
controller.onCodeRejected = onCodeRejected
|
||||
return controller
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: QRScannerViewController, context: Context) {
|
||||
// No updates needed
|
||||
}
|
||||
}
|
||||
|
||||
/// UIViewController that handles AVFoundation QR code scanning
|
||||
class QRScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
|
||||
var captureSession: AVCaptureSession?
|
||||
var previewLayer: AVCaptureVideoPreviewLayer?
|
||||
var prefixes: [String]?
|
||||
var onCodeScanned: ((String) -> Void)?
|
||||
var onCodeRejected: (() -> Void)?
|
||||
private var rejectedQRCodes = Set<String>() // Track rejected QR codes to avoid repeated haptic feedback
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupCamera()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if let session = captureSession, !session.isRunning {
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
session.startRunning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
if let session = captureSession, session.isRunning {
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
session.stopRunning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setupCamera() {
|
||||
let session = AVCaptureSession()
|
||||
|
||||
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else {
|
||||
return
|
||||
}
|
||||
|
||||
let videoInput: AVCaptureDeviceInput
|
||||
|
||||
do {
|
||||
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
if session.canAddInput(videoInput) {
|
||||
session.addInput(videoInput)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
let metadataOutput = AVCaptureMetadataOutput()
|
||||
|
||||
if session.canAddOutput(metadataOutput) {
|
||||
session.addOutput(metadataOutput)
|
||||
|
||||
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
|
||||
metadataOutput.metadataObjectTypes = [.qr]
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
let previewLayer = AVCaptureVideoPreviewLayer(session: session)
|
||||
previewLayer.frame = view.layer.bounds
|
||||
previewLayer.videoGravity = .resizeAspectFill
|
||||
view.layer.addSublayer(previewLayer)
|
||||
|
||||
self.captureSession = session
|
||||
self.previewLayer = previewLayer
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
session.startRunning()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
previewLayer?.frame = view.layer.bounds
|
||||
}
|
||||
|
||||
func metadataOutput(
|
||||
_ output: AVCaptureMetadataOutput,
|
||||
didOutput metadataObjects: [AVMetadataObject],
|
||||
from connection: AVCaptureConnection
|
||||
) {
|
||||
if let metadataObject = metadataObjects.first,
|
||||
let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject,
|
||||
let stringValue = readableObject.stringValue {
|
||||
|
||||
// Check if prefixes filter is enabled
|
||||
if let prefixes = prefixes, !prefixes.isEmpty {
|
||||
// Check if the scanned code starts with any of the accepted prefixes
|
||||
let hasValidPrefix = prefixes.contains { prefix in
|
||||
stringValue.hasPrefix(prefix)
|
||||
}
|
||||
|
||||
if !hasValidPrefix {
|
||||
// Invalid QR code - only give haptic feedback once per unique code
|
||||
if !rejectedQRCodes.contains(stringValue) {
|
||||
let generator = UINotificationFeedbackGenerator()
|
||||
generator.notificationOccurred(.warning)
|
||||
rejectedQRCodes.insert(stringValue)
|
||||
}
|
||||
|
||||
// Notify that code was rejected (to reset UI state if needed)
|
||||
onCodeRejected?()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Valid QR code - stop scanning
|
||||
captureSession?.stopRunning()
|
||||
|
||||
// Success haptic feedback
|
||||
let generator = UINotificationFeedbackGenerator()
|
||||
generator.notificationOccurred(.success)
|
||||
|
||||
// Callback with scanned code
|
||||
onCodeScanned?(stringValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
172
apps/mobile-app/package-lock.json
generated
172
apps/mobile-app/package-lock.json
generated
@@ -17,10 +17,8 @@
|
||||
"@types/jsrsasign": "^10.5.15",
|
||||
"expo": "^53.0.22",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "^16.1.11",
|
||||
"expo-clipboard": "~7.1.5",
|
||||
"expo-constants": "~17.1.7",
|
||||
"expo-dev-client": "~5.1.8",
|
||||
"expo-document-picker": "~13.1.6",
|
||||
"expo-file-system": "~18.1.11",
|
||||
"expo-font": "~13.3.2",
|
||||
@@ -37,8 +35,7 @@
|
||||
"expo-web-browser": "~14.2.0",
|
||||
"fbemitter": "^3.0.0",
|
||||
"i18next": "^25.3.2",
|
||||
"jest": "~29.7.0",
|
||||
"jest-expo": "~53.0.10",
|
||||
"lodash": "^4.17.21",
|
||||
"otpauth": "^9.4.0",
|
||||
"react": "19.0.0",
|
||||
"react-hook-form": "^7.56.1",
|
||||
@@ -55,6 +52,7 @@
|
||||
"react-native-safe-area-context": "5.6.1",
|
||||
"react-native-screens": "~4.15.4",
|
||||
"react-native-svg": "15.11.2",
|
||||
"react-native-svg-transformer": "^1.5.0",
|
||||
"react-native-toast-message": "^2.2.1",
|
||||
"react-native-webview": "13.13.5",
|
||||
"secure-remote-password": "github:LinusU/secure-remote-password#73e5f732b6ca0cdbdc19da1a0c5f48cdbad2cbf0",
|
||||
@@ -77,10 +75,10 @@
|
||||
"eslint-config-expo": "~9.2.0",
|
||||
"eslint-plugin-jsdoc": "^55.2.0",
|
||||
"eslint-plugin-react-native": "^5.0.0",
|
||||
"expo-dev-client": "~5.1.8",
|
||||
"globals": "^16.3.0",
|
||||
"jest": "^29.2.1",
|
||||
"jest-expo": "~53.0.0",
|
||||
"react-native-svg-transformer": "^1.5.0",
|
||||
"typescript": "~5.8.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -129,7 +127,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz",
|
||||
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.3",
|
||||
@@ -4100,7 +4097,6 @@
|
||||
"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",
|
||||
@@ -4225,7 +4221,6 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
|
||||
"integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -4242,7 +4237,6 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
|
||||
"integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -4259,7 +4253,6 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
|
||||
"integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -4276,7 +4269,6 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
|
||||
"integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -4293,7 +4285,6 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
|
||||
"integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -4310,7 +4301,6 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
|
||||
"integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -4327,7 +4317,6 @@
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz",
|
||||
"integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -4344,7 +4333,6 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
|
||||
"integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -4361,7 +4349,6 @@
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz",
|
||||
"integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
|
||||
@@ -4388,9 +4375,7 @@
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
|
||||
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.21.3",
|
||||
"@svgr/babel-preset": "8.1.0",
|
||||
@@ -4410,7 +4395,6 @@
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
||||
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@@ -4423,7 +4407,6 @@
|
||||
"version": "8.3.6",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
|
||||
"integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"import-fresh": "^3.3.0",
|
||||
@@ -4450,7 +4433,6 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
|
||||
"integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.21.3",
|
||||
@@ -4468,7 +4450,6 @@
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
@@ -4481,7 +4462,6 @@
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz",
|
||||
"integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.21.3",
|
||||
@@ -4504,7 +4484,6 @@
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz",
|
||||
"integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cosmiconfig": "^8.1.3",
|
||||
@@ -4526,7 +4505,6 @@
|
||||
"version": "8.3.6",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
|
||||
"integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"import-fresh": "^3.3.0",
|
||||
@@ -4563,7 +4541,6 @@
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
||||
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
@@ -4634,6 +4611,7 @@
|
||||
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
@@ -4645,6 +4623,7 @@
|
||||
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/eslint": "*",
|
||||
"@types/estree": "*"
|
||||
@@ -4775,7 +4754,6 @@
|
||||
"integrity": "sha512-ixLZ7zG7j1fM0DijL9hDArwhwcCb4vqmePgwtV0GfnkHRSCUEv4LvzarcTdhoqgyMznUx/EhoTUv31CKZzkQlw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -4893,7 +4871,6 @@
|
||||
"integrity": "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.43.0",
|
||||
"@typescript-eslint/types": "8.43.0",
|
||||
@@ -5424,6 +5401,7 @@
|
||||
"integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/helper-numbers": "1.13.2",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2"
|
||||
@@ -5434,21 +5412,24 @@
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
|
||||
"integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-api-error": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
|
||||
"integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-buffer": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
|
||||
"integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-numbers": {
|
||||
"version": "1.13.2",
|
||||
@@ -5456,6 +5437,7 @@
|
||||
"integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
|
||||
"@webassemblyjs/helper-api-error": "1.13.2",
|
||||
@@ -5467,7 +5449,8 @@
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
|
||||
"integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-wasm-section": {
|
||||
"version": "1.14.1",
|
||||
@@ -5475,6 +5458,7 @@
|
||||
"integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
@@ -5488,6 +5472,7 @@
|
||||
"integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@xtuc/ieee754": "^1.2.0"
|
||||
}
|
||||
@@ -5498,6 +5483,7 @@
|
||||
"integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
@@ -5507,7 +5493,8 @@
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
|
||||
"integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-edit": {
|
||||
"version": "1.14.1",
|
||||
@@ -5515,6 +5502,7 @@
|
||||
"integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
@@ -5532,6 +5520,7 @@
|
||||
"integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
@@ -5546,6 +5535,7 @@
|
||||
"integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
@@ -5559,6 +5549,7 @@
|
||||
"integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-api-error": "1.13.2",
|
||||
@@ -5574,6 +5565,7 @@
|
||||
"integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@xtuc/long": "4.2.2"
|
||||
@@ -5593,14 +5585,16 @@
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@xtuc/long": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
|
||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
"license": "Apache-2.0",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/abab": {
|
||||
"version": "2.0.6",
|
||||
@@ -5649,7 +5643,6 @@
|
||||
"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"
|
||||
},
|
||||
@@ -5674,6 +5667,7 @@
|
||||
"integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
@@ -5753,6 +5747,7 @@
|
||||
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
@@ -5771,6 +5766,7 @@
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@@ -5787,7 +5783,8 @@
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/anser": {
|
||||
"version": "1.4.10",
|
||||
@@ -6545,7 +6542,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001737",
|
||||
"electron-to-chromium": "^1.5.211",
|
||||
@@ -6691,7 +6687,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -6785,6 +6780,7 @@
|
||||
"integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
}
|
||||
@@ -7277,7 +7273,6 @@
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
|
||||
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"css-tree": "~2.2.0"
|
||||
@@ -7291,7 +7286,6 @@
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
|
||||
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mdn-data": "2.0.28",
|
||||
@@ -7306,7 +7300,6 @@
|
||||
"version": "2.0.28",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
|
||||
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
|
||||
"dev": true,
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/cssom": {
|
||||
@@ -7717,7 +7710,6 @@
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
|
||||
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"no-case": "^3.0.4",
|
||||
@@ -7823,6 +7815,7 @@
|
||||
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
@@ -8028,7 +8021,8 @@
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
|
||||
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
@@ -8144,7 +8138,6 @@
|
||||
"integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -8329,7 +8322,6 @@
|
||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
@@ -8686,7 +8678,6 @@
|
||||
"resolved": "https://registry.npmjs.org/expo/-/expo-53.0.22.tgz",
|
||||
"integrity": "sha512-sJ2I4W/e5iiM4u/wYCe3qmW4D7WPCRqByPDD0hJcdYNdjc9HFFFdO4OAudZVyC/MmtoWZEIH5kTJP1cw9FjzYA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.0",
|
||||
"@expo/cli": "0.24.21",
|
||||
@@ -8756,26 +8747,6 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-camera": {
|
||||
"version": "16.1.11",
|
||||
"resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-16.1.11.tgz",
|
||||
"integrity": "sha512-etA5ZKoC6nPBnWWqiTmlX//zoFZ6cWQCCIdmpUHTGHAKd4qZNCkhPvBWbi8o32pDe57lix1V4+TPFgEcvPwsaA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"invariant": "^2.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": "*",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-web": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-native-web": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/expo-clipboard": {
|
||||
"version": "7.1.5",
|
||||
"resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-7.1.5.tgz",
|
||||
@@ -8792,7 +8763,6 @@
|
||||
"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"
|
||||
@@ -8806,6 +8776,7 @@
|
||||
"version": "5.1.8",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-5.1.8.tgz",
|
||||
"integrity": "sha512-IopYPgBi3JflksO5ieTphbKsbYHy9iIVdT/d69It++y0iBMSm0oBIoDmUijrHKjE3fV6jnrwrm8luU13/mzIQQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"expo-dev-launcher": "5.1.11",
|
||||
@@ -8822,6 +8793,7 @@
|
||||
"version": "5.1.11",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-5.1.11.tgz",
|
||||
"integrity": "sha512-bN0+nv5H038s8Gzf8i16hwCyD3sWDmHp7vb+QbL1i6B3XNnICCKS/H/3VH6H3PRMvCmoLGPlg+ODDqGlf0nu3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "8.11.0",
|
||||
@@ -8837,6 +8809,7 @@
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
|
||||
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
@@ -8853,12 +8826,14 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expo-dev-menu": {
|
||||
"version": "6.1.10",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-6.1.10.tgz",
|
||||
"integrity": "sha512-LaI0Bw5zzw5XefjYSX6YaMydzk0YBysjqQoxzj6ufDyKgwAfPmFwOLkZ03DOSerc9naezGLNAGgTEN6QTgMmgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"expo-dev-menu-interface": "1.10.0"
|
||||
@@ -8871,6 +8846,7 @@
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-menu-interface/-/expo-dev-menu-interface-1.10.0.tgz",
|
||||
"integrity": "sha512-NxtM/qot5Rh2cY333iOE87dDg1S8CibW+Wu4WdLua3UMjy81pXYzAGCZGNOeY7k9GpNFqDPNDXWyBSlk9r2pBg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
@@ -8900,7 +8876,6 @@
|
||||
"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"
|
||||
},
|
||||
@@ -8922,6 +8897,7 @@
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.15.0.tgz",
|
||||
"integrity": "sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expo-keep-awake": {
|
||||
@@ -8950,7 +8926,6 @@
|
||||
"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"
|
||||
@@ -8989,6 +8964,7 @@
|
||||
"version": "0.16.6",
|
||||
"resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.16.6.tgz",
|
||||
"integrity": "sha512-1A+do6/mLUWF9xd3uCrlXr9QFDbjbfqAYmUy8UDLOjof1lMrOhyeC4Yi6WexA/A8dhZEpIxSMCKfn7G4aHAh4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@expo/config": "~11.0.12",
|
||||
@@ -9140,6 +9116,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-1.1.0.tgz",
|
||||
"integrity": "sha512-DeB+fRe0hUDPZhpJ4X4bFMAItatFBUPjw/TVSbJsaf3Exeami+2qbbJhWkcTMoYHOB73nOIcaYcWXYJnCJXO0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
@@ -9231,7 +9208,8 @@
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/fast-xml-parser": {
|
||||
"version": "4.5.3",
|
||||
@@ -9755,7 +9733,8 @@
|
||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
@@ -10091,7 +10070,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6"
|
||||
},
|
||||
@@ -10165,7 +10143,6 @@
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"parent-module": "^1.0.0",
|
||||
@@ -10182,7 +10159,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
@@ -10937,7 +10913,6 @@
|
||||
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/core": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
@@ -11281,7 +11256,8 @@
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
|
||||
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/jest-get-type": {
|
||||
"version": "29.6.3",
|
||||
@@ -11954,7 +11930,6 @@
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
@@ -12336,6 +12311,7 @@
|
||||
"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6.11.5"
|
||||
}
|
||||
@@ -12359,7 +12335,6 @@
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
@@ -12601,7 +12576,6 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
|
||||
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.3"
|
||||
@@ -13382,7 +13356,6 @@
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
|
||||
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lower-case": "^2.0.2",
|
||||
@@ -13916,7 +13889,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"callsites": "^3.0.0"
|
||||
@@ -13939,7 +13911,6 @@
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
@@ -13999,7 +13970,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
|
||||
"integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
@@ -14061,7 +14031,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -14476,6 +14445,7 @@
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
@@ -14534,7 +14504,6 @@
|
||||
"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"
|
||||
}
|
||||
@@ -14593,7 +14562,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz",
|
||||
"integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
@@ -14642,7 +14610,6 @@
|
||||
"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",
|
||||
@@ -14862,7 +14829,6 @@
|
||||
"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",
|
||||
@@ -14898,7 +14864,6 @@
|
||||
"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": "*"
|
||||
@@ -14909,7 +14874,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.15.4.tgz",
|
||||
"integrity": "sha512-aKHPDScUbpQiZEG9eZssHdG5jEQs4yiJ8eMx6g81Ex/xU7DZkv3911enzdCb+v4eJE79X8waizY0ZhauZJQmrw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"react-freeze": "^1.0.0",
|
||||
"react-native-is-edge-to-edge": "^1.2.1",
|
||||
@@ -14925,7 +14889,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.11.2.tgz",
|
||||
"integrity": "sha512-+YfF72IbWQUKzCIydlijV1fLuBsQNGMT6Da2kFlo1sh+LE3BIm/2Q7AR1zAAR6L0BFLi1WaQPLfFUC9bNZpOmw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"css-select": "^5.1.0",
|
||||
"css-tree": "^1.1.3",
|
||||
@@ -14940,7 +14903,6 @@
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-svg-transformer/-/react-native-svg-transformer-1.5.1.tgz",
|
||||
"integrity": "sha512-dFvBNR8A9VPum9KCfh+LE49YiJEF8zUSnEFciKQroR/bEOhlPoZA0SuQ0qNk7m2iZl2w59FYjdRe0pMHWMDl0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@svgr/core": "^8.1.0",
|
||||
@@ -14968,7 +14930,6 @@
|
||||
"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"
|
||||
@@ -15536,6 +15497,7 @@
|
||||
"integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"ajv": "^8.9.0",
|
||||
@@ -15574,6 +15536,7 @@
|
||||
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3"
|
||||
},
|
||||
@@ -15586,7 +15549,8 @@
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/secure-remote-password": {
|
||||
"version": "0.3.0",
|
||||
@@ -15696,6 +15660,7 @@
|
||||
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
@@ -16107,7 +16072,6 @@
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
|
||||
"integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dot-case": "^3.0.4",
|
||||
@@ -16678,14 +16642,12 @@
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
|
||||
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/svgo": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz",
|
||||
"integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@trysound/sax": "0.2.0",
|
||||
@@ -16711,7 +16673,6 @@
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
@@ -16721,7 +16682,6 @@
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mdn-data": "2.0.30",
|
||||
@@ -16735,7 +16695,6 @@
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"dev": true,
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/symbol-tree": {
|
||||
@@ -16751,6 +16710,7 @@
|
||||
"integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
@@ -16849,6 +16809,7 @@
|
||||
"integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jest-worker": "^27.4.5",
|
||||
@@ -16884,6 +16845,7 @@
|
||||
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"merge-stream": "^2.0.0",
|
||||
@@ -16899,6 +16861,7 @@
|
||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
@@ -17131,7 +17094,6 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
@@ -17266,7 +17228,6 @@
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -17475,6 +17436,7 @@
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
@@ -17629,6 +17591,7 @@
|
||||
"integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.1.2"
|
||||
@@ -17662,6 +17625,7 @@
|
||||
"integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.8",
|
||||
@@ -17721,6 +17685,7 @@
|
||||
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esrecurse": "^4.3.0",
|
||||
"estraverse": "^4.1.1"
|
||||
@@ -17735,6 +17700,7 @@
|
||||
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
|
||||
@@ -38,10 +38,8 @@
|
||||
"@types/jsrsasign": "^10.5.15",
|
||||
"expo": "^53.0.22",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "^16.1.11",
|
||||
"expo-clipboard": "~7.1.5",
|
||||
"expo-constants": "~17.1.7",
|
||||
"expo-dev-client": "~5.1.8",
|
||||
"expo-document-picker": "~13.1.6",
|
||||
"expo-file-system": "~18.1.11",
|
||||
"expo-font": "~13.3.2",
|
||||
@@ -58,8 +56,7 @@
|
||||
"expo-web-browser": "~14.2.0",
|
||||
"fbemitter": "^3.0.0",
|
||||
"i18next": "^25.3.2",
|
||||
"jest": "~29.7.0",
|
||||
"jest-expo": "~53.0.10",
|
||||
"lodash": "^4.17.21",
|
||||
"otpauth": "^9.4.0",
|
||||
"react": "19.0.0",
|
||||
"react-hook-form": "^7.56.1",
|
||||
@@ -76,6 +73,7 @@
|
||||
"react-native-safe-area-context": "5.6.1",
|
||||
"react-native-screens": "~4.15.4",
|
||||
"react-native-svg": "15.11.2",
|
||||
"react-native-svg-transformer": "^1.5.0",
|
||||
"react-native-toast-message": "^2.2.1",
|
||||
"react-native-webview": "13.13.5",
|
||||
"secure-remote-password": "github:LinusU/secure-remote-password#73e5f732b6ca0cdbdc19da1a0c5f48cdbad2cbf0",
|
||||
@@ -98,10 +96,10 @@
|
||||
"eslint-config-expo": "~9.2.0",
|
||||
"eslint-plugin-jsdoc": "^55.2.0",
|
||||
"eslint-plugin-react-native": "^5.0.0",
|
||||
"expo-dev-client": "~5.1.8",
|
||||
"globals": "^16.3.0",
|
||||
"jest": "^29.2.1",
|
||||
"jest-expo": "~53.0.0",
|
||||
"react-native-svg-transformer": "^1.5.0",
|
||||
"typescript": "~5.8.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -105,6 +105,13 @@ export interface Spec extends TurboModule {
|
||||
// Re-authentication methods
|
||||
// Authenticate user with biometric or PIN. If title/subtitle are null/empty, defaults to "Unlock Vault" context.
|
||||
authenticateUser(title: string | null, subtitle: string | null): Promise<boolean>;
|
||||
|
||||
// QR code scanner
|
||||
// Scan a QR code and return the scanned data. Returns null if cancelled or failed.
|
||||
// If prefixes is provided, only QR codes starting with one of these prefixes will be accepted.
|
||||
// Scanner will keep scanning until a matching code is found or user cancels.
|
||||
// statusText is the message to display on the scanner screen (defaults to "Scan QR code" if null/empty).
|
||||
scanQRCode(prefixes: string[] | null, statusText: string | null): Promise<string | null>;
|
||||
}
|
||||
|
||||
export default TurboModuleRegistry.getEnforcing<Spec>('NativeVaultManager');
|
||||
|
||||
@@ -71,11 +71,11 @@
|
||||
</data>
|
||||
<!-- Alias Settings Section -->
|
||||
<data name="AliasSettingsTitle">
|
||||
<value>Identity Generator Settings</value>
|
||||
<value>Henkilöllisyysgeneraattorin asetukset</value>
|
||||
<comment>Title for identity generator settings section</comment>
|
||||
</data>
|
||||
<data name="AliasGenerationLanguageLabel">
|
||||
<value>Language</value>
|
||||
<value>Kieli</value>
|
||||
<comment>Label for alias generation language setting</comment>
|
||||
</data>
|
||||
<data name="AliasGenerationLanguageDescription">
|
||||
@@ -83,7 +83,7 @@
|
||||
<comment>Description for alias generation language setting</comment>
|
||||
</data>
|
||||
<data name="AliasGenerationGenderLabel">
|
||||
<value>Gender</value>
|
||||
<value>Sukupuoli</value>
|
||||
<comment>Label for alias generation gender setting</comment>
|
||||
</data>
|
||||
<data name="AliasGenerationGenderDescription">
|
||||
@@ -103,7 +103,7 @@
|
||||
<comment>Female gender option</comment>
|
||||
</data>
|
||||
<data name="AliasGenerationAgeRangeLabel">
|
||||
<value>Age range</value>
|
||||
<value>Ikähaarukka</value>
|
||||
<comment>Label for alias generation age range setting</comment>
|
||||
</data>
|
||||
<data name="AliasGenerationAgeRangeDescription">
|
||||
|
||||
@@ -213,7 +213,7 @@
|
||||
<comment>Generic error message</comment>
|
||||
</data>
|
||||
<data name="ErrorUnknown" xml:space="preserve">
|
||||
<value>An unknown error occurred. Please try again.</value>
|
||||
<value>Une erreur inconnue s'est produite. Merci de réessayer.</value>
|
||||
<comment>Generic unknown error message</comment>
|
||||
</data>
|
||||
<data name="ErrorValidation" xml:space="preserve">
|
||||
@@ -289,7 +289,7 @@
|
||||
</data>
|
||||
<!-- General UI text -->
|
||||
<data name="Or" xml:space="preserve">
|
||||
<value>or</value>
|
||||
<value>ou</value>
|
||||
<comment>Divider text between options</comment>
|
||||
</data>
|
||||
<data name="LockVault" xml:space="preserve">
|
||||
|
||||
Reference in New Issue
Block a user