Onboarding, Spacedrop & Location Settings Screen & Styled API (#596)

* fix wrong current lib logic

* add delete lib dialog to LibraryGeneralSettings

* add delete lib to mobile LibraryGeneralSettings too

* onboarding screens

* move zxcvbn to @sd/client

* get started screen and bloom

* merge fix

* move generatePassword back to interface

* add useZodForm to mobile and match react-hook-form versions

* new lib screen

* Implement styled api

* create lib screen and some tweaks

* password input

* fix password meter comp

* new library style tweaks

* Fix remove password bug (interface)

* master password screen

* privacy screen

* creating lib screen

* hexagons are cool

* Expo 48

* keyboard handling

* fix P2P on IOS

* fix types

* asset script

* new icons

* Spacedrop screen

* Fix mobile asset imports

* fix import cycle warning

* Edit Location Settings screen and style changes on other setting screens

* fix library creating bug? hopefully lol

* move PasswordMeter to interface

---------

Co-authored-by: Oscar Beaumont <oscar@otbeaumont.me>
This commit is contained in:
Utku 2023-03-17 05:00:02 +03:00 committed by GitHub
parent 3b4248c836
commit 902a3b5ba1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
109 changed files with 3846 additions and 2630 deletions

View file

@ -1,3 +0,0 @@
{
"*.{js,jsx,ts,tsx,html,json,yml,yaml,css,scss,md}": ["prettier --write"]
}

12
.vscode/settings.json vendored
View file

@ -38,12 +38,12 @@
"rust-analyzer.procMacro.enable": true,
"rust-analyzer.diagnostics.experimental.enable": false,
"tailwindCSS.experimental.classRegex": [
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
"tw`([^`]*)",
"tw\\.[^`]+`([^`]*)`",
"tw\\(.*?\\).*?`([^`]*)",
"tw\\.style\\(([^)]*)\\)",
"twStyle\\(([^)]*)\\)"
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], // cva(....)`...`
"tw\\.[^`]+`([^`]*)`", // tw.xxx`...`
"tw\\(.*?\\).*?`([^`]*)", // tw(....)`...`
"tw`([^`]*)", // tw`...` (mobile)
"twStyle\\(([^)]*)\\)", // twStyle(....) (mobile)
["styled\\([^,)]+,([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] // styled(....)`...` (mobile)
],
"search.exclude": {
"**/node_modules": true,

View file

@ -69,12 +69,12 @@ To run mobile app
- Install [Android Studio](https://developer.android.com/studio) for Android and [Xcode](https://apps.apple.com/au/app/xcode/id497799835) for IOS development
- `./.github/scripts/setup-system.sh mobile`
- The should setup most of the dependencies for the mobile app to build.
- You must also ensure [you must have NDK 24.0.8215888 and CMake](https://developer.android.com/studio/projects/install-ndk#default-version) in Android Studio
- `cd apps/mobile && pnpm i` - This is a separate workspace, you need to do this!
- You must also ensure [you must have NDK 23.1.7779620 and CMake](https://developer.android.com/studio/projects/install-ndk#default-version) in Android Studio
- `pnpm android` - runs on Android Emulator
- `pnpm ios` - runs on iOS Emulator
- `pnpm start` - For already bundled app
### Pull Request

5
Cargo.lock generated
View file

@ -3130,8 +3130,7 @@ dependencies = [
[[package]]
name = "if-watch"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba7abdbb86e485125dad06c2691e1e393bf3b08c7b743b43aa162a00fd39062e"
source = "git+https://github.com/oscartbeaumont/if-watch?rev=410e8e1d2d0730f1441df1c29294fec4c3c04193#410e8e1d2d0730f1441df1c29294fec4c3c04193"
dependencies = [
"async-io",
"core-foundation",
@ -4081,7 +4080,7 @@ dependencies = [
[[package]]
name = "mdns-sd"
version = "0.6.1"
source = "git+https://github.com/oscartbeaumont/mdns-sd.git?rev=45515a98e9e408c102871abaa5a9bff3bee0cbe8#45515a98e9e408c102871abaa5a9bff3bee0cbe8"
source = "git+https://github.com/oscartbeaumont/mdns-sd?rev=45515a98e9e408c102871abaa5a9bff3bee0cbe8#45515a98e9e408c102871abaa5a9bff3bee0cbe8"
dependencies = [
"flume",
"if-addrs",

View file

@ -40,8 +40,10 @@ tokio = { version = "1.25.0" }
[patch.crates-io]
# We use this patch so we can compile for the IOS simulator on M1
openssl-sys = { git = "https://github.com/spacedriveapp/rust-openssl", rev = "92c3dec225a9e984884d5b30a517e5d44a24d03b" }
# We patch this so that it can be built for IOS - The `main` branch uses macOS specific APIs
if-watch = { git = "https://github.com/oscartbeaumont/if-watch", rev = "410e8e1d2d0730f1441df1c29294fec4c3c04193" }
mdns-sd = { git = "https://github.com/oscartbeaumont/mdns-sd.git", rev = "45515a98e9e408c102871abaa5a9bff3bee0cbe8" } # TODO: Do upstream PR
mdns-sd = { git = "https://github.com/oscartbeaumont/mdns-sd", rev = "45515a98e9e408c102871abaa5a9bff3bee0cbe8" } # TODO: Do upstream PR
rspc = { git = "https://github.com/oscartbeaumont/rspc", rev = "c03872c0ba29d2429e9c059dfb235cdd03e15e8c" } # TODO: Move back to crates.io when new jsonrpc executor + `tokio::spawn` in the Tauri IPC plugin + upgraded Tauri version is released
specta = { git = "https://github.com/oscartbeaumont/rspc", rev = "c03872c0ba29d2429e9c059dfb235cdd03e15e8c" }

View file

@ -29,7 +29,7 @@
"react-burger-menu": "^3.0.8",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0",
"react-hook-form": "^7.36.1",
"react-hook-form": "^7.43.5",
"react-tsparticles": "^2.3.3",
"sirv": "^2.0.2",
"ts-node": "^10.9.1",

View file

@ -37,12 +37,6 @@ node_modules/
npm-debug.log
yarn-error.log
# BUCK
buck-out/
\.buckd/
*.keystore
!debug.keystore
# Bundle artifacts
*.jsbundle
@ -53,3 +47,6 @@ buck-out/
.expo/
web-build/
dist/
# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*

View file

@ -11,11 +11,5 @@ local.properties
*.iml
*.hprof
# BUCK
buck-out/
\.buckd/
*.keystore
!debug.keystore
# Bundle artifacts
*.jsbundle
*.jsbundle

View file

@ -1,55 +0,0 @@
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
lib_deps = []
create_aar_targets(glob(["libs/*.aar"]))
create_jar_targets(glob(["libs/*.jar"]))
android_library(
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = "build_config",
package = "com.spacedrive.app",
)
android_resource(
name = "res",
package = "com.spacedrive.app",
res = "src/main/res",
)
android_binary(
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)

View file

@ -1,8 +1,10 @@
apply plugin: "com.android.application"
apply plugin: "com.facebook.react"
import com.android.build.OutputFile
import org.apache.tools.ant.taskdefs.condition.Os
// SPACEDRIVE CODE
apply plugin: 'org.mozilla.rust-android-gradle.rust-android'
cargo {
@ -23,134 +25,108 @@ tasks.whenTaskAdded { task ->
}
}
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
// SPACEDRIVE CODE END
def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
def expoDebuggableVariants = ['debug']
// Override `debuggableVariants` for expo-updates debugging
if (System.getenv('EX_UPDATES_NATIVE_DEBUG') == "1") {
react {
expoDebuggableVariants = []
}
}
def reactNativeRoot = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath()
project.ext.react = [
entryFile: ["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android"].execute(null, rootDir).text.trim(),
enableHermes: (findProperty('expo.jsEngine') ?: "jsc") == "hermes",
hermesCommand: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc",
cliPath: "${reactNativeRoot}/cli.js",
composeSourceMapsPath: "${reactNativeRoot}/scripts/compose-source-maps.js",
]
apply from: new File(reactNativeRoot, "react.gradle")
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
* This is the configuration block to customize your React Native Android app.
* By default you don't need to apply any configuration, just uncomment the lines you need.
*/
react {
entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim())
reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc"
debuggableVariants = expoDebuggableVariants
// Use Expo CLI to bundle the app, this ensures the Metro config
// works correctly with Expo projects.
cliFile = new File(["node", "--print", "require.resolve('@expo/cli')"].execute(null, rootDir).text.trim())
bundleCommand = "export:embed"
/* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '..'
// root = file("../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
// reactNativeDir = file("../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen
// codegenDir = file("../node_modules/react-native-codegen")
/* Variants */
// The list of variants to that are debuggable. For those we're going to
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
// debuggableVariants = ["liteDebug", "prodDebug"]
/* Bundling */
// A list containing the node command and its flags. Default is just 'node'.
// nodeExecutableAndArgs = ["node"]
//
// The path to the CLI configuration file. Default is empty.
// bundleConfig = file(../rn-cli.config.js)
//
// The name of the generated asset file containing your JS bundle
// bundleAssetName = "MyApplication.android.bundle"
//
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
// entryFile = file("../js/MyApplication.android.js")
//
// A list of extra flags to pass to the 'bundle' commands.
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
// extraPackagerArgs = []
/* Hermes Commands */
// The hermes compiler command to run. By default it is 'hermesc'
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
//
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
// hermesFlags = ["-O", "-output-source-map"]
}
// Override `hermesEnabled` by `expo.jsEngine`
ext {
hermesEnabled = (findProperty('expo.jsEngine') ?: "hermes") == "hermes"
}
/**
* Set this to true to create four separate APKs instead of one,
* one for each native architecture. This is useful if you don't
* use App Bundles (https://developer.android.com/guide/app-bundle/)
* and want to have separate APKs to upload to the Play Store.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
*/
def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean()
/**
* The preferred build flavor of JavaScriptCore.
* The preferred build flavor of JavaScriptCore (JSC)
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and that value will be read here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
/**
* Architectures to build native code for.
* Private function to get the list of Native Architectures you want to build.
* This reads the value from reactNativeArchitectures in your gradle.properties
* file and works together with the --active-arch-only flag of react-native run-android.
*/
def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
@ -163,84 +139,11 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
applicationId 'com.spacedrive.app'
applicationId "com.spacedrive.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "0.0.1"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
ndkBuild {
arguments "APP_PLATFORM=android-21",
"APP_STL=c++_shared",
"NDK_TOOLCHAIN_VERSION=clang",
"GENERATED_SRC_DIR=$buildDir/generated/source",
"PROJECT_BUILD_DIR=$buildDir",
"REACT_ANDROID_DIR=${reactNativeRoot}/ReactAndroid",
"REACT_ANDROID_BUILD_DIR=${reactNativeRoot}/ReactAndroid/build",
"NODE_MODULES_DIR=$rootDir/../node_modules"
cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
cppFlags "-std=c++17"
// Make sure this target name is the same you specify inside the
// src/main/jni/Android.mk file for the `LOCAL_MODULE` variable.
targets "spacedrive_appmodules"
// Fix for windows limit on number of character in file paths and in command lines
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
arguments "NDK_APP_SHORT_COMMANDS=true"
}
}
}
if (!enableSeparateBuildPerCPUArchitecture) {
ndk {
abiFilters (*reactNativeArchitectures())
}
}
}
}
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
ndkBuild {
path "$projectDir/src/main/jni/Android.mk"
}
}
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
afterEvaluate {
// If you wish to add a custom TurboModule or component locally,
// you should uncomment this line.
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
// Due to a bug inside AGP, we have to explicitly set a dependency
// between configureNdkBuild* tasks and the preBuild tasks.
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
configureNdkBuildRelease.dependsOn(preReleaseBuild)
configureNdkBuildDebug.dependsOn(preDebugBuild)
reactNativeArchitectures().each { architecture ->
tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure {
dependsOn("preDebugBuild")
}
tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure {
dependsOn("preReleaseBuild")
}
}
}
}
splits {
@ -277,11 +180,12 @@ android {
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
}
}
@ -309,10 +213,8 @@ android {
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
@ -321,76 +223,38 @@ dependencies {
// If your app supports Android versions before Ice Cream Sandwich (API level 14)
if (isGifEnabled || isWebpEnabled) {
implementation "com.facebook.fresco:fresco:${frescoVersion}"
implementation "com.facebook.fresco:imagepipeline-okhttp3:${frescoVersion}"
implementation("com.facebook.fresco:fresco:${frescoVersion}")
implementation("com.facebook.fresco:imagepipeline-okhttp3:${frescoVersion}")
}
if (isGifEnabled) {
// For animated gif support
implementation "com.facebook.fresco:animated-gif:${frescoVersion}"
implementation("com.facebook.fresco:animated-gif:${frescoVersion}")
}
if (isWebpEnabled) {
// For webp support
implementation "com.facebook.fresco:webpsupport:${frescoVersion}"
implementation("com.facebook.fresco:webpsupport:${frescoVersion}")
if (isWebpAnimatedEnabled) {
// Animated webp support
implementation "com.facebook.fresco:animated-webp:${frescoVersion}"
implementation("com.facebook.fresco:animated-webp:${frescoVersion}")
}
}
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
if (enableHermes) {
//noinspection GradleDynamicVersion
implementation("com.facebook.react:hermes-engine:+") { // From node_modules
exclude group:'com.facebook.fbjni'
}
if (hermesEnabled.toBoolean()) {
implementation("com.facebook.react:hermes-android")
} else {
implementation jscFlavor
}
}
if (isNewArchitectureEnabled()) {
// If new architecture is enabled, we let you build RN from source
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
// This will be applied to all the imported transtitive dependency.
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("com.facebook.react:react-native"))
.using(project(":ReactAndroid"))
.because("On New Architecture we're building React Native from source")
substitute(module("com.facebook.react:hermes-engine"))
.using(project(":ReactAndroid:hermes-engine"))
.because("On New Architecture we're building Hermes from source")
}
}
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.implementation
into 'libs'
}
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
applyNativeModulesAppBuildGradle(project)
def isNewArchitectureEnabled() {
// To opt-in for the New Architecture, you can either:
// - Set `newArchEnabled` to true inside the `gradle.properties` file
// - Invoke gradle with `-newArchEnabled=true`
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}
applyNativeModulesAppBuildGradle(project)

View file

@ -1,19 +0,0 @@
"""Helper definitions to glob .aar and .jar targets"""
def create_aar_targets(aarfiles):
for aarfile in aarfiles:
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
lib_deps.append(":" + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
def create_jar_targets(jarfiles):
for jarfile in jarfiles:
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
lib_deps.append(":" + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)

View file

@ -11,4 +11,4 @@
-keep class com.swmansion.reanimated.** { *; }
-keep class com.facebook.react.turbomodule.** { *; }
# Add any project specific keep options here:
# Add any project specific keep options here:

View file

@ -1,5 +1,5 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
@ -17,22 +17,27 @@ import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceEventListener;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;
/**
* Class responsible of loading Flipper inside your React Native application. This is the debug
* flavor of it. Here you can add your own plugins and customize the Flipper setup.
*/
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
NetworkingModule.setCustomClientBuilder(
new NetworkingModule.CustomClientBuilder() {
@ -43,12 +48,13 @@ public class ReactNativeFlipper {
});
client.addPlugin(networkFlipperPlugin);
client.start();
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
// Hence we run if after all native modules have been initialized
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
new ReactInstanceManager.ReactInstanceEventListener() {
new ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
reactInstanceManager.removeReactInstanceEventListener(this);

View file

@ -5,7 +5,8 @@ import android.os.Bundle;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;
import expo.modules.ReactActivityDelegateWrapper;
@ -29,15 +30,20 @@ public class MainActivity extends ReactActivity {
}
/**
* Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
* you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer
* (Paper).
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
* (aka React 18) with two boolean flags.
*/
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED,
new MainActivityDelegate(this, getMainComponentName())
);
return new ReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, new DefaultReactActivityDelegate(
this,
getMainComponentName(),
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled
// If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18).
DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
));
}
/**
@ -59,25 +65,4 @@ public class MainActivity extends ReactActivity {
// because it's doing more than {@link Activity#moveTaskToBack} in fact.
super.invokeDefaultOnBackPressed();
}
public static class MainActivityDelegate extends ReactActivityDelegate {
public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
super(activity, mainComponentName);
}
@Override
protected ReactRootView createRootView() {
ReactRootView reactRootView = new ReactRootView(getContext());
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
return reactRootView;
}
@Override
protected boolean isConcurrentRootEnabled() {
// If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18).
// More on this on https://reactjs.org/blog/2022/03/29/react-v18.html
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
}
}
}

View file

@ -1,69 +1,70 @@
package com.spacedrive.app;
import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
import androidx.annotation.NonNull;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactNativeHost;
import com.facebook.soloader.SoLoader;
import com.spacedrive.app.newarchitecture.MainApplicationReactNativeHost;
import expo.modules.ApplicationLifecycleDispatcher;
import expo.modules.ReactNativeHostWrapper;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHostWrapper(
this,
new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
packages.add(new com.spacedrive.app.SpacedrivePackage());
return packages;
}
private final ReactNativeHost mReactNativeHost =
new ReactNativeHostWrapper(this, new DefaultReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
packages.add(new com.spacedrive.app.SpacedrivePackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@Override
protected boolean isNewArchEnabled() {
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
@Override
protected Boolean isHermesEnabled() {
return BuildConfig.IS_HERMES_ENABLED;
}
});
private final ReactNativeHost mNewArchitectureNativeHost =
new ReactNativeHostWrapper(this, new MainApplicationReactNativeHost(this));
@Override
public ReactNativeHost getReactNativeHost() {
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
return mNewArchitectureNativeHost;
} else {
return mReactNativeHost;
}
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
// If you opted-in for the New Architecture, we enable the TurboModule system
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
DefaultNewArchitectureEntryPoint.load();
}
ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
ApplicationLifecycleDispatcher.onApplicationCreate(this);
}
@ -72,35 +73,4 @@ public class MainApplication extends Application implements ReactApplication {
super.onConfigurationChanged(newConfig);
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("com.spacedrive.app.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}

View file

@ -1,117 +0,0 @@
package com.spacedrive.app.newarchitecture;
import android.app.Application;
import androidx.annotation.NonNull;
import com.facebook.react.PackageList;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
import com.facebook.react.bridge.JSIModulePackage;
import com.facebook.react.bridge.JSIModuleProvider;
import com.facebook.react.bridge.JSIModuleSpec;
import com.facebook.react.bridge.JSIModuleType;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.react.fabric.CoreComponentsRegistry;
import com.facebook.react.fabric.EmptyReactNativeConfig;
import com.facebook.react.fabric.FabricJSIModuleProvider;
import com.facebook.react.fabric.ReactNativeConfig;
import com.facebook.react.uimanager.ViewManagerRegistry;
import com.spacedrive.app.BuildConfig;
import com.spacedrive.app.newarchitecture.components.MainComponentsRegistry;
import com.spacedrive.app.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
import java.util.ArrayList;
import java.util.List;
/**
* A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
* TurboModule delegates and the Fabric Renderer.
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
public class MainApplicationReactNativeHost extends ReactNativeHost {
public MainApplicationReactNativeHost(Application application) {
super(application);
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
// TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
// packages.add(new TurboReactPackage() { ... });
// If you have custom Fabric Components, their ViewManagers should also be loaded here
// inside a ReactPackage.
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@NonNull
@Override
protected ReactPackageTurboModuleManagerDelegate.Builder
getReactPackageTurboModuleManagerDelegateBuilder() {
// Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
// for the new architecture and to use TurboModules correctly.
return new MainApplicationTurboModuleManagerDelegate.Builder();
}
@Override
protected JSIModulePackage getJSIModulePackage() {
return new JSIModulePackage() {
@Override
public List<JSIModuleSpec> getJSIModules(
final ReactApplicationContext reactApplicationContext,
final JavaScriptContextHolder jsContext) {
final List<JSIModuleSpec> specs = new ArrayList<>();
// Here we provide a new JSIModuleSpec that will be responsible of providing the
// custom Fabric Components.
specs.add(
new JSIModuleSpec() {
@Override
public JSIModuleType getJSIModuleType() {
return JSIModuleType.UIManager;
}
@Override
public JSIModuleProvider<UIManager> getJSIModuleProvider() {
final ComponentFactory componentFactory = new ComponentFactory();
CoreComponentsRegistry.register(componentFactory);
// Here we register a Components Registry.
// The one that is generated with the template contains no components
// and just provides you the one from React Native core.
MainComponentsRegistry.register(componentFactory);
final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
ViewManagerRegistry viewManagerRegistry =
new ViewManagerRegistry(
reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
return new FabricJSIModuleProvider(
reactApplicationContext,
componentFactory,
ReactNativeConfig.DEFAULT_CONFIG,
viewManagerRegistry);
}
});
return specs;
}
};
}
}

View file

@ -1,36 +0,0 @@
package com.spacedrive.app.newarchitecture.components;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.soloader.SoLoader;
/**
* Class responsible to load the custom Fabric Components. This class has native methods and needs a
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
* folder for you).
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
@DoNotStrip
public class MainComponentsRegistry {
static {
SoLoader.loadLibrary("fabricjni");
}
@DoNotStrip private final HybridData mHybridData;
@DoNotStrip
private native HybridData initHybrid(ComponentFactory componentFactory);
@DoNotStrip
private MainComponentsRegistry(ComponentFactory componentFactory) {
mHybridData = initHybrid(componentFactory);
}
@DoNotStrip
public static MainComponentsRegistry register(ComponentFactory componentFactory) {
return new MainComponentsRegistry(componentFactory);
}
}

View file

@ -1,48 +0,0 @@
package com.spacedrive.app.newarchitecture.modules;
import com.facebook.jni.HybridData;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.soloader.SoLoader;
import java.util.List;
/**
* Class responsible to load the TurboModules. This class has native methods and needs a
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
* folder for you).
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
public class MainApplicationTurboModuleManagerDelegate
extends ReactPackageTurboModuleManagerDelegate {
private static volatile boolean sIsSoLibraryLoaded;
protected MainApplicationTurboModuleManagerDelegate(
ReactApplicationContext reactApplicationContext, List<ReactPackage> packages) {
super(reactApplicationContext, packages);
}
protected native HybridData initHybrid();
native boolean canCreateTurboModule(String moduleName);
public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
protected MainApplicationTurboModuleManagerDelegate build(
ReactApplicationContext context, List<ReactPackage> packages) {
return new MainApplicationTurboModuleManagerDelegate(context, packages);
}
}
@Override
protected synchronized void maybeLoadOtherSoLibraries() {
if (!sIsSoLibraryLoaded) {
// If you change the name of your application .so file in the Android.mk file,
// make sure you update the name here as well.
SoLoader.loadLibrary("spacedrive_appmodules");
sIsSoLibraryLoaded = true;
}
}
}

View file

@ -1,24 +0,0 @@
#include "MainApplicationModuleProvider.h"
#include <rncore.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
const std::string moduleName,
const JavaTurboModule::InitParams &params) {
// Here you can provide your own module provider for TurboModules coming from
// either your application or from external libraries. The approach to follow
// is similar to the following (for a library called `samplelibrary`:
//
// auto module = samplelibrary_ModuleProvider(moduleName, params);
// if (module != nullptr) {
// return module;
// }
// return rncore_ModuleProvider(moduleName, params);
return rncore_ModuleProvider(moduleName, params);
}
} // namespace react
} // namespace facebook

View file

@ -1,16 +0,0 @@
#pragma once
#include <memory>
#include <string>
#include <ReactCommon/JavaTurboModule.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
const std::string moduleName,
const JavaTurboModule::InitParams &params);
} // namespace react
} // namespace facebook

View file

@ -1,45 +0,0 @@
#include "MainApplicationTurboModuleManagerDelegate.h"
#include "MainApplicationModuleProvider.h"
namespace facebook {
namespace react {
jni::local_ref<MainApplicationTurboModuleManagerDelegate::jhybriddata>
MainApplicationTurboModuleManagerDelegate::initHybrid(
jni::alias_ref<jhybridobject>) {
return makeCxxInstance();
}
void MainApplicationTurboModuleManagerDelegate::registerNatives() {
registerHybrid({
makeNativeMethod(
"initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
makeNativeMethod(
"canCreateTurboModule",
MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
});
}
std::shared_ptr<TurboModule>
MainApplicationTurboModuleManagerDelegate::getTurboModule(
const std::string name,
const std::shared_ptr<CallInvoker> jsInvoker) {
// Not implemented yet: provide pure-C++ NativeModules here.
return nullptr;
}
std::shared_ptr<TurboModule>
MainApplicationTurboModuleManagerDelegate::getTurboModule(
const std::string name,
const JavaTurboModule::InitParams &params) {
return MainApplicationModuleProvider(name, params);
}
bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
std::string name) {
return getTurboModule(name, nullptr) != nullptr ||
getTurboModule(name, {.moduleName = name}) != nullptr;
}
} // namespace react
} // namespace facebook

View file

@ -1,38 +0,0 @@
#include <memory>
#include <string>
#include <ReactCommon/TurboModuleManagerDelegate.h>
#include <fbjni/fbjni.h>
namespace facebook {
namespace react {
class MainApplicationTurboModuleManagerDelegate
: public jni::HybridClass<
MainApplicationTurboModuleManagerDelegate,
TurboModuleManagerDelegate> {
public:
// Adapt it to the package you used for your Java class.
static constexpr auto kJavaDescriptor =
"Lcom/spacedrive/app/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>);
static void registerNatives();
std::shared_ptr<TurboModule> getTurboModule(
const std::string name,
const std::shared_ptr<CallInvoker> jsInvoker) override;
std::shared_ptr<TurboModule> getTurboModule(
const std::string name,
const JavaTurboModule::InitParams &params) override;
/**
* Test-only method. Allows user to verify whether a TurboModule can be
* created by instances of this class.
*/
bool canCreateTurboModule(std::string name);
};
} // namespace react
} // namespace facebook

View file

@ -1,61 +0,0 @@
#include "MainComponentsRegistry.h"
#include <CoreComponentsRegistry.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/components/rncore/ComponentDescriptors.h>
namespace facebook {
namespace react {
MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
std::shared_ptr<ComponentDescriptorProviderRegistry const>
MainComponentsRegistry::sharedProviderRegistry() {
auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
// Custom Fabric Components go here. You can register custom
// components coming from your App or from 3rd party libraries here.
//
// providerRegistry->add(concreteComponentDescriptorProvider<
// AocViewerComponentDescriptor>());
return providerRegistry;
}
jni::local_ref<MainComponentsRegistry::jhybriddata>
MainComponentsRegistry::initHybrid(
jni::alias_ref<jclass>,
ComponentFactory *delegate) {
auto instance = makeCxxInstance(delegate);
auto buildRegistryFunction =
[](EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer)
-> ComponentDescriptorRegistry::Shared {
auto registry = MainComponentsRegistry::sharedProviderRegistry()
->createComponentDescriptorRegistry(
{eventDispatcher, contextContainer});
auto mutableRegistry =
std::const_pointer_cast<ComponentDescriptorRegistry>(registry);
mutableRegistry->setFallbackComponentDescriptor(
std::make_shared<UnimplementedNativeViewComponentDescriptor>(
ComponentDescriptorParameters{
eventDispatcher, contextContainer, nullptr}));
return registry;
};
delegate->buildRegistryFunction = buildRegistryFunction;
return instance;
}
void MainComponentsRegistry::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
});
}
} // namespace react
} // namespace facebook

View file

@ -1,32 +0,0 @@
#pragma once
#include <ComponentFactory.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>
namespace facebook {
namespace react {
class MainComponentsRegistry
: public facebook::jni::HybridClass<MainComponentsRegistry> {
public:
// Adapt it to the package you used for your Java class.
constexpr static auto kJavaDescriptor =
"Lcom/spacedrive/app/newarchitecture/components/MainComponentsRegistry;";
static void registerNatives();
MainComponentsRegistry(ComponentFactory *delegate);
private:
static std::shared_ptr<ComponentDescriptorProviderRegistry const>
sharedProviderRegistry();
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jclass>,
ComponentFactory *delegate);
};
} // namespace react
} // namespace facebook

View file

@ -1,11 +0,0 @@
#include <fbjni/fbjni.h>
#include "MainApplicationTurboModuleManagerDelegate.h"
#include "MainComponentsRegistry.h"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::jni::initialize(vm, [] {
facebook::react::MainApplicationTurboModuleManagerDelegate::
registerNatives();
facebook::react::MainComponentsRegistry::registerNatives();
});
}

View file

@ -0,0 +1,20 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package com.spacedrive.app;
import android.content.Context;
import com.facebook.react.ReactInstanceManager;
/**
* Class responsible of loading Flipper inside your React Native application. This is the release
* flavor of it so it's empty as we don't want to load Flipper.
*/
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
// Do nothing as we don't want to initialize Flipper on Release.
}
}

View file

@ -4,38 +4,31 @@ import org.apache.tools.ant.taskdefs.condition.Os
buildscript {
ext {
buildToolsVersion = findProperty('android.buildToolsVersion') ?: '31.0.0'
buildToolsVersion = findProperty('android.buildToolsVersion') ?: '33.0.0'
minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '21')
compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '31')
targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '31')
reactNativeVersion = "0.69.4" // https://github.com/expo/expo/issues/18129
compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '33')
targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '33')
if (findProperty('android.kotlinVersion')) {
kotlinVersion = findProperty('android.kotlinVersion')
}
frescoVersion = findProperty('expo.frescoVersion') ?: '2.5.0'
if (System.properties['os.arch'] == 'aarch64') {
// For M1 Users we need to use the NDK 24 which added support for aarch64
ndkVersion = '24.0.8215888'
} else {
// Otherwise we default to the side-by-side NDK version from AGP.
ndkVersion = '21.4.7075529'
}
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
ndkVersion = "23.1.7779620"
}
repositories {
google()
mavenCentral()
// Spacedrive -- Required for the Android Rust plugin.
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath('com.android.tools.build:gradle:7.1.1')
classpath('com.android.tools.build:gradle:7.4.1')
classpath('com.facebook.react:react-native-gradle-plugin')
classpath('de.undercouch:gradle-download-task:5.0.1')
// Spacedrive -- Rust plugin.
classpath('org.mozilla.rust-android-gradle:plugin:0.9.3')
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
@ -52,13 +45,7 @@ allprojects {
}
google()
mavenCentral {
// We don't want to fetch react-native from Maven Central as there are
// older versions over there.
content {
excludeGroup 'com.facebook.react'
}
}
mavenCentral()
maven { url 'https://www.jitpack.io' }
}
}

View file

@ -50,4 +50,4 @@ expo.gif.enabled=true
expo.webp.enabled=true
# Enable animated webp support (~3.4 MB increase)
# Disabled by default because iOS doesn't support animated webp
expo.webp.animated=false
expo.webp.animated=false

View file

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStorePath=wrapper/dists

View file

@ -205,6 +205,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
@ -231,4 +237,4 @@ eval "set -- $(
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
exec "$JAVACMD" "$@"

View file

@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -75,15 +75,17 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
:omega

View file

@ -7,11 +7,4 @@ apply from: new File(["node", "--print", "require.resolve('@react-native-communi
applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild(new File(["node", "--print", "require.resolve('react-native-gradle-plugin/package.json')"].execute(null, rootDir).text.trim()).getParentFile())
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
include(":ReactAndroid")
project(":ReactAndroid").projectDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../ReactAndroid");
include(":ReactAndroid:hermes-engine")
project(":ReactAndroid:hermes-engine").projectDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../ReactAndroid/hermes-engine");
}
includeBuild(new File(["node", "--print", "require.resolve('react-native-gradle-plugin/package.json')"].execute(null, rootDir).text.trim()).getParentFile())

View file

@ -7,6 +7,7 @@
"orientation": "portrait",
"jsEngine": "hermes",
"scheme": "spacedrive",
"platforms": ["ios", "android"],
"userInterfaceStyle": "automatic",
"updates": {
"enabled": false,

View file

@ -5,34 +5,62 @@ require File.join(File.dirname(`node --print "require.resolve('@react-native-com
require 'json'
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0'
platform :ios, podfile_properties['ios.deploymentTarget'] || '13.0'
install! 'cocoapods',
:deterministic_uuids => false
prepare_react_native_project!
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
# because `react-native-flipper` depends on (FlipperKit,...), which will be excluded. To fix this,
# you can also exclude `react-native-flipper` in `react-native.config.js`
#
# ```js
# module.exports = {
# dependencies: {
# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
# }
# }
# ```
flipper_config = FlipperConfiguration.disabled
if ENV['NO_FLIPPER'] == '1' then
# Explicitly disabled through environment variables
flipper_config = FlipperConfiguration.disabled
elsif podfile_properties.key?('ios.flipper') then
# Configure Flipper in Podfile.properties.json
if podfile_properties['ios.flipper'] == 'true' then
flipper_config = FlipperConfiguration.enabled(["Debug", "Release"])
elsif podfile_properties['ios.flipper'] != 'false' then
flipper_config = FlipperConfiguration.enabled(["Debug", "Release"], { 'Flipper' => podfile_properties['ios.flipper'] })
end
end
target 'Spacedrive' do
use_expo_modules!
config = use_native_modules!
use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
# Flags change depending on the env values.
flags = get_default_flags()
use_react_native!(
:path => config[:reactNativePath],
:hermes_enabled => podfile_properties['expo.jsEngine'] == 'hermes',
:hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
:fabric_enabled => flags[:fabric_enabled],
# An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/..",
#
# Uncomment to opt-in to using Flipper
# Note that if you have use_frameworks! enabled, Flipper will not work
# :flipper_configuration => !ENV['CI'] ? FlipperConfiguration.enabled : FlipperConfiguration.disabled,
# Note that if you have use_frameworks! enabled, Flipper will not work if enabled
:flipper_configuration => flipper_config
)
post_install do |installer|
react_native_post_install(
installer,
config[:reactNativePath],
# Set `mac_catalyst_enabled` to `true` in order to apply patches
# necessary for Mac Catalyst builds
:mac_catalyst_enabled => false

View file

@ -1,38 +1,41 @@
PODS:
- boost (1.76.0)
- DoubleConversion (1.1.6)
- EXApplication (5.0.1):
- EXApplication (5.1.1):
- ExpoModulesCore
- EXConstants (14.0.2):
- EXConstants (14.2.1):
- ExpoModulesCore
- EXFileSystem (15.1.1):
- EXFileSystem (15.2.2):
- ExpoModulesCore
- EXFont (11.0.1):
- EXFont (11.1.1):
- ExpoModulesCore
- EXMediaLibrary (15.0.0):
- EXMediaLibrary (15.2.2):
- ExpoModulesCore
- React-Core
- Expo (47.0.13):
- Expo (48.0.6):
- ExpoModulesCore
- ExpoKeepAwake (11.0.1):
- ExpoKeepAwake (12.0.1):
- ExpoModulesCore
- ExpoModulesCore (1.1.1):
- ExpoModulesCore (1.2.4):
- React-Core
- React-RCTAppDelegate
- ReactCommon/turbomodule/core
- EXSplashScreen (0.17.5):
- EXSplashScreen (0.18.1):
- ExpoModulesCore
- React-Core
- FBLazyVector (0.70.5)
- FBReactNativeSpec (0.70.5):
- FBLazyVector (0.71.3)
- FBReactNativeSpec (0.71.3):
- RCT-Folly (= 2021.07.22.00)
- RCTRequired (= 0.70.5)
- RCTTypeSafety (= 0.70.5)
- React-Core (= 0.70.5)
- React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.70.5)
- RCTRequired (= 0.71.3)
- RCTTypeSafety (= 0.71.3)
- React-Core (= 0.71.3)
- React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.3)
- fmt (6.2.1)
- glog (0.3.5)
- hermes-engine (0.70.5)
- hermes-engine (0.71.3):
- hermes-engine/Pre-built (= 0.71.3)
- hermes-engine/Pre-built (0.71.3)
- libevent (2.1.12)
- lottie-ios (3.4.4)
- lottie-react-native (5.1.4):
@ -55,214 +58,239 @@ PODS:
- fmt (~> 6.2.1)
- glog
- libevent
- RCTRequired (0.70.5)
- RCTTypeSafety (0.70.5):
- FBLazyVector (= 0.70.5)
- RCTRequired (= 0.70.5)
- React-Core (= 0.70.5)
- React (0.70.5):
- React-Core (= 0.70.5)
- React-Core/DevSupport (= 0.70.5)
- React-Core/RCTWebSocket (= 0.70.5)
- React-RCTActionSheet (= 0.70.5)
- React-RCTAnimation (= 0.70.5)
- React-RCTBlob (= 0.70.5)
- React-RCTImage (= 0.70.5)
- React-RCTLinking (= 0.70.5)
- React-RCTNetwork (= 0.70.5)
- React-RCTSettings (= 0.70.5)
- React-RCTText (= 0.70.5)
- React-RCTVibration (= 0.70.5)
- React-bridging (0.70.5):
- RCT-Folly (= 2021.07.22.00)
- React-jsi (= 0.70.5)
- React-callinvoker (0.70.5)
- React-Codegen (0.70.5):
- FBReactNativeSpec (= 0.70.5)
- RCT-Folly (= 2021.07.22.00)
- RCTRequired (= 0.70.5)
- RCTTypeSafety (= 0.70.5)
- React-Core (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.70.5)
- React-Core (0.70.5):
- RCTRequired (0.71.3)
- RCTTypeSafety (0.71.3):
- FBLazyVector (= 0.71.3)
- RCTRequired (= 0.71.3)
- React-Core (= 0.71.3)
- React (0.71.3):
- React-Core (= 0.71.3)
- React-Core/DevSupport (= 0.71.3)
- React-Core/RCTWebSocket (= 0.71.3)
- React-RCTActionSheet (= 0.71.3)
- React-RCTAnimation (= 0.71.3)
- React-RCTBlob (= 0.71.3)
- React-RCTImage (= 0.71.3)
- React-RCTLinking (= 0.71.3)
- React-RCTNetwork (= 0.71.3)
- React-RCTSettings (= 0.71.3)
- React-RCTText (= 0.71.3)
- React-RCTVibration (= 0.71.3)
- React-callinvoker (0.71.3)
- React-Codegen (0.71.3):
- FBReactNativeSpec
- hermes-engine
- RCT-Folly
- RCTRequired
- RCTTypeSafety
- React-Core
- React-jsi
- React-jsiexecutor
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- React-Core (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.70.5)
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-Core/Default (= 0.71.3)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/CoreModulesHeaders (0.70.5):
- React-Core/CoreModulesHeaders (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/Default (0.70.5):
- React-Core/Default (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/DevSupport (0.70.5):
- React-Core/DevSupport (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.70.5)
- React-Core/RCTWebSocket (= 0.70.5)
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-jsinspector (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-Core/Default (= 0.71.3)
- React-Core/RCTWebSocket (= 0.71.3)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-jsinspector (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/RCTActionSheetHeaders (0.70.5):
- React-Core/RCTActionSheetHeaders (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/RCTAnimationHeaders (0.70.5):
- React-Core/RCTAnimationHeaders (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/RCTBlobHeaders (0.70.5):
- React-Core/RCTBlobHeaders (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/RCTImageHeaders (0.70.5):
- React-Core/RCTImageHeaders (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/RCTLinkingHeaders (0.70.5):
- React-Core/RCTLinkingHeaders (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/RCTNetworkHeaders (0.70.5):
- React-Core/RCTNetworkHeaders (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/RCTSettingsHeaders (0.70.5):
- React-Core/RCTSettingsHeaders (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/RCTTextHeaders (0.70.5):
- React-Core/RCTTextHeaders (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/RCTVibrationHeaders (0.70.5):
- React-Core/RCTVibrationHeaders (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-Core/RCTWebSocket (0.70.5):
- React-Core/RCTWebSocket (0.71.3):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.70.5)
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-Core/Default (= 0.71.3)
- React-cxxreact (= 0.71.3)
- React-hermes
- React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.3)
- Yoga
- React-CoreModules (0.70.5):
- React-CoreModules (0.71.3):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.70.5)
- React-Codegen (= 0.70.5)
- React-Core/CoreModulesHeaders (= 0.70.5)
- React-jsi (= 0.70.5)
- React-RCTImage (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.70.5)
- React-cxxreact (0.70.5):
- RCTTypeSafety (= 0.71.3)
- React-Codegen (= 0.71.3)
- React-Core/CoreModulesHeaders (= 0.71.3)
- React-jsi (= 0.71.3)
- React-RCTBlob
- React-RCTImage (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.3)
- React-cxxreact (0.71.3):
- boost (= 1.76.0)
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-callinvoker (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsinspector (= 0.70.5)
- React-logger (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-runtimeexecutor (= 0.70.5)
- React-hermes (0.70.5):
- React-callinvoker (= 0.71.3)
- React-jsi (= 0.71.3)
- React-jsinspector (= 0.71.3)
- React-logger (= 0.71.3)
- React-perflogger (= 0.71.3)
- React-runtimeexecutor (= 0.71.3)
- React-hermes (0.71.3):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly/Futures (= 2021.07.22.00)
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-jsiexecutor (= 0.70.5)
- React-jsinspector (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-jsi (0.70.5):
- React-cxxreact (= 0.71.3)
- React-jsi
- React-jsiexecutor (= 0.71.3)
- React-jsinspector (= 0.71.3)
- React-perflogger (= 0.71.3)
- React-jsi (0.71.3):
- boost (= 1.76.0)
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-jsi/Default (= 0.70.5)
- React-jsi/Default (0.70.5):
- boost (= 1.76.0)
- React-jsiexecutor (0.71.3):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-jsiexecutor (0.70.5):
- DoubleConversion
- glog
- RCT-Folly (= 2021.07.22.00)
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-jsinspector (0.70.5)
- React-logger (0.70.5):
- React-cxxreact (= 0.71.3)
- React-jsi (= 0.71.3)
- React-perflogger (= 0.71.3)
- React-jsinspector (0.71.3)
- React-logger (0.71.3):
- glog
- react-native-document-picker (8.1.3):
- React-Core
@ -272,77 +300,95 @@ PODS:
- RCTTypeSafety
- React-Core
- ReactCommon/turbomodule/core
- React-perflogger (0.70.5)
- React-RCTActionSheet (0.70.5):
- React-Core/RCTActionSheetHeaders (= 0.70.5)
- React-RCTAnimation (0.70.5):
- React-perflogger (0.71.3)
- React-RCTActionSheet (0.71.3):
- React-Core/RCTActionSheetHeaders (= 0.71.3)
- React-RCTAnimation (0.71.3):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.70.5)
- React-Codegen (= 0.70.5)
- React-Core/RCTAnimationHeaders (= 0.70.5)
- React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTBlob (0.70.5):
- RCTTypeSafety (= 0.71.3)
- React-Codegen (= 0.71.3)
- React-Core/RCTAnimationHeaders (= 0.71.3)
- React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTAppDelegate (0.71.3):
- RCT-Folly
- RCTRequired
- RCTTypeSafety
- React-Core
- ReactCommon/turbomodule/core
- React-RCTBlob (0.71.3):
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Codegen (= 0.70.5)
- React-Core/RCTBlobHeaders (= 0.70.5)
- React-Core/RCTWebSocket (= 0.70.5)
- React-jsi (= 0.70.5)
- React-RCTNetwork (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTImage (0.70.5):
- React-Codegen (= 0.71.3)
- React-Core/RCTBlobHeaders (= 0.71.3)
- React-Core/RCTWebSocket (= 0.71.3)
- React-jsi (= 0.71.3)
- React-RCTNetwork (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTImage (0.71.3):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.70.5)
- React-Codegen (= 0.70.5)
- React-Core/RCTImageHeaders (= 0.70.5)
- React-jsi (= 0.70.5)
- React-RCTNetwork (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTLinking (0.70.5):
- React-Codegen (= 0.70.5)
- React-Core/RCTLinkingHeaders (= 0.70.5)
- React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTNetwork (0.70.5):
- RCTTypeSafety (= 0.71.3)
- React-Codegen (= 0.71.3)
- React-Core/RCTImageHeaders (= 0.71.3)
- React-jsi (= 0.71.3)
- React-RCTNetwork (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTLinking (0.71.3):
- React-Codegen (= 0.71.3)
- React-Core/RCTLinkingHeaders (= 0.71.3)
- React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTNetwork (0.71.3):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.70.5)
- React-Codegen (= 0.70.5)
- React-Core/RCTNetworkHeaders (= 0.70.5)
- React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTSettings (0.70.5):
- RCTTypeSafety (= 0.71.3)
- React-Codegen (= 0.71.3)
- React-Core/RCTNetworkHeaders (= 0.71.3)
- React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTSettings (0.71.3):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.70.5)
- React-Codegen (= 0.70.5)
- React-Core/RCTSettingsHeaders (= 0.70.5)
- React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.70.5)
- React-RCTText (0.70.5):
- React-Core/RCTTextHeaders (= 0.70.5)
- React-RCTVibration (0.70.5):
- RCTTypeSafety (= 0.71.3)
- React-Codegen (= 0.71.3)
- React-Core/RCTSettingsHeaders (= 0.71.3)
- React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTText (0.71.3):
- React-Core/RCTTextHeaders (= 0.71.3)
- React-RCTVibration (0.71.3):
- RCT-Folly (= 2021.07.22.00)
- React-Codegen (= 0.70.5)
- React-Core/RCTVibrationHeaders (= 0.70.5)
- React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (= 0.70.5)
- React-runtimeexecutor (0.70.5):
- React-jsi (= 0.70.5)
- ReactCommon/turbomodule/core (0.70.5):
- React-Codegen (= 0.71.3)
- React-Core/RCTVibrationHeaders (= 0.71.3)
- React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.3)
- React-runtimeexecutor (0.71.3):
- React-jsi (= 0.71.3)
- ReactCommon/turbomodule/bridging (0.71.3):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-bridging (= 0.70.5)
- React-callinvoker (= 0.70.5)
- React-Core (= 0.70.5)
- React-cxxreact (= 0.70.5)
- React-jsi (= 0.70.5)
- React-logger (= 0.70.5)
- React-perflogger (= 0.70.5)
- React-callinvoker (= 0.71.3)
- React-Core (= 0.71.3)
- React-cxxreact (= 0.71.3)
- React-jsi (= 0.71.3)
- React-logger (= 0.71.3)
- React-perflogger (= 0.71.3)
- ReactCommon/turbomodule/core (0.71.3):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-callinvoker (= 0.71.3)
- React-Core (= 0.71.3)
- React-cxxreact (= 0.71.3)
- React-jsi (= 0.71.3)
- React-logger (= 0.71.3)
- React-perflogger (= 0.71.3)
- RNCAsyncStorage (1.17.11):
- React-Core
- RNCMaskedView (0.2.8):
- React-Core
- RNFlashList (1.4.1):
- RNFlashList (1.4.0):
- React-Core
- RNFS (2.20.0):
- React-Core
@ -375,16 +421,16 @@ PODS:
- React-RCTText
- ReactCommon/turbomodule/core
- Yoga
- RNScreens (3.19.0):
- RNScreens (3.20.0):
- React-Core
- React-RCTImage
- RNSVG (13.8.0):
- RNSVG (13.4.0):
- React-Core
- Yoga (1.14.0)
DEPENDENCIES:
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- boost (from `../../../node_modules/react-native/third-party-podspecs/boost.podspec`)
- DoubleConversion (from `../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- EXApplication (from `../../../node_modules/expo-application/ios`)
- EXConstants (from `../../../node_modules/expo-constants/ios`)
- EXFileSystem (from `../../../node_modules/expo-file-system/ios`)
@ -394,51 +440,51 @@ DEPENDENCIES:
- ExpoKeepAwake (from `../../../node_modules/expo-keep-awake/ios`)
- ExpoModulesCore (from `../../../node_modules/expo-modules-core`)
- EXSplashScreen (from `../../../node_modules/expo-splash-screen/ios`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (from `../node_modules/react-native/sdks/hermes/hermes-engine.podspec`)
- FBLazyVector (from `../../../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../../../node_modules/react-native/React/FBReactNativeSpec`)
- glog (from `../../../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (from `../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- libevent (~> 2.1.12)
- lottie-react-native (from `../node_modules/lottie-react-native`)
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../node_modules/react-native/`)
- React-bridging (from `../node_modules/react-native/ReactCommon`)
- React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
- lottie-react-native (from `../../../node_modules/lottie-react-native`)
- RCT-Folly (from `../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../../../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../../../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../../../node_modules/react-native/`)
- React-callinvoker (from `../../../node_modules/react-native/ReactCommon/callinvoker`)
- React-Codegen (from `build/generated/ios`)
- React-Core (from `../node_modules/react-native/`)
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
- React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
- React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
- React-hermes (from `../node_modules/react-native/ReactCommon/hermes`)
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
- React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
- React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
- React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)"
- "RNFlashList (from `../node_modules/@shopify/flash-list`)"
- RNFS (from `../node_modules/react-native-fs`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNSVG (from `../node_modules/react-native-svg`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
- React-Core (from `../../../node_modules/react-native/`)
- React-Core/RCTWebSocket (from `../../../node_modules/react-native/`)
- React-CoreModules (from `../../../node_modules/react-native/React/CoreModules`)
- React-cxxreact (from `../../../node_modules/react-native/ReactCommon/cxxreact`)
- React-hermes (from `../../../node_modules/react-native/ReactCommon/hermes`)
- React-jsi (from `../../../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../../../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../../../node_modules/react-native/ReactCommon/jsinspector`)
- React-logger (from `../../../node_modules/react-native/ReactCommon/logger`)
- react-native-document-picker (from `../../../node_modules/react-native-document-picker`)
- react-native-safe-area-context (from `../../../node_modules/react-native-safe-area-context`)
- React-perflogger (from `../../../node_modules/react-native/ReactCommon/reactperflogger`)
- React-RCTActionSheet (from `../../../node_modules/react-native/Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../../../node_modules/react-native/Libraries/NativeAnimation`)
- React-RCTAppDelegate (from `../../../node_modules/react-native/Libraries/AppDelegate`)
- React-RCTBlob (from `../../../node_modules/react-native/Libraries/Blob`)
- React-RCTImage (from `../../../node_modules/react-native/Libraries/Image`)
- React-RCTLinking (from `../../../node_modules/react-native/Libraries/LinkingIOS`)
- React-RCTNetwork (from `../../../node_modules/react-native/Libraries/Network`)
- React-RCTSettings (from `../../../node_modules/react-native/Libraries/Settings`)
- React-RCTText (from `../../../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../../../node_modules/react-native/Libraries/Vibration`)
- React-runtimeexecutor (from `../../../node_modules/react-native/ReactCommon/runtimeexecutor`)
- ReactCommon/turbomodule/core (from `../../../node_modules/react-native/ReactCommon`)
- "RNCAsyncStorage (from `../../../node_modules/@react-native-async-storage/async-storage`)"
- "RNCMaskedView (from `../../../node_modules/@react-native-masked-view/masked-view`)"
- "RNFlashList (from `../../../node_modules/@shopify/flash-list`)"
- RNFS (from `../../../node_modules/react-native-fs`)
- RNGestureHandler (from `../../../node_modules/react-native-gesture-handler`)
- RNReanimated (from `../../../node_modules/react-native-reanimated`)
- RNScreens (from `../../../node_modules/react-native-screens`)
- RNSVG (from `../../../node_modules/react-native-svg`)
- Yoga (from `../../../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
trunk:
@ -448,9 +494,9 @@ SPEC REPOS:
EXTERNAL SOURCES:
boost:
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
:podspec: "../../../node_modules/react-native/third-party-podspecs/boost.podspec"
DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
:podspec: "../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
EXApplication:
:path: "../../../node_modules/expo-application/ios"
EXConstants:
@ -470,151 +516,151 @@ EXTERNAL SOURCES:
EXSplashScreen:
:path: "../../../node_modules/expo-splash-screen/ios"
FBLazyVector:
:path: "../node_modules/react-native/Libraries/FBLazyVector"
:path: "../../../node_modules/react-native/Libraries/FBLazyVector"
FBReactNativeSpec:
:path: "../node_modules/react-native/React/FBReactNativeSpec"
:path: "../../../node_modules/react-native/React/FBReactNativeSpec"
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
:podspec: "../../../node_modules/react-native/third-party-podspecs/glog.podspec"
hermes-engine:
:podspec: "../node_modules/react-native/sdks/hermes/hermes-engine.podspec"
:podspec: "../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
lottie-react-native:
:path: "../node_modules/lottie-react-native"
:path: "../../../node_modules/lottie-react-native"
RCT-Folly:
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
:podspec: "../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
RCTRequired:
:path: "../node_modules/react-native/Libraries/RCTRequired"
:path: "../../../node_modules/react-native/Libraries/RCTRequired"
RCTTypeSafety:
:path: "../node_modules/react-native/Libraries/TypeSafety"
:path: "../../../node_modules/react-native/Libraries/TypeSafety"
React:
:path: "../node_modules/react-native/"
React-bridging:
:path: "../node_modules/react-native/ReactCommon"
:path: "../../../node_modules/react-native/"
React-callinvoker:
:path: "../node_modules/react-native/ReactCommon/callinvoker"
:path: "../../../node_modules/react-native/ReactCommon/callinvoker"
React-Codegen:
:path: build/generated/ios
React-Core:
:path: "../node_modules/react-native/"
:path: "../../../node_modules/react-native/"
React-CoreModules:
:path: "../node_modules/react-native/React/CoreModules"
:path: "../../../node_modules/react-native/React/CoreModules"
React-cxxreact:
:path: "../node_modules/react-native/ReactCommon/cxxreact"
:path: "../../../node_modules/react-native/ReactCommon/cxxreact"
React-hermes:
:path: "../node_modules/react-native/ReactCommon/hermes"
:path: "../../../node_modules/react-native/ReactCommon/hermes"
React-jsi:
:path: "../node_modules/react-native/ReactCommon/jsi"
:path: "../../../node_modules/react-native/ReactCommon/jsi"
React-jsiexecutor:
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
:path: "../../../node_modules/react-native/ReactCommon/jsiexecutor"
React-jsinspector:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
:path: "../../../node_modules/react-native/ReactCommon/jsinspector"
React-logger:
:path: "../node_modules/react-native/ReactCommon/logger"
:path: "../../../node_modules/react-native/ReactCommon/logger"
react-native-document-picker:
:path: "../node_modules/react-native-document-picker"
:path: "../../../node_modules/react-native-document-picker"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
:path: "../../../node_modules/react-native-safe-area-context"
React-perflogger:
:path: "../node_modules/react-native/ReactCommon/reactperflogger"
:path: "../../../node_modules/react-native/ReactCommon/reactperflogger"
React-RCTActionSheet:
:path: "../node_modules/react-native/Libraries/ActionSheetIOS"
:path: "../../../node_modules/react-native/Libraries/ActionSheetIOS"
React-RCTAnimation:
:path: "../node_modules/react-native/Libraries/NativeAnimation"
:path: "../../../node_modules/react-native/Libraries/NativeAnimation"
React-RCTAppDelegate:
:path: "../../../node_modules/react-native/Libraries/AppDelegate"
React-RCTBlob:
:path: "../node_modules/react-native/Libraries/Blob"
:path: "../../../node_modules/react-native/Libraries/Blob"
React-RCTImage:
:path: "../node_modules/react-native/Libraries/Image"
:path: "../../../node_modules/react-native/Libraries/Image"
React-RCTLinking:
:path: "../node_modules/react-native/Libraries/LinkingIOS"
:path: "../../../node_modules/react-native/Libraries/LinkingIOS"
React-RCTNetwork:
:path: "../node_modules/react-native/Libraries/Network"
:path: "../../../node_modules/react-native/Libraries/Network"
React-RCTSettings:
:path: "../node_modules/react-native/Libraries/Settings"
:path: "../../../node_modules/react-native/Libraries/Settings"
React-RCTText:
:path: "../node_modules/react-native/Libraries/Text"
:path: "../../../node_modules/react-native/Libraries/Text"
React-RCTVibration:
:path: "../node_modules/react-native/Libraries/Vibration"
:path: "../../../node_modules/react-native/Libraries/Vibration"
React-runtimeexecutor:
:path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
:path: "../../../node_modules/react-native/ReactCommon/runtimeexecutor"
ReactCommon:
:path: "../node_modules/react-native/ReactCommon"
:path: "../../../node_modules/react-native/ReactCommon"
RNCAsyncStorage:
:path: "../node_modules/@react-native-async-storage/async-storage"
:path: "../../../node_modules/@react-native-async-storage/async-storage"
RNCMaskedView:
:path: "../node_modules/@react-native-masked-view/masked-view"
:path: "../../../node_modules/@react-native-masked-view/masked-view"
RNFlashList:
:path: "../node_modules/@shopify/flash-list"
:path: "../../../node_modules/@shopify/flash-list"
RNFS:
:path: "../node_modules/react-native-fs"
:path: "../../../node_modules/react-native-fs"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
:path: "../../../node_modules/react-native-gesture-handler"
RNReanimated:
:path: "../node_modules/react-native-reanimated"
:path: "../../../node_modules/react-native-reanimated"
RNScreens:
:path: "../node_modules/react-native-screens"
:path: "../../../node_modules/react-native-screens"
RNSVG:
:path: "../node_modules/react-native-svg"
:path: "../../../node_modules/react-native-svg"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
:path: "../../../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
boost: a7c83b31436843459a1961bfd74b96033dc77234
boost: 57d2868c099736d80fcd648bf211b4431e51a558
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
EXApplication: 034b1c40a8e9fe1bff76a1e511ee90dff64ad834
EXConstants: 3c86653c422dd77e40d10cbbabb3025003977415
EXFileSystem: 60602b6eefa6873f97172c684b7537c9760b50d6
EXFont: 319606bfe48c33b5b5063fb0994afdc496befe80
EXMediaLibrary: b1c4f78878e45f6a359aff3a059e1660c41b73ab
Expo: b9fa98bf260992312ee3c424400819fb9beadafe
ExpoKeepAwake: 69b59d0a8d2b24de9f82759c39b3821fec030318
ExpoModulesCore: 65ae09e2b2d3dd8ece30a5acc83c569968125ab0
EXSplashScreen: 3e989924f61a8dd07ee4ea584c6ba14be9b51949
FBLazyVector: affa4ba1bfdaac110a789192f4d452b053a86624
FBReactNativeSpec: fe8b5f1429cfe83a8d72dc8ed61dc7704cac8745
EXApplication: d8f53a7eee90a870a75656280e8d4b85726ea903
EXConstants: f348da07e21b23d2b085e270d7b74f282df1a7d9
EXFileSystem: 844e86ca9b5375486ecc4ef06d3838d5597d895d
EXFont: 6ea3800df746be7233208d80fe379b8ed74f4272
EXMediaLibrary: 792fe9b828b5bfa2c5a8b629730f175af2938285
Expo: 04ba1ddde0be07aff4306ae636a1804810679145
ExpoKeepAwake: 69f5f627670d62318410392d03e0b5db0f85759a
ExpoModulesCore: 1667335d4f4c9b7801990930e6f0eea42c916a21
EXSplashScreen: cd7fb052dff5ba8311d5c2455ecbebffe1b7a8ca
FBLazyVector: 60195509584153283780abdac5569feffb8f08cc
FBReactNativeSpec: c5a5c4f1b95ae42a17cd22c8c89c482a7b327fe3
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
hermes-engine: 7fe5fc6ef707b7fdcb161b63898ec500e285653d
hermes-engine: 38bfe887e456b33b697187570a08de33969f5db7
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
lottie-ios: 8f97d3271e155c2d688875c29cd3c74908aef5f8
lottie-react-native: b702fab740cdb952a8e2354713d3beda63ff97b0
RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda
RCTRequired: 21229f84411088e5d8538f21212de49e46cc83e2
RCTTypeSafety: 62eed57a32924b09edaaf170a548d1fc96223086
React: f0254ccddeeef1defe66c6b1bb9133a4f040792b
React-bridging: e46911666b7ec19538a620a221d6396cd293d687
React-callinvoker: 66b62e2c34546546b2f21ab0b7670346410a2b53
React-Codegen: b6999435966df3bdf82afa3f319ba0d6f9a8532a
React-Core: dabbc9d1fe0a11d884e6ee1599789cf8eb1058a5
React-CoreModules: 5b6b7668f156f73a56420df9ec68ca2ec8f2e818
React-cxxreact: c7ca2baee46db22a30fce9e639277add3c3f6ad1
React-hermes: c93e1d759ad5560dfea54d233013d7d2c725c286
React-jsi: a565dcb49130ed20877a9bb1105ffeecbb93d02d
React-jsiexecutor: 31564fa6912459921568e8b0e49024285a4d584b
React-jsinspector: badd81696361249893a80477983e697aab3c1a34
React-logger: fdda34dd285bdb0232e059b19d9606fa0ec3bb9c
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
RCTRequired: bec48f07daf7bcdc2655a0cde84e07d24d2a9e2a
RCTTypeSafety: 171394eebacf71e1cfad79dbfae7ee8fc16ca80a
React: d7433ccb6a8c36e4cbed59a73c0700fc83c3e98a
React-callinvoker: 15f165009bd22ae829b2b600e50bcc98076ce4b8
React-Codegen: b5910000eaf1e0c2f47d29be6f82f5f1264420d7
React-Core: b6f2f78d580a90b83fd7b0d1c6911c799f6eac82
React-CoreModules: e0cbc1a4f4f3f60e23c476fef7ab37be363ea8c1
React-cxxreact: c87f3f124b2117d00d410b35f16c2257e25e50fa
React-hermes: c64ca6bdf16a7069773103c9bedaf30ec90ab38f
React-jsi: 39729361645568e238081b3b3180fbad803f25a4
React-jsiexecutor: 515b703d23ffadeac7687bc2d12fb08b90f0aaa1
React-jsinspector: 9f7c9137605e72ca0343db4cea88006cb94856dd
React-logger: 957e5dc96d9dbffc6e0f15e0ee4d2b42829ff207
react-native-document-picker: 958e2bc82e128be69055be261aeac8d872c8d34c
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
React-perflogger: e68d3795cf5d247a0379735cbac7309adf2fb931
React-RCTActionSheet: 05452c3b281edb27850253db13ecd4c5a65bc247
React-RCTAnimation: 578eebac706428e68466118e84aeacf3a282b4da
React-RCTBlob: f47a0aa61e7d1fb1a0e13da832b0da934939d71a
React-RCTImage: 60f54b66eed65d86b6dffaf4733d09161d44929d
React-RCTLinking: 91073205aeec4b29450ca79b709277319368ac9e
React-RCTNetwork: ca91f2c9465a7e335c8a5fae731fd7f10572213b
React-RCTSettings: 1a9a5d01337d55c18168c1abe0f4a589167d134a
React-RCTText: c591e8bd9347a294d8416357ca12d779afec01d5
React-RCTVibration: 8e5c8c5d17af641f306d7380d8d0fe9b3c142c48
React-runtimeexecutor: 7401c4a40f8728fd89df4a56104541b760876117
ReactCommon: c9246996e73bf75a2c6c3ff15f1e16707cdc2da9
React-perflogger: af8a3d31546077f42d729b949925cc4549f14def
React-RCTActionSheet: 57cc5adfefbaaf0aae2cf7e10bccd746f2903673
React-RCTAnimation: 11c61e94da700c4dc915cf134513764d87fc5e2b
React-RCTAppDelegate: c3980adeaadcfd6cb495532e928b36ac6db3c14a
React-RCTBlob: ccc5049d742b41971141415ca86b83b201495695
React-RCTImage: 7a9226b0944f1e76e8e01e35a9245c2477cdbabb
React-RCTLinking: bbe8cc582046a9c04f79c235b73c93700263e8b4
React-RCTNetwork: fc2ca322159dc54e06508d4f5c3e934da63dc013
React-RCTSettings: f1e9db2cdf946426d3f2b210e4ff4ce0f0d842ef
React-RCTText: 1c41dd57e5d742b1396b4eeb251851ce7ff0fca1
React-RCTVibration: 5199a180d04873366a83855de55ac33ce60fe4d5
React-runtimeexecutor: 7bf0dafc7b727d93c8cb94eb00a9d3753c446c3e
ReactCommon: 6f65ea5b7d84deb9e386f670dd11ce499ded7b40
RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60
RNCMaskedView: bc0170f389056201c82a55e242e5d90070e18e5a
RNFlashList: 8ec7f7454721145fe84566bb9e88bcf58981c9fe
RNFlashList: 399bf6a0db68f594ad2c86aaff3ea39564f39f8a
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
RNReanimated: 6668b0587bebd4b15dd849b99e5a9c70fc12ed95
RNScreens: ea4cd3a853063cda19a4e3c28d2e52180c80f4eb
RNSVG: c1e76b81c76cdcd34b4e1188852892dc280eb902
Yoga: eca980a5771bf114c41a754098cd85e6e0d90ed7
RNReanimated: addc4900bf47882118d0a1b21747fa6705fa8cff
RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
RNSVG: 07dbd870b0dcdecc99b3a202fa37c8ca163caec2
Yoga: 5ed1699acbba8863755998a4245daa200ff3817b
PODFILE CHECKSUM: 4065c8b949a453403939de6e852185dbd374d972
PODFILE CHECKSUM: 17065850599b1e955efad7a619cf825c012744d7
COCOAPODS: 1.11.3

View file

@ -250,7 +250,7 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
@ -589,7 +589,7 @@
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos;
};
name = Debug;
@ -642,7 +642,7 @@
);
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = NO;
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};

View file

@ -1,9 +1,7 @@
#import <Foundation/Foundation.h>
#import <React/RCTBridgeDelegate.h>
#import <RCTAppDelegate.h>
#import <UIKit/UIKit.h>
#import <Expo/Expo.h>
@interface AppDelegate : EXAppDelegateWrapper <RCTBridgeDelegate>
@interface AppDelegate : EXAppDelegateWrapper
@end
@end

View file

@ -1,89 +1,19 @@
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTLinkingManager.h>
#import <React/RCTConvert.h>
#import <React/RCTAppSetupUtils.h>
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
RCTTurboModuleManager *_turboModuleManager;
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTAppSetupPrepareApp(application);
self.moduleName = @"main";
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:initProps];
rootView.backgroundColor = [UIColor whiteColor];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
[super application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
// If you'd like to export some custom RCTBridgeModules, add them here!
return @[];
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
// Switch this bool to turn on and off the concurrent root
return true;
}
- (NSDictionary *)prepareInitialProps
{
NSMutableDictionary *initProps = [NSMutableDictionary new];
#if RCT_NEW_ARCH_ENABLED
initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
#endif
return initProps;
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
@ -95,6 +25,16 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
#endif
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
return true;
}
// Linking API
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
@ -124,43 +64,4 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
}
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
@end
@end

View file

@ -9,7 +9,7 @@
<key>EXUpdatesLaunchWaitMs</key>
<integer>0</integer>
<key>EXUpdatesSDKVersion</key>
<string>46.0.0</string>
<string>48.0.0</string>
<key>EXUpdatesURL</key>
<string>https://exp.host/@utkudev/spacedrive</string>
</dict>

View file

@ -6,5 +6,4 @@ int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
}

View file

@ -2,6 +2,8 @@
set -e
export PROTOC=/opt/homebrew/bin/protoc
TARGET_DIRECTORY=../../../target
CARGO_FLAGS=

View file

@ -12,6 +12,7 @@ const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, '../..');
const metroConfig = makeMetroConfig({
...expoDefaultConfig,
projectRoot,
watchFolders: [workspaceRoot],
resolver: {
@ -29,6 +30,7 @@ const metroConfig = makeMetroConfig({
]
},
transformer: {
...expoDefaultConfig.transformer,
// Metro default is "uglify-es" but terser should be faster and has better defaults.
minifierPath: 'metro-minify-terser',
minifierConfig: {

View file

@ -11,62 +11,62 @@
"xcode": "open ios/spacedrive.xcworkspace",
"android-studio": "open -a '/Applications/Android Studio.app' ./android",
"lint": "eslint src",
"postinstall": "node scripts/postinstall.js",
"typecheck": "tsc -b",
"eas-build-pre-install": "npm i -g pnpm@7.18.2"
},
"dependencies": {
"@gorhom/bottom-sheet": "^4.4.5",
"@hookform/resolvers": "^2.9.11",
"@react-native-async-storage/async-storage": "~1.17.11",
"@react-native-masked-view/masked-view": "0.2.8",
"@react-navigation/bottom-tabs": "^6.5.4",
"@react-navigation/drawer": "^6.5.8",
"@react-navigation/native": "^6.1.3",
"@react-navigation/stack": "^6.3.12",
"@react-navigation/bottom-tabs": "^6.5.7",
"@react-navigation/drawer": "^6.6.2",
"@react-navigation/native": "^6.1.6",
"@react-navigation/stack": "^6.3.16",
"@rspc/client": "^0.0.0-main-7c0a67c1",
"@rspc/react": "^0.0.0-main-7c0a67c1",
"@sd/assets": "workspace:*",
"@sd/client": "workspace:*",
"@shopify/flash-list": "1.4.1",
"@tanstack/react-query": "^4.24.4",
"@shopify/flash-list": "1.4.0",
"@tanstack/react-query": "^4.26.1",
"byte-size": "^8.1.0",
"class-variance-authority": "^0.4.0",
"dayjs": "^1.11.5",
"expo": "^47.0.13",
"expo-linking": "~3.3.0",
"expo-media-library": "~15.0.0",
"expo-splash-screen": "~0.17.5",
"expo-status-bar": "~1.4.2",
"expo": "^48.0.6",
"expo-linking": "~4.0.1",
"expo-media-library": "~15.2.2",
"expo-splash-screen": "~0.18.1",
"expo-status-bar": "~1.4.4",
"intl": "^1.2.5",
"lottie-react-native": "5.1.4",
"moti": "^0.22.0",
"moti": "^0.24.2",
"phosphor-react-native": "^1.1.2",
"react": "18.1.0",
"react-hook-form": "^7.43.0",
"react-native": "0.70.5",
"react": "18.2.0",
"react-hook-form": "^7.43.5",
"react-native": "0.71.3",
"react-native-document-picker": "^8.1.1",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "~2.9.0",
"react-native-popup-menu": "^0.16.1",
"react-native-reanimated": "~2.14.4",
"react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.19.0",
"react-native-svg": "13.8.0",
"react-native-screens": "~3.20.0",
"react-native-svg": "13.4.0",
"react-native-wheel-color-picker": "^1.2.0",
"twrnc": "^3.5.0",
"twrnc": "^3.6.0",
"use-count-up": "^3.0.1",
"use-debounce": "^9.0.2",
"valtio": "^1.8.0"
"valtio": "^1.10.3",
"zod": "^3.21.4"
},
"devDependencies": {
"@rnx-kit/metro-config": "^1.3.5",
"@sd/config": "workspace:*",
"@types/react": "~18.0.26",
"@types/react-native": "~0.70.8",
"@types/react": "~18.0.27",
"babel-plugin-module-resolver": "^5.0.0",
"eslint-plugin-react-native": "^4.0.0",
"metro-minify-terser": "^0.74.1",
"metro-minify-terser": "0.76.0",
"react-native-svg-transformer": "^1.0.0",
"typescript": "^4.9.4"
"typescript": "^4.9.5"
}
}

View file

@ -2,6 +2,7 @@
let fs = require('fs-extra');
let path = require('path');
// Not used atm, keeping it here in case we need it in the future
async function copyReactNativeCodegen() {
const paths = [
['../../../node_modules/react-native-codegen', '../node_modules/react-native-codegen'],

View file

@ -1,32 +1,49 @@
import { MotiView, useDynamicAnimation } from 'moti';
import { PropsWithChildren, ReactNode } from 'react';
import { StyleSheet, View } from 'react-native';
import { StyleSheet, View, ViewProps } from 'react-native';
import { useDerivedValue, useSharedValue } from 'react-native-reanimated';
import Layout from '~/constants/Layout';
import { tw } from '~/lib/tailwind';
// Anything wrapped with FadeIn will fade in on mount.
export const FadeInAnimation = ({ children, delay }: PropsWithChildren<{ delay?: number }>) => (
<MotiView from={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ type: 'timing', delay }}>
{children}
</MotiView>
);
type MotiViewProps = PropsWithChildren<ViewProps>;
export const FadeInUpAnimation = ({ children, delay }: PropsWithChildren<{ delay?: number }>) => (
// Anything wrapped with FadeIn will fade in on mount.
export const FadeInAnimation = ({
children,
delay,
...props
}: MotiViewProps & { delay?: number }) => (
<MotiView
from={{ opacity: 0, translateY: 20 }}
animate={{ opacity: 1, translateY: 0 }}
from={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ type: 'timing', delay }}
{...props}
>
{children}
</MotiView>
);
export const LogoAnimation = ({ children }: PropsWithChildren) => (
export const FadeInUpAnimation = ({
children,
delay,
...props
}: MotiViewProps & { delay?: number }) => (
<MotiView
from={{ opacity: 0, translateY: 20 }}
animate={{ opacity: 1, translateY: 0 }}
transition={{ type: 'timing', delay }}
{...props}
>
{children}
</MotiView>
);
export const LogoAnimation = ({ children, ...props }: MotiViewProps) => (
<MotiView
transition={{ type: 'timing', delay: 200 }}
from={{ opacity: 0.8, translateY: Layout.window.width / 2 }}
animate={{ opacity: 1, translateY: 0 }}
transition={{ type: 'timing', delay: 200 }}
{...props}
>
{children}
</MotiView>

View file

@ -1,19 +1,14 @@
import AnimatedLottieView from 'lottie-react-native';
import { StyleProp, View, ViewStyle } from 'react-native';
import AnimatedLottieView, { AnimatedLottieViewProps } from 'lottie-react-native';
type Props = {
style?: StyleProp<ViewStyle>;
};
type AnimationProps = Omit<AnimatedLottieViewProps, 'source'>;
export const PulseAnimation = ({ style }: Props) => {
export const PulseAnimation = ({ style }: AnimationProps) => {
return (
<View>
<AnimatedLottieView
autoPlay
loop
source={require('@sd/assets/lottie/loading-pulse.json')}
style={style}
/>
</View>
<AnimatedLottieView
autoPlay
loop
source={require('@sd/assets/lottie/loading-pulse.json')}
style={style}
/>
);
};

View file

@ -9,7 +9,7 @@ import { tw, twStyle } from '~/lib/tailwind';
import { currentLibraryStore } from '~/utils/nav';
import { AnimatedHeight } from '../animation/layout';
import CreateLibraryDialog from '../dialog/CreateLibraryDialog';
import Divider from '../primitive/Divider';
import { Divider } from '../primitive/Divider';
const DrawerLibraryManager = () => {
const [dropdownClosed, setDropdownClosed] = useState(true);
@ -51,7 +51,7 @@ const DrawerLibraryManager = () => {
<View style={tw`bg-sidebar-button border-sidebar-line rounded-b-md p-2`}>
{/* Libraries */}
{libraries.data?.map((library) => {
console.log('library', library);
// console.log('library', library);
return (
<Pressable key={library.uuid} onPress={() => (currentLibraryStore.id = library.uuid)}>
<View

View file

@ -1,5 +1,5 @@
import { Text, View } from 'react-native';
import { ExplorerItem, ObjectKind, isObject } from '@sd/client';
import { ExplorerItem } from '@sd/client';
import Layout from '~/constants/Layout';
import { tw, twStyle } from '~/lib/tailwind';
import { getExplorerStore } from '~/stores/explorerStore';
@ -12,10 +12,6 @@ type FileItemProps = {
const FileItem = ({ data }: FileItemProps) => {
const { item } = data;
// temp fix (will handle this on mobile-inspector branch)
const objectData = data ? (isObject(data) ? data.item : data.item.object) : null;
const isVid = ObjectKind[objectData?.kind || 0] === 'Video';
const gridItemSize = Layout.window.width / getExplorerStore().gridNumColumns;
return (
@ -25,15 +21,7 @@ const FileItem = ({ data }: FileItemProps) => {
height: gridItemSize
})}
>
<FileThumb
data={data}
kind={data.item.extension === 'zip' ? 'zip' : isVid ? 'video' : 'other'}
/>
{item.extension && isVid && (
<View style={tw`absolute bottom-8 right-5 rounded bg-black/70 py-0.5 px-1 opacity-70`}>
<Text style={tw`text-[9px] font-semibold uppercase text-white`}>{item.extension}</Text>
</View>
)}
<FileThumb data={data} />
<View style={tw`mt-1 px-1.5 py-[1px]`}>
<Text numberOfLines={1} style={tw`text-center text-xs font-medium text-white`}>
{item?.name}

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Text, View } from 'react-native';
import { ExplorerItem, ObjectKind, isObject } from '@sd/client';
import { ExplorerItem } from '@sd/client';
import { tw, twStyle } from '~/lib/tailwind';
import { getExplorerStore } from '~/stores/explorerStore';
import FileThumb from './FileThumb';
@ -12,21 +12,13 @@ type FileRowProps = {
const FileRow = ({ data }: FileRowProps) => {
const { item } = data;
// temp fix (will handle this on mobile-inspector branch)
const objectData = data ? (isObject(data) ? data.item : data.item.object) : null;
const isVid = ObjectKind[objectData?.kind || 0] === 'Video';
return (
<View
style={twStyle('flex flex-row items-center px-3', {
height: getExplorerStore().listItemSize
})}
>
<FileThumb
data={data}
kind={item.extension === 'zip' ? 'zip' : isVid ? 'video' : 'other'}
size={0.6}
/>
<FileThumb data={data} size={0.6} />
<View style={tw`ml-3`}>
<Text numberOfLines={1} style={tw`text-ink-dull text-center text-xs font-medium`}>
{item?.name}

View file

@ -1,8 +1,8 @@
import * as icons from '@sd/assets/icons';
import { PropsWithChildren } from 'react';
import { Image, View } from 'react-native';
import { DocumentDirectoryPath } from 'react-native-fs';
import { ExplorerItem, isObject, isPath } from '@sd/client';
// import icons from '../../assets/icons/file';
import { ExplorerItem, ObjectKind, isObject, isPath } from '@sd/client';
import { tw } from '../../lib/tailwind';
import FolderIcon from '../icons/FolderIcon';
@ -13,112 +13,82 @@ type FileThumbProps = {
* default: `1`
*/
size?: number;
kind?: string;
};
export const getThumbnailUrlById = (casId: string) =>
`${DocumentDirectoryPath}/thumbnails/${encodeURIComponent(casId)}.webp`;
type KindType = keyof typeof icons | 'Unknown';
function getExplorerItemData(data: ExplorerItem) {
const objectData = data ? (isObject(data) ? data.item : data.item.object) : null;
return {
casId: (isObject(data) ? data.item.file_paths[0]?.cas_id : data.item.cas_id) || null,
isDir: isPath(data) && data.item.is_dir,
kind: ObjectKind[objectData?.kind || 0] as KindType,
hasThumbnail: data.has_thumbnail,
extension: data.item.extension
};
}
const FileThumbWrapper = ({ children, size = 1 }: PropsWithChildren<{ size: number }>) => (
<View style={[tw`items-center justify-center`, { width: 80 * size, height: 80 * size }]}>
{children}
</View>
);
export default function FileThumb({ data, size = 1, kind }: FileThumbProps) {
// const Icon = useMemo(() => {
// const Icon = icons[data.extension];
// return Icon;
// }, [data.extension]);
export default function FileThumb({ data, size = 1 }: FileThumbProps) {
const { casId, isDir, kind, hasThumbnail, extension } = getExplorerItemData(data);
if (isPath(data) && data.item.is_dir)
if (isPath(data) && data.item.is_dir) {
return (
<FileThumbWrapper size={size}>
<FolderIcon size={70 * size} />
</FileThumbWrapper>
);
}
const casId = isObject(data) ? data.item.file_paths[0]?.cas_id : data.item.cas_id;
if (!casId) return null;
// Icon
let icon = undefined;
if (kind === 'Archive') icon = require('@sd/assets/images/Archive.png');
else if (kind === 'Video') icon = require('@sd/assets/images/Video.png');
else if (kind === 'Document' && data.item.extension === 'pdf')
icon = require('@sd/assets/images/Document_pdf.png');
else if (kind === 'Executable') icon = require('@sd/assets/images/Executable.png');
if (icon) {
if (hasThumbnail && casId) {
// TODO: Handle Image checkers bg?
return (
<FileThumbWrapper size={size}>
<Image source={icon} style={{ width: 70 * size, height: 70 * size }} />
<Image
source={{ uri: getThumbnailUrlById(casId) }}
resizeMode="contain"
style={tw`h-full w-full`}
/>
</FileThumbWrapper>
);
}
const url = getThumbnailUrlById(casId);
// Default icon
let icon = icons['Document'];
if (data.has_thumbnail && url) {
return (
<FileThumbWrapper size={size}>
<Image source={{ uri: url }} resizeMode="contain" style={tw`h-full w-full`} />
</FileThumbWrapper>
);
if (isDir) {
icon = icons['Folder'];
} else if (
kind &&
extension &&
icons[`${kind}_${extension.toLowerCase()}` as keyof typeof icons]
) {
// e.g. Document_pdf
icon = icons[`${kind}_${extension.toLowerCase()}` as keyof typeof icons];
} else if (kind !== 'Unknown' && kind && icons[kind]) {
icon = icons[kind];
}
// TODO: Handle video thumbnails
// // 10 percent of the size
// const videoBarsHeight = Math.floor(size / 10);
// // calculate 16:9 ratio for height from size
// const videoHeight = Math.floor((size * 9) / 16) + videoBarsHeight * 2;
return (
<FileThumbWrapper size={size}>
<Image
source={require('@sd/assets/images/File.png')}
style={{ width: 70 * size, height: 70 * size }}
/>
<Image source={icon} style={{ width: 70 * size, height: 70 * size }} />
</FileThumbWrapper>
);
// Default file icon
// return (
// <View style={[tw`justify-center`, { width: 60 * size, height: 60 * size }]}>
// <View style={[tw`m-auto relative`, { width: 45 * size, height: 60 * size }]}>
// <Svg
// // Background
// style={tw`absolute top-0 left-0`}
// fill={tw.color('app-box')}
// width={45 * size}
// height={60 * size}
// viewBox="0 0 65 81"
// >
// <Path d="M0 8a8 8 0 0 1 8-8h31.686a8 8 0 0 1 5.657 2.343L53.5 10.5l9.157 9.157A8 8 0 0 1 65 25.314V73a8 8 0 0 1-8 8H8a8 8 0 0 1-8-8V8Z" />
// </Svg>
// <Svg
// // Peel
// style={tw`absolute top-[2px] -right-[0.6px]`}
// fill={tw.color('app-highlight')}
// width={15 * size}
// height={15 * size}
// viewBox="0 0 41 41"
// >
// <Path d="M41.412 40.558H11.234C5.03 40.558 0 35.528 0 29.324V0l41.412 40.558Z" />
// </Svg>
// {/* File Icon & Extension */}
// <View style={tw`absolute w-full h-full items-center justify-center`}>
// {Icon && (
// <Suspense fallback={<></>}>
// <Icon width={18 * size} height={18 * size} style={tw`mt-2`} />
// </Suspense>
// )}
// <Text
// style={[
// tw`mt-1 font-bold text-center uppercase text-gray-450`,
// {
// fontSize: 10 * (size * 0.8)
// }
// ]}
// >
// {data.extension}
// </Text>
// </View>
// </View>
// </View>
// );
}

View file

@ -1,15 +1,17 @@
import { VariantProps, cva } from 'class-variance-authority';
import { FC } from 'react';
import { TextInputProps as RNTextInputProps, TextInput } from 'react-native';
import { Eye, EyeSlash } from 'phosphor-react-native';
import { useState } from 'react';
import { Pressable, TextInputProps as RNTextInputProps, TextInput, View } from 'react-native';
import { tw, twStyle } from '~/lib/tailwind';
const input = cva(['rounded-md border text-sm leading-tight shadow-sm'], {
variants: {
variant: {
default: 'border-app-line bg-app text-ink'
default: 'border-app-line bg-app-input text-ink'
},
size: {
default: ['py-2', 'px-3']
default: ['py-2', 'px-3'],
md: ['py-2.5', 'px-3.5']
}
},
defaultVariants: {
@ -20,13 +22,51 @@ const input = cva(['rounded-md border text-sm leading-tight shadow-sm'], {
type InputProps = VariantProps<typeof input> & RNTextInputProps;
export const Input: FC<InputProps> = ({ variant, ...props }) => {
export const Input = ({ variant, size, ...props }: InputProps) => {
const { style, ...otherProps } = props;
return (
<TextInput
placeholderTextColor={tw.color('ink-dull')}
style={twStyle(input({ variant }), style as string)}
placeholderTextColor={tw.color('ink-faint')}
style={twStyle(input({ variant, size }), style as string)}
{...otherProps}
/>
);
};
// Same as above but configured with password props & show/hide password button
type PasswordInputProps = InputProps & {
isNewPassword?: boolean;
};
export const PasswordInput = ({ variant, size, ...props }: PasswordInputProps) => {
const { style, isNewPassword = false, ...otherProps } = props;
const [showPassword, setShowPassword] = useState(false);
const Icon = showPassword ? EyeSlash : Eye;
return (
<View style={tw`relative`}>
<TextInput
autoComplete={isNewPassword ? 'password-new' : 'password'}
textContentType={isNewPassword ? 'newPassword' : 'password'}
placeholder="Password"
secureTextEntry={!showPassword}
autoCorrect={false}
autoCapitalize="none"
placeholderTextColor={tw.color('ink-dull')}
// Do not use margin here, it will break the absolute positioning of the button.
// Maybe switch to flexbox?
style={twStyle(input({ variant, size }), style as string)}
{...otherProps}
/>
<Pressable
style={tw`absolute inset-y-[10px] right-4`}
onPress={() => setShowPassword((v) => !v)}
>
<Icon size={18} color="white" />
</Pressable>
</View>
);
};

View file

@ -1,3 +1,4 @@
import { Folder } from '@sd/assets/icons';
import FolderWhite from '@sd/assets/svgs/folder-white.svg';
import { Image } from 'react-native';
@ -17,7 +18,7 @@ const FolderIcon: React.FC<FolderProps> = ({ size = 24, isWhite }) => {
return isWhite ? (
<FolderWhite width={size} height={size} />
) : (
<Image source={require('@sd/assets/images/Folder.png')} style={{ width: size, height: size }} />
<Image source={Folder} style={{ width: size, height: size }} />
);
};

View file

@ -0,0 +1,51 @@
import { Text, View, ViewStyle } from 'react-native';
import { getPasswordStrength } from '@sd/client';
import { tw, twStyle } from '~/lib/tailwind';
// NOTE: Lazy load this component.
type PasswordMeterProps = {
password: string;
containerStyle?: ViewStyle;
};
const PasswordMeter = (props: PasswordMeterProps) => {
const { score, scoreText } = getPasswordStrength(props.password);
return (
<View style={props.containerStyle}>
<View style={tw`flex flex-row items-center justify-between`}>
<Text style={tw`text-sm text-white`}>Password strength</Text>
<Text
style={twStyle(
'text-sm font-semibold',
score === 0 && 'text-red-500',
score === 1 && 'text-red-500',
score === 2 && 'text-amber-400',
score === 3 && 'text-lime-500',
score === 4 && 'text-accent'
)}
>
{scoreText}
</Text>
</View>
<View style={tw`bg-app-box/80 mt-2 w-full rounded-full`}>
<View
style={twStyle(
{
width: `${score !== 0 ? score * 25 : 12.5}%`
},
'h-2 rounded-full',
score === 0 && 'bg-red-500',
score === 1 && 'bg-red-500',
score === 2 && 'bg-amber-400',
score === 3 && 'bg-lime-500',
score === 4 && 'bg-accent'
)}
/>
</View>
</View>
);
};
export default PasswordMeter;

View file

@ -11,7 +11,7 @@ const Card = ({ children, ...props }: CardProps) => {
return (
<View
style={twStyle('border-app-line bg-app-overlay rounded-lg border px-4 py-3', style as string)}
style={twStyle('border-app-line bg-app-overlay rounded-lg border px-4 py-5', style as string)}
{...otherProps}
>
{children}

View file

@ -95,8 +95,7 @@ const Dialog = (props: DialogProps) => {
{props.loading && <PulseAnimation style={tw`h-7`} />}
<View style={tw`grow`} />
<Button
variant="dark_gray"
size="md"
variant="darkGray"
disabled={props.loading} // Disables Close button if loading
onPress={handleCloseDialog}
>
@ -106,7 +105,6 @@ const Dialog = (props: DialogProps) => {
<Button
style={tw`ml-2`}
variant={props.ctaDanger ? 'danger' : 'accent'}
size="md"
onPress={props.ctaAction}
disabled={props.ctaDisabled || props.loading}
>

View file

@ -132,13 +132,13 @@ const ImportModal = forwardRef<ModalRef, unknown>((_, ref) => {
return (
<Modal ref={modalRef} snapPoints={['25%']}>
<View style={tw`flex-1 px-8 pt-8 pb-2`}>
{/* <Button size="md" variant="accent" style={tw`my-2`} onPress={testFN}>
{/* <Button variant="accent" style={tw`my-2`} onPress={testFN}>
<Text>TEST</Text>
</Button> */}
<Button size="md" variant="accent" style={tw`my-2`} onPress={handleFilesButton}>
<Button variant="accent" style={tw`my-2`} onPress={handleFilesButton}>
<Text>Import from Files</Text>
</Button>
<Button size="md" variant="accent" onPress={handlePhotosButton}>
<Button variant="accent" onPress={handlePhotosButton}>
<Text>Import from Photos</Text>
</Button>
<Text style={tw`mt-4 text-center text-white`}>TODO</Text>

View file

@ -14,7 +14,7 @@ import { ExplorerItem, formatBytes, isObject, useLibraryQuery } from '@sd/client
import FileThumb from '~/components/explorer/FileThumb';
import InfoTagPills from '~/components/explorer/sections/InfoTagPills';
import { Modal, ModalRef, ModalScrollView } from '~/components/layout/Modal';
import Divider from '~/components/primitive/Divider';
import { Divider } from '~/components/primitive/Divider';
import useForwardedRef from '~/hooks/useForwardedRef';
import { tw } from '~/lib/tailwind';

View file

@ -78,7 +78,6 @@ const CreateTagModal = forwardRef<ModalRef, unknown>((_, ref) => {
)}
<Button
variant="accent"
size="md"
onPress={() => createTag({ color: tagColor, name: tagName })}
style={tw`mt-6`}
disabled={tagName.length === 0}

View file

@ -77,7 +77,6 @@ const UpdateTagModal = forwardRef<ModalRef, Props>((props, ref) => {
{/* TODO: Add loading to button */}
<Button
variant="accent"
size="md"
onPress={() => updateTag({ id: props.tag.id, color: tagColor, name: tagName })}
style={tw`mt-6`}
disabled={tagName.length === 0}

View file

@ -1,21 +1,21 @@
import { VariantProps, cva } from 'class-variance-authority';
import { MotiPressable, MotiPressableProps } from 'moti/interactions';
import { FC, useMemo } from 'react';
import { Pressable, PressableProps } from 'react-native';
import { tw, twStyle } from '~/lib/tailwind';
import { Pressable, PressableProps, View, ViewProps } from 'react-native';
import { twStyle } from '~/lib/tailwind';
const button = cva(['items-center justify-center rounded-md border shadow-sm'], {
variants: {
variant: {
danger: ['border-red-800 bg-red-600'],
gray: ['border-app-line bg-app-button'],
dark_gray: ['border-app-box bg-app'],
accent: ['border-accent-deep bg-accent shadow-app-shade/10 shadow-md']
darkGray: ['border-app-box bg-app'],
accent: ['border-accent-deep bg-accent shadow-app-shade/10 shadow-md'],
outline: ['border-sidebar-line/60 ']
},
size: {
default: ['py-1', 'px-3'],
default: ['py-1.5', 'px-3'],
sm: ['py-1', 'px-2'],
md: ['py-1.5', 'px-3'],
lg: ['py-2', 'px-4']
},
disabled: {
@ -70,3 +70,18 @@ export const AnimatedButton: FC<AnimatedButtonProps> = ({ variant, size, disable
</MotiPressable>
);
};
// Useful for when you want to replicate a button but don't want to deal with the pressable logic (e.g. you need to disable the inner pressable)
type FakeButtonProps = VariantProps<typeof button> & ViewProps;
export const FakeButton: FC<FakeButtonProps> = ({ variant, size, ...props }) => {
const { style, ...otherProps } = props;
return (
<View
style={twStyle(button({ variant, size, disabled: false }), style as string)}
{...otherProps}
>
{props.children}
</View>
);
};

View file

@ -1,12 +1,4 @@
import { StyleProp, View, ViewStyle } from 'react-native';
import { tw } from '~/lib/tailwind';
import { View } from 'react-native';
import { styled } from '~/lib/tailwind';
type DividerProps = {
style?: StyleProp<ViewStyle>;
};
const Divider = ({ style }: DividerProps) => {
return <View style={[tw`bg-app-line my-1 h-[1px] w-full`, style]} />;
};
export default Divider;
export const Divider = styled(View, 'bg-app-line my-1 h-[1px] w-full');

View file

@ -1,6 +1,6 @@
import { PropsWithChildren } from 'react';
import { Text, View } from 'react-native';
import { tw } from '~/lib/tailwind';
import { styled, tw } from '~/lib/tailwind';
type SettingsContainerProps = PropsWithChildren<{
title?: string;
@ -16,3 +16,6 @@ export function SettingsContainer({ children, title, description }: SettingsCont
</View>
);
}
export const SettingsInputTitle = styled(Text, 'text-ink mb-1.5 ml-1 text-sm font-medium');
export const SettingsInputInfo = styled(Text, 'mt-2 text-xs text-ink-faint');

View file

@ -12,7 +12,7 @@ type SettingsItemProps = {
export function SettingsItem(props: SettingsItemProps) {
return (
<Pressable onPress={props.onPress}>
<View style={tw`bg-app-overlay flex flex-row items-center justify-between px-4`}>
<View style={tw`bg-app-box flex flex-row items-center justify-between px-4`}>
<View style={tw`flex flex-row items-center py-4`}>
{props.leftIcon && props.leftIcon({ size: 20, color: tw.color('ink'), style: tw`mr-3` })}
<Text style={tw`text-ink text-[14px]`}>{props.title}</Text>

View file

@ -0,0 +1,21 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { UseFormProps, useForm } from 'react-hook-form';
import { z } from 'zod';
interface UseZodFormProps<S extends z.ZodSchema>
extends Exclude<UseFormProps<z.infer<S>>, 'resolver'> {
schema?: S;
}
export const useZodForm = <S extends z.ZodSchema = z.ZodObject<Record<string, never>>>(
props?: UseZodFormProps<S>
) => {
const { schema, ...formProps } = props ?? {};
return useForm({
...formProps,
resolver: zodResolver(schema || z.object({}))
});
};
export { z } from 'zod';

View file

@ -1,7 +1,23 @@
import React, { ComponentType } from 'react';
import { create } from 'twrnc';
const tw = create(require(`../../tailwind.config.js`));
function styled<P>(Component: ComponentType<P>, baseStyles?: string) {
return React.forwardRef<ComponentType<P>, P>(({ style, ...props }: any, ref) =>
React.createElement(Component as any, {
...props,
style: twStyle(baseStyles, style),
ref
})
);
}
// Same as clsx, this works with the eslint plugin (tailwindcss/classnames-order).
const twStyle = tw.style;
export { tw, twStyle };
tw.style = () => {
throw new Error('Use twStyle instead of tw.style');
};
export { tw, twStyle, styled };

View file

@ -9,6 +9,7 @@ SplashScreen.preventAutoHideAsync();
const _localStorage = new Map<string, string>();
// We patch stuff onto `globalThis` so that `@sd/client` can use it. This is super hacky but as far as I can tell, there's no better way to do this.
// TODO: Add env value to automatically set this to `true` in development and false in production builds.
globalThis.isDev = true;
// Custom polyfill for browser `localStorage`

View file

@ -1,21 +1,37 @@
import { StackScreenProps, createStackNavigator } from '@react-navigation/stack';
import CreateLibraryScreen from '~/screens/onboarding/CreateLibrary';
import OnboardingScreen from '~/screens/onboarding/Onboarding';
import CreatingLibraryScreen from '~/screens/onboarding/CreatingLibrary';
import GetStartedScreen from '~/screens/onboarding/GetStarted';
import MasterPasswordScreen from '~/screens/onboarding/MasterPassword';
import NewLibraryScreen from '~/screens/onboarding/NewLibrary';
import PrivacyScreen from '~/screens/onboarding/Privacy';
const OnboardingStack = createStackNavigator<OnboardingStackParamList>();
export default function OnboardingNavigator() {
return (
<OnboardingStack.Navigator screenOptions={{ headerShown: false }}>
<OnboardingStack.Screen name="Onboarding" component={OnboardingScreen} />
<OnboardingStack.Screen name="CreateLibrary" component={CreateLibraryScreen} />
<OnboardingStack.Navigator initialRouteName="GetStarted" screenOptions={{ headerShown: false }}>
<OnboardingStack.Screen name="GetStarted" component={GetStartedScreen} />
<OnboardingStack.Screen name="NewLibrary" component={NewLibraryScreen} />
<OnboardingStack.Screen name="MasterPassword" component={MasterPasswordScreen} />
<OnboardingStack.Screen name="Privacy" component={PrivacyScreen} />
<OnboardingStack.Screen
name="CreatingLibrary"
component={CreatingLibraryScreen}
options={{
// Disable swipe back gesture
gestureEnabled: false
}}
/>
</OnboardingStack.Navigator>
);
}
export type OnboardingStackParamList = {
Onboarding: undefined;
CreateLibrary: undefined;
GetStarted: undefined;
NewLibrary: undefined;
MasterPassword: undefined;
Privacy: undefined;
CreatingLibrary: undefined;
};
export type OnboardingStackScreenProps<Screen extends keyof OnboardingStackParamList> =

View file

@ -8,6 +8,7 @@ import LibrarySettingsScreen from '~/screens/settings/client/LibrarySettings';
import PrivacySettingsScreen from '~/screens/settings/client/PrivacySettings';
import AboutScreen from '~/screens/settings/info/About';
import SupportScreen from '~/screens/settings/info/Support';
import EditLocationSettingsScreen from '~/screens/settings/library/EditLocationSettings';
import KeysSettingsScreen from '~/screens/settings/library/KeysSettings';
import LibraryGeneralSettingsScreen from '~/screens/settings/library/LibraryGeneralSettings';
import LocationSettingsScreen from '~/screens/settings/library/LocationSettings';
@ -71,6 +72,11 @@ export default function SettingsNavigator() {
component={LocationSettingsScreen}
options={{ headerTitle: 'Locations' }}
/>
<SettingsStack.Screen
name="EditLocationSettings"
component={EditLocationSettingsScreen}
options={{ headerTitle: 'Edit Location' }}
/>
<SettingsStack.Screen
name="NodesSettings"
component={NodesSettingsScreen}
@ -112,7 +118,11 @@ export type SettingsStackParamList = {
ExtensionsSettings: undefined;
// Library
LibraryGeneralSettings: undefined;
// Location
LocationSettings: undefined;
EditLocationSettings: { id: number };
NodesSettings: undefined;
TagsSettings: undefined;
KeysSettings: undefined;

View file

@ -1,11 +1,11 @@
import { BottomTabScreenProps, createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native';
import { CirclesFour, Planet, ShareNetwork } from 'phosphor-react-native';
import { Broadcast, CirclesFour, Planet, ShareNetwork } from 'phosphor-react-native';
import React from 'react';
import { tw } from '~/lib/tailwind';
import type { HomeDrawerScreenProps } from './DrawerNavigator';
import NodesStack, { NodesStackParamList } from './tabs/NodesStack';
import OverviewStack, { OverviewStackParamList } from './tabs/OverviewStack';
import SpacedropStack, { SpacedropStackParamList } from './tabs/SpacedropStack';
import SpacesStack, { SpacesStackParamList } from './tabs/SpacesStack';
const Tab = createBottomTabNavigator<TabParamList>();
@ -39,21 +39,6 @@ export default function TabNavigator() {
tabBarLabelStyle: tw`text-[10px] font-semibold`
}}
/>
<Tab.Screen
name="NodesStack"
component={NodesStack}
options={{
tabBarIcon: ({ focused }) => (
<ShareNetwork
size={22}
weight={focused ? 'bold' : 'regular'}
color={focused ? tw.color('accent') : tw.color('ink')}
/>
),
tabBarLabel: 'Nodes',
tabBarLabelStyle: tw`text-[10px] font-semibold`
}}
/>
<Tab.Screen
name="SpacesStack"
component={SpacesStack}
@ -69,13 +54,28 @@ export default function TabNavigator() {
tabBarLabelStyle: tw`text-[10px] font-semibold`
}}
/>
<Tab.Screen
name="SpacedropStack"
component={SpacedropStack}
options={{
tabBarIcon: ({ focused }) => (
<Broadcast
size={22}
weight={focused ? 'bold' : 'regular'}
color={focused ? tw.color('accent') : tw.color('ink')}
/>
),
tabBarLabel: 'Spacedrop',
tabBarLabelStyle: tw`text-[10px] font-semibold`
}}
/>
</Tab.Navigator>
);
}
export type TabParamList = {
OverviewStack: NavigatorScreenParams<OverviewStackParamList>;
NodesStack: NavigatorScreenParams<NodesStackParamList>;
SpacedropStack: NavigatorScreenParams<SpacedropStackParamList>;
SpacesStack: NavigatorScreenParams<SpacesStackParamList>;
};

View file

@ -2,16 +2,16 @@ import { CompositeScreenProps } from '@react-navigation/native';
import { StackScreenProps, createStackNavigator } from '@react-navigation/stack';
import Header from '~/components/header/Header';
import { tw } from '~/lib/tailwind';
import NodesScreen from '~/screens/Nodes';
import SpacedropScreen from '~/screens/Spacedrop';
import { SharedScreens, SharedScreensParamList } from '../SharedScreens';
import { TabScreenProps } from '../TabNavigator';
const Stack = createStackNavigator<NodesStackParamList>();
const Stack = createStackNavigator<SpacedropStackParamList>();
export default function NodesStack() {
export default function SpacedropStack() {
return (
<Stack.Navigator
initialRouteName="Nodes"
initialRouteName="Spacedrop"
screenOptions={{
headerStyle: { backgroundColor: tw.color('app-box') },
headerTintColor: tw.color('ink'),
@ -19,17 +19,18 @@ export default function NodesStack() {
headerBackTitleStyle: tw`text-base`
}}
>
<Stack.Screen name="Nodes" component={NodesScreen} options={{ header: Header }} />
<Stack.Screen name="Spacedrop" component={SpacedropScreen} options={{ header: Header }} />
{SharedScreens(Stack as any)}
</Stack.Navigator>
);
}
export type NodesStackParamList = {
Nodes: undefined;
export type SpacedropStackParamList = {
Spacedrop: undefined;
} & SharedScreensParamList;
export type NodesStackScreenProps<Screen extends keyof NodesStackParamList> = CompositeScreenProps<
StackScreenProps<NodesStackParamList, Screen>,
TabScreenProps<'NodesStack'>
>;
export type SpacedropStackScreenProps<Screen extends keyof SpacedropStackParamList> =
CompositeScreenProps<
StackScreenProps<SpacedropStackParamList, Screen>,
TabScreenProps<'SpacedropStack'>
>;

View file

@ -1,11 +0,0 @@
import { Text, View } from 'react-native';
import { tw } from '~/lib/tailwind';
import { NodesStackScreenProps } from '~/navigation/tabs/NodesStack';
export default function NodesScreen({ navigation }: NodesStackScreenProps<'Nodes'>) {
return (
<View style={tw`flex-1 items-center justify-center`}>
<Text style={tw`text-ink text-xl font-bold`}>Nodes</Text>
</View>
);
}

View file

@ -0,0 +1,141 @@
import { GoogleDrive, Mega, iCloud } from '@sd/assets/images';
import { DeviceMobile, Icon, Laptop, User } from 'phosphor-react-native';
import { Alert, Image, ImageSourcePropType, Pressable, ScrollView, Text, View } from 'react-native';
import { Polygon, Svg } from 'react-native-svg';
import { InfoPill } from '~/components/primitive/InfoPill';
import { tw, twStyle } from '~/lib/tailwind';
import { SpacedropStackScreenProps } from '~/navigation/tabs/SpacedropStack';
const testData = [
{
name: "Jamie's MacBook Pro",
receivingNodeOsType: 'macOS',
connectionType: 'lan',
icon: Laptop
},
{
name: "Jamie's MacBook Pro",
receivingNodeOsType: 'iOS',
connectionType: 'lan',
icon: DeviceMobile
},
{
name: 'Brendan Alan',
image: 'https://github.com/brendonovich.png',
connectionType: 'p2p'
},
{
name: 'Oscar Beaumont',
image: 'https://github.com/oscartbeaumont.png',
connectionType: 'usb'
},
{
name: 'maxichrome',
image: 'https://github.com/maxichrome.png',
connectionType: 'p2p'
},
{
name: 'Utku',
image: 'https://github.com/utkubakir.png',
connectionType: 'p2p'
},
{ name: "Jamie's Google Drive", brandIcon: 'google-drive', connectionType: 'cloud' },
{ name: 'iCloud', brandIcon: 'icloud', connectionType: 'cloud' },
{ name: 'Mega', brandIcon: 'mega', connectionType: 'cloud' }
] as DropItemProps[];
const Hexagon = () => {
const width = 180;
const height = width * 1.1547;
return (
<Svg width={width} height={height} viewBox="0 0 100 100">
<Polygon points="0,25 0,75 50,100 100,75 100,25 50,0" fill={tw.color('bg-app-box/30')} />
</Svg>
);
};
type OperatingSystem = 'browser' | 'linux' | 'macOS' | 'windows' | 'iOS' | 'android';
type DropItemProps = {
name: string;
connectionType: 'lan' | 'bluetooth' | 'usb' | 'p2p' | 'cloud';
receivingNodeOsType: OperatingSystem;
} & ({ image: string } | { icon: Icon } | { brandIcon: string });
function DropItem(props: DropItemProps) {
let icon;
if ('image' in props) {
icon = <Image style={tw`h-12 w-12 rounded-full`} source={{ uri: props.image }} />;
} else if ('brandIcon' in props) {
let brandIconSrc: ImageSourcePropType | undefined;
switch (props.brandIcon) {
case 'google-drive':
brandIconSrc = GoogleDrive;
break;
case 'icloud':
brandIconSrc = iCloud;
break;
case 'mega':
brandIconSrc = Mega;
break;
}
if (!brandIconSrc) throw new Error('Invalid brand icon url: ' + props.brandIcon);
icon = (
<View style={tw`flex items-center justify-center p-3`}>
{/* // Needs width and height */}
<Image source={brandIconSrc} style={tw`h-8 w-8 rounded-full`} />
</View>
);
} else {
// Use the custom icon or default to User icon.
const Icon = props.icon || User;
icon = <Icon size={30} color="white" style={twStyle(!props.name && 'opacity-20')} />;
}
return (
<View style={tw`relative`}>
<Hexagon />
<View style={tw`absolute h-full w-full items-center justify-center`}>
<Pressable
style={tw`w-full items-center justify-center`}
onPress={() => Alert.alert('TODO')}
>
<View style={tw`bg-app-button h-12 w-12 items-center justify-center rounded-full`}>
{icon}
</View>
{props.name && (
<Text numberOfLines={1} style={tw`mt-1 text-sm font-medium text-white`}>
{props.name}
</Text>
)}
<View style={tw`mt-1 flex flex-row gap-x-1`}>
{props.receivingNodeOsType && <InfoPill text={props.receivingNodeOsType} />}
{props.connectionType && (
<InfoPill
text={props.connectionType}
containerStyle={twStyle(
'px-1',
props.connectionType === 'lan' && 'bg-green-500',
props.connectionType === 'p2p' && 'bg-blue-500'
)}
textStyle={tw`uppercase text-white`}
/>
)}
</View>
</Pressable>
</View>
</View>
);
}
export default function SpacedropScreen({ navigation }: SpacedropStackScreenProps<'Spacedrop'>) {
return (
<View style={tw`flex-1 py-4`}>
<ScrollView contentContainerStyle={tw`flex flex-row flex-wrap justify-center gap-x-2`}>
{testData.map((item, i) => (
<DropItem key={i} {...item} />
))}
</ScrollView>
</View>
);
}

View file

@ -1,24 +0,0 @@
import { Text, View } from 'react-native';
import CreateLibraryDialog from '~/components/dialog/CreateLibraryDialog';
import { AnimatedButton } from '~/components/primitive/Button';
import { tw } from '~/lib/tailwind';
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
const CreateLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'CreateLibrary'>) => {
return (
<View style={tw`bg-app flex-1 items-center justify-center p-4`}>
<Text style={tw`text-ink-dull my-8 px-6 text-center text-base leading-relaxed`}>
Onboarding screen for users to create their first library
</Text>
<CreateLibraryDialog disableBackdropClose>
<AnimatedButton variant="accent">
<Text style={tw`text-ink px-6 py-2 text-center text-base font-medium`}>
Create Library
</Text>
</AnimatedButton>
</CreateLibraryDialog>
</View>
);
};
export default CreateLibraryScreen;

View file

@ -0,0 +1,87 @@
import { useQueryClient } from '@tanstack/react-query';
import React, { useEffect, useRef, useState } from 'react';
import { Text } from 'react-native';
import {
HASHING_ALGOS,
resetOnboardingStore,
telemetryStore,
useBridgeMutation,
useDebugState,
useOnboardingStore
} from '@sd/client';
import { PulseAnimation } from '~/components/animation/lottie';
import { tw } from '~/lib/tailwind';
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
import { currentLibraryStore } from '~/utils/nav';
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './GetStarted';
const CreatingLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'CreatingLibrary'>) => {
const [status, setStatus] = useState('Creating your library...');
const queryClient = useQueryClient();
const debugState = useDebugState();
const obStore = useOnboardingStore();
const createLibrary = useBridgeMutation('library.create', {
onSuccess: (lib) => {
resetOnboardingStore();
queryClient.setQueryData(['library.list'], (libraries: any) => [...(libraries || []), lib]);
// Switch to the new library
currentLibraryStore.id = lib.uuid;
},
onError: () => {
// TODO: Show toast
resetOnboardingStore();
navigation.navigate('GetStarted');
}
});
const created = useRef(false);
const create = async () => {
telemetryStore.shareTelemetry = obStore.shareTelemetry;
createLibrary.mutate({
name: obStore.newLibraryName,
auth: {
type: 'TokenizedPassword',
value: obStore.passwordSetToken || ''
},
algorithm: obStore.algorithm,
hashing_algorithm: HASHING_ALGOS[obStore.hashingAlgorithm]
});
return;
};
useEffect(() => {
if (created.current == true) return;
created.current = true;
create();
const timer = setTimeout(() => {
setStatus('Almost done...');
}, 2000);
const timer2 = setTimeout(() => {
if (debugState.enabled) {
setStatus(`You're running in development, this will take longer...`);
}
}, 5000);
return () => {
clearTimeout(timer);
clearTimeout(timer2);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<OnboardingContainer>
<Text style={tw`mb-4 text-5xl`}>🛠</Text>
<OnboardingTitle>Creating your library</OnboardingTitle>
<OnboardingDescription style={tw`mt-4`}>{status}</OnboardingDescription>
<PulseAnimation style={tw`mt-2 h-10`} speed={0.3} />
</OnboardingContainer>
);
};
export default CreatingLibraryScreen;

View file

@ -0,0 +1,82 @@
import { AppLogo, BloomOne } from '@sd/assets/images';
import { useNavigation, useRoute } from '@react-navigation/native';
import { CaretLeft } from 'phosphor-react-native';
import { Image, KeyboardAvoidingView, Platform, Pressable, Text, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { FadeInUpAnimation, LogoAnimation } from '~/components/animation/layout';
import { AnimatedButton } from '~/components/primitive/Button';
import { styled, tw, twStyle } from '~/lib/tailwind';
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
export function OnboardingContainer({ children }: React.PropsWithChildren) {
const navigation = useNavigation();
const route = useRoute();
const { top, bottom } = useSafeAreaInsets();
return (
<View style={tw`flex-1`}>
{route.name !== 'GetStarted' && route.name !== 'CreatingLibrary' && (
<Pressable
style={twStyle('absolute left-6 z-50', { top: top + 16 })}
onPress={() => navigation.goBack()}
>
<CaretLeft size={24} weight="bold" color="white" />
</Pressable>
)}
<View style={tw`z-10 flex-1 items-center justify-center`}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={bottom}
style={tw`w-full flex-1 items-center justify-center px-4`}
>
{children}
</KeyboardAvoidingView>
<Text style={tw`text-ink-dull/50 absolute bottom-8 text-xs`}>
&copy; 2022 Spacedrive Technology Inc.
</Text>
</View>
{/* Bloom */}
<Image source={BloomOne} style={tw`top-100 absolute h-screen w-screen opacity-20`} />
</View>
);
}
export const OnboardingTitle = styled(
Text,
'text-ink text-center text-4xl font-extrabold leading-tight'
);
export const OnboardingDescription = styled(
Text,
'text-ink-dull text-center text-base leading-relaxed'
);
const GetStartedScreen = ({ navigation }: OnboardingStackScreenProps<'GetStarted'>) => {
return (
<OnboardingContainer>
{/* Logo */}
<LogoAnimation style={tw`items-center`}>
<Image source={AppLogo} style={tw`h-30 w-30`} />
</LogoAnimation>
{/* Title */}
<FadeInUpAnimation delay={500} style={tw`mt-8`}>
<OnboardingTitle>The file explorer from the future.</OnboardingTitle>
</FadeInUpAnimation>
{/* Description */}
<FadeInUpAnimation delay={800} style={tw`mt-8`}>
<OnboardingDescription style={tw`px-4`}>
Welcome to Spacedrive, an open source cross-platform file manager.
</OnboardingDescription>
</FadeInUpAnimation>
{/* Get Started Button */}
<FadeInUpAnimation delay={1200} style={tw`mt-8`}>
<AnimatedButton variant="accent" onPress={() => navigation.push('NewLibrary')}>
<Text style={tw`text-ink text-center text-base font-medium`}>Get Started</Text>
</AnimatedButton>
</FadeInUpAnimation>
</OnboardingContainer>
);
};
export default GetStartedScreen;

View file

@ -0,0 +1,151 @@
import React, { lazy, useState } from 'react';
import { Controller } from 'react-hook-form';
import { Alert, Text, View } from 'react-native';
import { getOnboardingStore, useBridgeMutation, useOnboardingStore } from '@sd/client';
import { PasswordInput } from '~/components/form/Input';
import { Button } from '~/components/primitive/Button';
import { useZodForm, z } from '~/hooks/useZodForm';
import { tw } from '~/lib/tailwind';
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './GetStarted';
const PasswordMeter = lazy(() => import('~/components/key/PasswordMeter'));
const schema = z.object({
password: z.string(),
password_validate: z.string(),
algorithm: z.string(),
hashing_algorithm: z.string()
});
const MasterPasswordScreen = ({ navigation }: OnboardingStackScreenProps<'MasterPassword'>) => {
const [showPasswordValidate, setShowPasswordValidate] = useState(false);
const obStore = useOnboardingStore();
const form = useZodForm({
schema,
defaultValues: {
password: '',
password_validate: '',
algorithm: 'XChaCha20Poly1305',
hashing_algorithm: 'Argon2id-s'
}
});
const tokenizeSensitiveKey = useBridgeMutation('nodes.tokenizeSensitiveKey', {
onSuccess: (data) => {
getOnboardingStore().passwordSetToken = data.token;
navigation.navigate('Privacy');
},
onError: (err: any) => {
Alert.alert(err);
}
});
const handleSetPassword = form.handleSubmit(async (data) => {
if (data.password !== data.password_validate) {
if (!showPasswordValidate) {
setShowPasswordValidate(true);
// focus on confirm password field
} else {
form.setError('password_validate', {
type: 'manual',
message: 'Passwords do not match'
});
}
} else {
tokenizeSensitiveKey.mutate({
secret_key: data.password
});
}
});
const handleNoPassword = form.handleSubmit(async (data) => {
tokenizeSensitiveKey.mutate({ secret_key: '' });
});
const password = form.watch('password');
return (
<OnboardingContainer>
<OnboardingTitle>Set a master password</OnboardingTitle>
<OnboardingDescription style={tw`mt-4`}>
This will be used to encrypt your library and/or open the built-in key manager.
</OnboardingDescription>
<View style={tw`w-full`}>
<View style={tw`mt-4 mb-2`}>
<Controller
control={form.control}
name="password"
render={({ field: { onBlur, onChange, value } }) => (
<PasswordInput onChangeText={onChange} onBlur={onBlur} value={value} isNewPassword />
)}
/>
</View>
{showPasswordValidate && (
<View style={tw`my-2`}>
<Controller
control={form.control}
name="password_validate"
render={({ field: { onBlur, onChange, value } }) => (
<PasswordInput
onChangeText={onChange}
onBlur={onBlur}
value={value}
placeholder="Confirm password"
/>
)}
/>
</View>
)}
{form.formState.errors.password_validate && (
<Text style={tw`my-2 text-center text-xs font-bold text-red-500`}>
{form.formState.errors.password_validate.message}
</Text>
)}
<PasswordMeter containerStyle={tw`mt-3 px-2`} password={form.watch('password')} />
<View style={tw`mt-6`}>
{password.length > 0 && (
<Button
variant="outline"
size="sm"
disabled={form.formState.isSubmitting}
onPress={handleSetPassword}
>
<Text style={tw`text-ink text-center font-medium`}>
{!showPasswordValidate ? 'Set password' : 'Confirm Password'}
</Text>
</Button>
)}
<View style={tw`mt-4`}>
{obStore.passwordSetToken ? (
<Button
variant="outline"
size="sm"
disabled={form.formState.isSubmitting}
onPress={() => {
getOnboardingStore().passwordSetToken = null;
form.reset();
}}
>
<Text style={tw`text-ink text-center font-medium`}>Remove password</Text>
</Button>
) : (
<Button
variant="outline"
size="sm"
disabled={form.formState.isSubmitting}
onPress={handleNoPassword}
>
<Text style={tw`text-ink text-center font-medium`}>Continue without password</Text>
</Button>
)}
</View>
</View>
</View>
</OnboardingContainer>
);
};
export default MasterPasswordScreen;

View file

@ -0,0 +1,77 @@
import { Database } from '@sd/assets/icons';
import { Controller } from 'react-hook-form';
import { Alert, Image, Text, View } from 'react-native';
import { getOnboardingStore, useOnboardingStore } from '@sd/client';
import { Input } from '~/components/form/Input';
import { Button } from '~/components/primitive/Button';
import { useZodForm, z } from '~/hooks/useZodForm';
import { tw } from '~/lib/tailwind';
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './GetStarted';
const schema = z.object({
name: z.string().min(1, { message: 'Library name is required' })
});
const NewLibraryScreen = ({ navigation }: OnboardingStackScreenProps<'NewLibrary'>) => {
const obStore = useOnboardingStore();
const form = useZodForm({
schema,
defaultValues: {
name: obStore.newLibraryName
}
});
const handleNewLibrary = form.handleSubmit(async (data) => {
getOnboardingStore().newLibraryName = data.name;
navigation.navigate('MasterPassword');
});
const handleImport = () => {
Alert.alert('TODO');
};
return (
<OnboardingContainer>
<Image source={Database} style={tw`h-25 w-25`} />
<OnboardingTitle style={tw`mt-4`}>Create a Library</OnboardingTitle>
<View style={tw`w-full px-4`}>
<OnboardingDescription style={tw`mt-4`}>
Libraries are a secure, on-device database. Your files remain where they are, the Library
catalogs them and stores all Spacedrive related data.
</OnboardingDescription>
<Controller
name="name"
control={form.control}
render={({ field: { onBlur, onChange, value } }) => (
<Input
style={tw`my-3`}
placeholder='e.g. "James Library"'
onBlur={onBlur}
onChangeText={onChange}
value={value}
/>
)}
/>
</View>
{form.formState.errors.name && (
<Text style={tw`text-center text-xs font-bold text-red-500`}>
{form.formState.errors.name.message}
</Text>
)}
<View style={tw`mt-4 flex w-full flex-row items-center justify-center`}>
<Button variant="accent" onPress={handleNewLibrary}>
<Text style={tw`text-ink text-center font-medium`}>New Library</Text>
</Button>
<Text style={tw`text-ink-faint px-4 text-xs font-bold`}>OR</Text>
<Button onPress={handleImport} variant="outline">
<Text style={tw`text-ink text-center font-medium`}>Import Library</Text>
</Button>
</View>
</OnboardingContainer>
);
};
export default NewLibraryScreen;

View file

@ -1,41 +0,0 @@
import { AppLogo } from '@sd/assets/images';
import { Image, Text, View } from 'react-native';
import { FadeInUpAnimation, LogoAnimation } from '~/components/animation/layout';
import { AnimatedButton } from '~/components/primitive/Button';
import { tw } from '~/lib/tailwind';
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding'>) => {
return (
<View style={tw`bg-app z-10 flex-1 items-center justify-around p-4`}>
{/* Logo */}
<LogoAnimation>
<View style={tw`mt-2 items-center`}>
<Image source={AppLogo} style={tw`h-24 w-24`} />
</View>
</LogoAnimation>
{/* Text */}
<View>
<FadeInUpAnimation delay={500}>
<Text style={tw`text-ink text-center text-5xl font-black leading-tight`}>
A file explorer from the future.
</Text>
</FadeInUpAnimation>
<FadeInUpAnimation delay={800}>
<Text style={tw`text-ink-dull mt-8 px-6 text-center text-base leading-relaxed`}>
Combine your drives and clouds into one database that you can organize and explore from
any device.
</Text>
</FadeInUpAnimation>
</View>
{/* Get Started Button */}
<FadeInUpAnimation delay={1200}>
<AnimatedButton variant="accent" onPress={() => navigation.navigate('CreateLibrary')}>
<Text style={tw`text-ink px-6 py-2 text-center text-base font-medium`}>Get Started</Text>
</AnimatedButton>
</FadeInUpAnimation>
</View>
);
};
export default OnboardingScreen;

View file

@ -0,0 +1,82 @@
import React, { useState } from 'react';
import { Pressable, Text, View, ViewStyle } from 'react-native';
import { getOnboardingStore } from '@sd/client';
import { Button } from '~/components/primitive/Button';
import { tw, twStyle } from '~/lib/tailwind';
import { OnboardingStackScreenProps } from '~/navigation/OnboardingNavigator';
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './GetStarted';
type RadioButtonProps = {
title: string;
description: string;
isSelected: boolean;
style?: ViewStyle;
};
// Make this a component?
const RadioButton = ({ title, description, isSelected, style }: RadioButtonProps) => {
return (
<View
style={twStyle(
'border-app-line bg-app-box/50 flex w-full flex-row items-center rounded-md border p-3',
style
)}
>
<View
style={twStyle(
'mr-2.5 h-5 w-5 items-center justify-center rounded-full',
isSelected ? 'bg-accent' : 'bg-gray-900'
)}
>
{isSelected && <View style={tw`h-1.5 w-1.5 rounded-full bg-white`} />}
</View>
<View style={tw`flex-1`}>
<Text style={tw`text-ink text-base font-bold`}>{title}</Text>
<Text style={tw`text-ink-faint text-sm`}>{description}</Text>
</View>
</View>
);
};
const PrivacyScreen = ({ navigation }: OnboardingStackScreenProps<'Privacy'>) => {
const [shareTelemetry, setShareTelemetry] = useState<'share-telemetry' | 'no-share-telemetry'>(
'share-telemetry'
);
const onPress = () => {
getOnboardingStore().shareTelemetry = shareTelemetry === 'share-telemetry';
navigation.navigate('CreatingLibrary');
};
return (
<OnboardingContainer>
<OnboardingTitle>Your Privacy</OnboardingTitle>
<OnboardingDescription style={tw`mt-4`}>
Spacedrive is built for privacy, that's why we're open source and local first. So we'll make
it very clear what data is shared with us.
</OnboardingDescription>
<View style={tw`w-full`}>
<Pressable onPress={() => setShareTelemetry('share-telemetry')}>
<RadioButton
title="Share anonymous usage"
description="Share completely anonymous telemetry data to help the developers improve the app"
isSelected={shareTelemetry === 'share-telemetry'}
style={tw`mt-4 mb-3`}
/>
</Pressable>
<Pressable onPress={() => setShareTelemetry('no-share-telemetry')}>
<RadioButton
title="Share nothing"
description="Do not share any telemetry data with the developers"
isSelected={shareTelemetry === 'no-share-telemetry'}
/>
</Pressable>
</View>
<Button variant="accent" size="sm" onPress={onPress} style={tw`mt-6`}>
<Text style={tw`text-ink text-center text-base font-medium`}>Continue</Text>
</Button>
</OnboardingContainer>
);
};
export default PrivacyScreen;

View file

@ -109,7 +109,7 @@ function renderSectionHeader({ section }: { section: { title: string } }) {
return (
<Text
style={twStyle(
'text-ink-dull mb-2 ml-3 text-sm font-semibold',
'text-ink mb-2 ml-2 text-sm font-bold',
section.title === 'Client' ? 'mt-2' : 'mt-5'
)}
>
@ -129,14 +129,15 @@ export default function SettingsScreen({ navigation }: SettingsStackScreenProps<
<SettingsItem
title={item.title}
leftIcon={item.icon}
onPress={() => navigation.navigate(item.navigateTo)}
onPress={() => navigation.navigate(item.navigateTo as any)}
/>
)}
renderSectionHeader={renderSectionHeader}
ListFooterComponent={
<View style={tw`mt-6 mb-4 items-center`}>
<Text style={tw`text-ink text-sm font-bold`}>Spacedrive</Text>
<Text style={tw`text-ink-faint mt-0.5 text-xs`}>v0.1.0</Text>
<Text style={tw`text-ink text-base font-bold`}>Spacedrive</Text>
{/* TODO: Get this automatically (expo-device have this?) */}
<Text style={tw`text-ink-faint mt-0.5 text-xs font-medium`}>v0.1.0</Text>
</View>
}
showsVerticalScrollIndicator={false}

View file

@ -1,9 +1,9 @@
import React from 'react';
import { Text, View } from 'react-native';
import { useBridgeQuery } from '@sd/client';
import { Input } from '~/components/form/Input';
import Card from '~/components/layout/Card';
import Divider from '~/components/primitive/Divider';
import { Divider } from '~/components/primitive/Divider';
import { SettingsInputTitle } from '~/components/settings/SettingsContainer';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
@ -14,7 +14,7 @@ const GeneralSettingsScreen = ({ navigation }: SettingsStackScreenProps<'General
return (
<View style={tw`flex-1 p-4`}>
<Card>
<Card style={tw`bg-app-box`}>
{/* Card Header */}
<View style={tw`flex flex-row justify-between`}>
<Text style={tw`text-ink font-semibold`}>Connected Node</Text>
@ -32,9 +32,9 @@ const GeneralSettingsScreen = ({ navigation }: SettingsStackScreenProps<'General
{/* Divider */}
<Divider style={tw`mt-2 mb-4`} />
{/* Node Name and Port */}
<Text style={tw`text-ink-dull mb-1 ml-1 text-xs font-medium`}>Node Name</Text>
<SettingsInputTitle>Node Name</SettingsInputTitle>
<Input value={node.name} />
<Text style={tw`text-ink-dull mt-2 mb-1 ml-1 text-xs font-medium`}>Node Port</Text>
<SettingsInputTitle style={tw`mt-3`}>Node Port</SettingsInputTitle>
<Input value={node.p2p_port?.toString() ?? '5795'} keyboardType="numeric" />
</Card>
</View>

View file

@ -31,13 +31,13 @@ function LibraryItem({
<Animated.View
style={[tw`flex flex-row items-center`, { transform: [{ translateX: translate }] }]}
>
<AnimatedButton size="md" onPress={() => navigation.replace('LibraryGeneralSettings')}>
<AnimatedButton onPress={() => navigation.replace('LibraryGeneralSettings')}>
<Pen size={18} color="white" />
</AnimatedButton>
<DeleteLibraryModal
libraryUuid={library.uuid}
trigger={
<AnimatedButton size="md" style={tw`mx-2`}>
<AnimatedButton style={tw`mx-2`}>
<Trash size={18} color="white" />
</AnimatedButton>
}

View file

@ -0,0 +1,218 @@
import { useQueryClient } from '@tanstack/react-query';
import { Archive, ArrowsClockwise, Trash } from 'phosphor-react-native';
import React from 'react';
import { Controller } from 'react-hook-form';
import { Alert, ScrollView, Text, View } from 'react-native';
import { useLibraryMutation, useLibraryQuery } from '@sd/client';
import { Input } from '~/components/form/Input';
import { Switch } from '~/components/form/Switch';
import DeleteLocationModal from '~/components/modal/confirm-modals/DeleteLocationModal';
import { AnimatedButton, Button, FakeButton } from '~/components/primitive/Button';
import { Divider } from '~/components/primitive/Divider';
import {
SettingsContainer,
SettingsInputInfo,
SettingsInputTitle
} from '~/components/settings/SettingsContainer';
import { SettingsItem } from '~/components/settings/SettingsItem';
import { useZodForm, z } from '~/hooks/useZodForm';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const schema = z.object({
displayName: z.string(),
localPath: z.string(),
indexer_rules_ids: z.array(z.string()),
generatePreviewMedia: z.boolean(),
syncPreviewMedia: z.boolean(),
hidden: z.boolean()
});
const EditLocationSettingsScreen = ({
route,
navigation
}: SettingsStackScreenProps<'EditLocationSettings'>) => {
const { id } = route.params;
const queryClient = useQueryClient();
const form = useZodForm({ schema });
const updateLocation = useLibraryMutation('locations.update', {
onError: (e) => console.log({ e }),
onSuccess: () => {
form.reset(form.getValues());
queryClient.invalidateQueries(['locations.list']);
// TODO: Show toast & navigate back & reset input focus!
}
});
const onSubmit = form.handleSubmit((data) =>
updateLocation.mutateAsync({
id: Number(id),
name: data.displayName,
sync_preview_media: data.syncPreviewMedia,
generate_preview_media: data.generatePreviewMedia,
hidden: data.hidden,
indexer_rules_ids: []
})
);
navigation.setOptions({
headerRight: () => (
<View style={tw`mr-1 flex flex-row gap-x-1`}>
{form.formState.isDirty && (
<AnimatedButton
variant="outline"
onPress={() => form.reset()}
disabled={!form.formState.isDirty}
>
<Text style={tw`text-white`}>Reset</Text>
</AnimatedButton>
)}
<AnimatedButton
onPress={onSubmit}
disabled={!form.formState.isDirty || form.formState.isSubmitting}
variant={form.formState.isDirty ? 'accent' : 'outline'}
>
<Text style={tw`font-bold text-white`}>Save</Text>
</AnimatedButton>
</View>
)
});
useLibraryQuery(['locations.getById', id], {
onSuccess: (data) => {
if (data && !form.formState.isDirty)
form.reset({
displayName: data.name,
localPath: data.path,
indexer_rules_ids: data.indexer_rules.map((i) => i.indexer_rule.id.toString()),
generatePreviewMedia: data.generate_preview_media,
syncPreviewMedia: data.sync_preview_media,
hidden: data.hidden
});
}
});
const fullRescan = useLibraryMutation('locations.fullRescan');
return (
<ScrollView contentContainerStyle={tw`gap-y-6 pb-12 pt-4`}>
{/* Inputs */}
<View style={tw`px-2`}>
<SettingsInputTitle>Display Name</SettingsInputTitle>
<Controller
name="displayName"
control={form.control}
render={({ field: { onBlur, onChange, value } }) => (
<Input onBlur={onBlur} onChangeText={onChange} value={value} />
)}
/>
<SettingsInputInfo>
The name of this Location, this is what will be displayed in the sidebar. Will not rename
the actual folder on disk.
</SettingsInputInfo>
<SettingsInputTitle style={tw`mt-3`}>Local Path</SettingsInputTitle>
<Controller
name="localPath"
control={form.control}
render={({ field: { onBlur, onChange, value } }) => (
<Input onBlur={onBlur} onChangeText={onChange} value={value} />
)}
/>
<SettingsInputInfo>
The path to this Location, this is where the files will be stored on disk.
</SettingsInputInfo>
</View>
<Divider style={tw`my-0`} />
{/* Switches */}
<View style={tw`gap-y-6`}>
<SettingsContainer>
<SettingsItem
title="Generate preview media"
rightArea={
<Controller
name="generatePreviewMedia"
control={form.control}
render={({ field: { onChange, value } }) => (
<Switch value={value} onValueChange={onChange} />
)}
/>
}
/>
<SettingsItem
title="Sync preview media with your devices"
rightArea={
<Controller
name="syncPreviewMedia"
control={form.control}
render={({ field: { onChange, value } }) => (
<Switch value={value} onValueChange={onChange} />
)}
/>
}
/>
<SettingsItem
title="Hide location and contents from view"
rightArea={
<Controller
name="hidden"
control={form.control}
render={({ field: { onChange, value } }) => (
<Switch value={value} onValueChange={onChange} />
)}
/>
}
/>
</SettingsContainer>
</View>
{/* Indexer Rules */}
<Text style={tw`text-center text-xs font-bold text-white`}>TODO: Indexer Rules</Text>
{/* Buttons */}
<View style={tw`gap-y-6`}>
<SettingsContainer description="Perform a full rescan of this Location.">
<SettingsItem
title="Full Reindex"
rightArea={
<AnimatedButton size="sm" onPress={() => fullRescan.mutate(id)}>
<ArrowsClockwise color="white" size={20} />
</AnimatedButton>
}
/>
</SettingsContainer>
<SettingsContainer description="Extract data from Library as an archive, useful to preserve Location folder structure.">
<SettingsItem
title="Archive"
rightArea={
<AnimatedButton
size="sm"
onPress={() => Alert.alert('Archiving locations is coming soon...')}
>
<Archive color="white" size={20} />
</AnimatedButton>
}
/>
</SettingsContainer>
<SettingsContainer description="This will not delete the actual folder on disk. Preview media will be...???">
<SettingsItem
title="Delete"
rightArea={
<DeleteLocationModal
locationId={id}
trigger={
<FakeButton size="sm" variant="danger">
<Trash color={tw.color('ink')} size={20} />
</FakeButton>
}
/>
}
/>
</SettingsContainer>
</View>
</ScrollView>
);
};
export default EditLocationSettingsScreen;

View file

@ -1,23 +1,29 @@
import { Trash } from 'phosphor-react-native';
import React from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Alert, Text, View } from 'react-native';
import { Controller } from 'react-hook-form';
import { Alert, View } from 'react-native';
import { useBridgeMutation, useLibraryContext } from '@sd/client';
import { Input } from '~/components/form/Input';
import { Switch } from '~/components/form/Switch';
import { Button } from '~/components/primitive/Button';
import { SettingsContainer } from '~/components/settings/SettingsContainer';
import DeleteLibraryModal from '~/components/modal/confirm-modals/DeleteLibraryModal';
import { AnimatedButton } from '~/components/primitive/Button';
import { Divider } from '~/components/primitive/Divider';
import { SettingsContainer, SettingsInputTitle } from '~/components/settings/SettingsContainer';
import { SettingsItem } from '~/components/settings/SettingsItem';
import { useAutoForm } from '~/hooks/useAutoForm';
import { useZodForm, z } from '~/hooks/useZodForm';
import { tw } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
const schema = z.object({ name: z.string(), description: z.string() });
const LibraryGeneralSettingsScreen = ({
navigation
}: SettingsStackScreenProps<'LibraryGeneralSettings'>) => {
const { library } = useLibraryContext();
const form = useForm({
const form = useZodForm({
schema,
defaultValues: { name: library.config.name, description: library.config.description }
});
@ -25,15 +31,14 @@ const LibraryGeneralSettingsScreen = ({
useAutoForm(form, (value) => {
editLibrary({ description: value.description, name: value.name, id: library.uuid });
console.log('Updated', value);
// console.log('Updated', value);
// TODO: Show toast
});
return (
<View>
{/* This looks bad... */}
<View style={tw`bg-app-overlay mt-4 px-2 py-4`}>
<Text style={tw`text-ink-dull mb-1 ml-1 text-xs font-medium`}>Name</Text>
<View style={tw`gap-4`}>
<View style={tw`mt-4 px-2`}>
<SettingsInputTitle>Name</SettingsInputTitle>
<Controller
name="name"
control={form.control}
@ -42,7 +47,7 @@ const LibraryGeneralSettingsScreen = ({
)}
/>
{/* Description */}
<Text style={tw`text-ink-dull mb-1 ml-1 mt-3 text-xs font-medium`}>Description</Text>
<SettingsInputTitle style={tw`mt-4`}>Description</SettingsInputTitle>
<Controller
name="description"
control={form.control}
@ -51,27 +56,31 @@ const LibraryGeneralSettingsScreen = ({
)}
/>
</View>
{/* Encrypt */}
<View style={tw`mt-6`} />
<SettingsContainer description="Enable encryption for this library, this will only encrypt the Spacedrive database, not the files themselves.">
<SettingsItem title="Encrypt Library" rightArea={<Switch value={true} />} />
</SettingsContainer>
<View style={tw`mt-6`} />
{/* Export */}
<SettingsItem title="Export Library" onPress={() => Alert.alert('TODO')} />
<View style={tw`mt-4`} />
{/* Delete Library */}
{/* TODO: Open delete library dialog here, but do handle library switching and what happens if there is no library set ? */}
<SettingsContainer description="This is permanent, your files will not be deleted, only the Spacedrive library.">
<SettingsItem
title="Delete Library"
rightArea={
<Button size="sm" variant="danger" onPress={() => Alert.alert('TODO')}>
<Trash color={tw.color('ink')} size={20} />
</Button>
}
/>
</SettingsContainer>
<Divider />
<View style={tw`gap-y-6`}>
{/* Encrypt */}
<SettingsContainer description="Enable encryption for this library, this will only encrypt the Spacedrive database, not the files themselves.">
<SettingsItem title="Encrypt Library" rightArea={<Switch value={true} />} />
</SettingsContainer>
{/* Export */}
<SettingsItem title="Export Library" onPress={() => Alert.alert('TODO')} />
{/* Delete Library */}
<SettingsContainer description="This is permanent, your files will not be deleted, only the Spacedrive library.">
<SettingsItem
title="Delete Library"
rightArea={
<DeleteLibraryModal
libraryUuid={library.uuid}
trigger={
<AnimatedButton size="sm" variant="danger">
<Trash color={tw.color('ink')} size={20} />
</AnimatedButton>
}
/>
}
/>
</SettingsContainer>
</View>
</View>
);
};

View file

@ -1,4 +1,4 @@
import { CaretRight, Repeat, Trash } from 'phosphor-react-native';
import { CaretRight, Pen, Repeat, Trash } from 'phosphor-react-native';
import { Animated, FlatList, Pressable, Text, View } from 'react-native';
import { Swipeable } from 'react-native-gesture-handler';
import {
@ -14,7 +14,13 @@ import DeleteLocationModal from '~/components/modal/confirm-modals/DeleteLocatio
import { tw, twStyle } from '~/lib/tailwind';
import { SettingsStackScreenProps } from '~/navigation/SettingsNavigator';
function LocationItem({ location, index }: { location: Location & { node: Node }; index: number }) {
type LocationItemProps = {
location: Location & { node: Node };
index: number;
navigation: SettingsStackScreenProps<'LocationSettings'>['navigation'];
};
function LocationItem({ location, index, navigation }: LocationItemProps) {
const fullRescan = useLibraryMutation('locations.fullRescan', {
onMutate: () => {
// TODO: Show Toast
@ -23,7 +29,11 @@ function LocationItem({ location, index }: { location: Location & { node: Node }
const onlineLocations = useOnlineLocations();
const renderRightActions = (progress: Animated.AnimatedInterpolation<number>) => {
const renderRightActions = (
progress: Animated.AnimatedInterpolation<number>,
_: any,
swipeable: Swipeable
) => {
const translate = progress.interpolate({
inputRange: [0, 1],
outputRange: [100, 0],
@ -32,8 +42,20 @@ function LocationItem({ location, index }: { location: Location & { node: Node }
return (
<Animated.View
style={[tw`flex flex-row items-center`, { transform: [{ translateX: translate }] }]}
style={[
tw`mr-3 flex flex-row items-center gap-2`,
{ transform: [{ translateX: translate }] }
]}
>
<Pressable
style={tw`border-app-line bg-app-button items-center justify-center rounded-md border py-1.5 px-3 shadow-sm`}
onPress={() => {
navigation.navigate('EditLocationSettings', { id: location.id });
swipeable.close();
}}
>
<Pen size={18} color="white" />
</Pressable>
<DeleteLocationModal
locationId={location.id}
trigger={
@ -46,7 +68,7 @@ function LocationItem({ location, index }: { location: Location & { node: Node }
/>
{/* Full Re-scan IS too much here */}
<Pressable
style={tw`border-app-line bg-app-button mx-2 items-center justify-center rounded-md border py-1.5 px-3 shadow-sm`}
style={tw`border-app-line bg-app-button items-center justify-center rounded-md border py-1.5 px-3 shadow-sm`}
onPress={() => fullRescan.mutate(location.id)}
>
<Repeat size={18} color="white" />
@ -106,7 +128,9 @@ const LocationSettingsScreen = ({ navigation }: SettingsStackScreenProps<'Locati
<FlatList
data={locations}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item, index }) => <LocationItem location={item} index={index} />}
renderItem={({ item, index }) => (
<LocationItem navigation={navigation} location={item} index={index} />
)}
/>
</View>
);

View file

@ -29,13 +29,13 @@ function TagItem({ tag, index }: { tag: Tag; index: number }) {
style={[tw`flex flex-row items-center`, { transform: [{ translateX: translate }] }]}
>
<UpdateTagModal tag={tag} ref={updateTagModalRef} onSubmit={() => swipeable.close()} />
<AnimatedButton size="md" onPress={() => updateTagModalRef.current?.present()}>
<AnimatedButton onPress={() => updateTagModalRef.current?.present()}>
<Pen size={18} color="white" />
</AnimatedButton>
<DeleteTagModal
tagId={tag.id}
trigger={
<AnimatedButton size="md" style={tw`mx-2`}>
<AnimatedButton style={tw`mx-2`}>
<Trash size={18} color="white" />
</AnimatedButton>
}

View file

@ -36,6 +36,8 @@ module.exports = {
button: 'hsla(230, 15%, 23%, 1)',
// menu
menu: 'hsla(230, 25%, 5%, 1)',
// input
input: 'hsla(230, 15%, 20%, 1)',
50: 'hsla(230, 15%, 5%, 1)',
100: 'hsla(230, 15%, 10%, 1)',
150: 'hsla(230, 15%, 15%, 1)',

View file

@ -112,8 +112,8 @@ impl Node {
);
#[cfg(not(target_os = "android"))]
let subscriber = subscriber.with(tracing_subscriber::fmt::layer().with_filter(CONSOLE_LOG_FILTER));
#[cfg(target_os = "android")]
let subscriber = subscriber.with(tracing_android::layer("com.spacedrive.app").unwrap()); // TODO: This is not working
// #[cfg(target_os = "android")]
// let subscriber = subscriber.with(tracing_android::layer("com.spacedrive.app").unwrap()); // TODO: This is not working
subscriber
// .with(
// Layer::default()

View file

@ -59,7 +59,7 @@ To run mobile app
1. Install [Android Studio](https://developer.android.com/studio) for Android and [Xcode](https://apps.apple.com/au/app/xcode/id497799835) for IOS development
2. `./.github/scripts/setup-system.sh mobile`
_The should setup most of the dependencies for the mobile app to build._
3. You must also ensure you have [NDK 24.0.8215888 and CMake](https://developer.android.com/studio/projects/install-ndk#default-version) in Android Studio
3. You must also ensure you have [NDK 23.1.7779620 and CMake](https://developer.android.com/studio/projects/install-ndk#default-version) in Android Studio
4. `pnpm mobile android` - runs on Android Emulator
5. `pnpm mobile ios` - runs on iOS Emulator
@ -68,4 +68,4 @@ To run mobile app
If you are having issues ensure you are using the following versions of Rust and Node:
- Rust version: **1.67.0**
- Node version: **17**
- Node version: **18**

View file

@ -1,14 +1,9 @@
import { Eye, EyeSlash, Info } from 'phosphor-react';
import { useEffect, useRef, useState } from 'react';
import {
Algorithm,
HASHING_ALGOS,
HashingAlgoSlug,
generatePassword,
useLibraryMutation
} from '@sd/client';
import { Algorithm, HASHING_ALGOS, HashingAlgoSlug, useLibraryMutation } from '@sd/client';
import { Button, CategoryHeading, Input, Select, SelectOption, Slider, Switch, tw } from '@sd/ui';
import { Tooltip } from '@sd/ui';
import { generatePassword } from '~/util';
const KeyHeading = tw(CategoryHeading)`mb-1`;

View file

@ -1,9 +1,10 @@
import { useForm } from 'react-hook-form';
import { useBridgeMutation, useLibraryContext } from '@sd/client';
import { Button, Input, Switch } from '@sd/ui';
import { Button, Input, Switch, dialogManager } from '@sd/ui';
import { useDebouncedFormWatch } from '~/hooks/useDebouncedForm';
import { Heading } from '../Layout';
import Setting from '../Setting';
import DeleteLibraryDialog from '../node/libraries/DeleteDialog';
export const Component = () => {
const { library } = useLibraryContext();
@ -64,7 +65,16 @@ export const Component = () => {
description="This is permanent, your files will not be deleted, only the Spacedrive library."
>
<div className="mt-2">
<Button size="sm" variant="colored" className="border-red-500 bg-red-500">
<Button
size="sm"
variant="colored"
className="border-red-500 bg-red-500"
onClick={() => {
dialogManager.create((dp) => (
<DeleteLibraryDialog {...dp} libraryUuid={library.uuid} />
));
}}
>
Delete
</Button>
</div>

View file

@ -4,22 +4,14 @@ import {
Algorithm,
HASHING_ALGOS,
HashingAlgoSlug,
generatePassword,
hashingAlgoSlugSchema,
useLibraryMutation
} from '@sd/client';
import {
Button,
Dialog,
Input,
PasswordMeter,
Select,
SelectOption,
UseDialogProps,
useDialog
} from '@sd/ui';
import { Button, Dialog, Input, Select, SelectOption, UseDialogProps, useDialog } from '@sd/ui';
import { useZodForm, z } from '@sd/ui/src/forms';
import { showAlertDialog } from '~/components/AlertDialog';
import { PasswordMeter } from '~/components/PasswordMeter';
import { generatePassword } from '~/util';
const schema = z.object({
masterPassword: z.string(),

View file

@ -5,7 +5,6 @@ import {
Algorithm,
HASHING_ALGOS,
HashingAlgoSlug,
generatePassword,
hashingAlgoSlugSchema,
useBridgeMutation,
usePlausibleEvent
@ -14,7 +13,6 @@ import {
Button,
CheckBox,
Dialog,
PasswordMeter,
Select,
SelectOption,
Tooltip,
@ -22,6 +20,8 @@ import {
useDialog
} from '@sd/ui';
import { forms } from '@sd/ui';
import { PasswordMeter } from '~/components/PasswordMeter';
import { generatePassword } from '~/util';
import { usePlatform } from '~/util/Platform';
const { Input, z, useZodForm } = forms;

View file

@ -1,9 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { useQueryClient } from '@tanstack/react-query';
import { useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router';
import {
Algorithm,
HASHING_ALGOS,
resetOnboardingStore,
telemetryStore,
@ -59,7 +57,7 @@ export default function OnboardingCreatingLibrary() {
type: 'TokenizedPassword',
value: obStore.passwordSetToken || ''
},
algorithm: obStore.algorithm as Algorithm,
algorithm: obStore.algorithm,
hashing_algorithm: HASHING_ALGOS[obStore.hashingAlgorithm]
});
@ -84,6 +82,7 @@ export default function OnboardingCreatingLibrary() {
clearTimeout(timer);
clearTimeout(timer2);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (

View file

@ -1,8 +1,9 @@
import { useState } from 'react';
import { useNavigate } from 'react-router';
import { getOnboardingStore, useBridgeMutation, useOnboardingStore } from '@sd/client';
import { Button, Card, PasswordMeter } from '@sd/ui';
import { Button, Card } from '@sd/ui';
import { Form, PasswordInput, useZodForm, z } from '@sd/ui/src/forms';
import { PasswordMeter } from '~/components/PasswordMeter';
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './Layout';
import { useUnlockOnboardingScreen } from './Progress';
@ -118,7 +119,9 @@ export default function OnboardingNewLibrary() {
disabled={form.formState.isSubmitting}
variant="outline"
size="sm"
onClick={() => {
onClick={(event: any) => {
// Without this, form is submitted before token gets removed
event.preventDefault();
getOnboardingStore().passwordSetToken = null;
form.reset();
}}

View file

@ -0,0 +1,45 @@
import clsx from 'clsx';
import { getPasswordStrength } from '@sd/client';
export interface PasswordMeterProps {
password: string;
}
export const PasswordMeter = (props: PasswordMeterProps) => {
const { score, scoreText } = getPasswordStrength(props.password);
return (
<div className="relative">
<h3 className="text-sm">Password strength</h3>
<span
className={clsx(
'absolute top-0.5 right-0 px-1 text-sm font-semibold',
score === 0 && 'text-red-500',
score === 1 && 'text-red-500',
score === 2 && 'text-amber-400',
score === 3 && 'text-lime-500',
score === 4 && 'text-accent'
)}
>
{scoreText}
</span>
<div className="flex grow">
<div className="bg-app-box/50 mt-2 w-full rounded-full">
<div
style={{
width: `${score !== 0 ? score * 25 : 12.5}%`
}}
className={clsx(
'h-2 rounded-full',
score === 0 && 'bg-red-500',
score === 1 && 'bg-red-500',
score === 2 && 'bg-amber-400',
score === 3 && 'bg-lime-500',
score === 4 && 'bg-accent'
)}
/>
</div>
</div>
</div>
);
};

View file

@ -19,6 +19,7 @@
"@fontsource/inter": "^4.5.13",
"@headlessui/react": "^1.7.3",
"@hookform/resolvers": "^2.9.10",
"crypto-random-string": "^5.0.0",
"@radix-ui/react-progress": "^1.0.1",
"@radix-ui/react-slider": "^1.1.0",
"@radix-ui/react-toast": "^1.1.2",
@ -35,9 +36,6 @@
"@tanstack/react-query-devtools": "^4.22.0",
"@tanstack/react-virtual": "3.0.0-beta.18",
"@vitejs/plugin-react": "^2.1.0",
"@zxcvbn-ts/core": "^2.1.0",
"@zxcvbn-ts/language-common": "^2.0.1",
"@zxcvbn-ts/language-en": "^2.1.0",
"autoprefixer": "^10.4.12",
"byte-size": "^8.1.0",
"class-variance-authority": "^0.4.0",
@ -48,7 +46,7 @@
"react-colorful": "^5.6.1",
"react-dom": "^18.2.0",
"react-error-boundary": "^3.1.4",
"react-hook-form": "^7.36.1",
"react-hook-form": "^7.43.5",
"react-json-view": "^1.21.3",
"react-loading-skeleton": "^3.1.0",
"react-qr-code": "^2.0.11",

5
interface/util/index.tsx Normal file
View file

@ -0,0 +1,5 @@
import cryptoRandomString from 'crypto-random-string';
// NOTE: `crypto` module is not available in RN so this can't be in client
export const generatePassword = (length: number) =>
cryptoRandomString({ length, type: 'ascii-printable' });

View file

@ -21,7 +21,7 @@
"codegen": "cargo test -p sd-core api::tests::test_and_export_rspc_bindings -- --exact",
"typecheck": "turbo run typecheck",
"lint": "turbo run lint",
"lint:fix": "turbo run lint -- lint:fix",
"lint:fix": "turbo run lint -- --fix",
"clean": "rimraf node_modules/ **/node_modules/ target/ **/.build/ **/.next/ **/dist/**"
},
"pnpm": {

View file

@ -18,7 +18,9 @@
"@rspc/react": "^0.0.0-main-7c0a67c1",
"@sd/config": "workspace:*",
"@tanstack/react-query": "^4.12.0",
"crypto-random-string": "^5.0.0",
"@zxcvbn-ts/core": "^2.1.0",
"@zxcvbn-ts/language-common": "^2.0.1",
"@zxcvbn-ts/language-en": "^2.1.0",
"plausible-tracker": "^0.3.8",
"valtio": "^1.7.4"
},

Some files were not shown because too many files have changed in this diff Show more