From 40f9a1519634a6033aac14f8a40416cc3fe4ae54 Mon Sep 17 00:00:00 2001 From: RAZINJ <37253974+razinj@users.noreply.github.com> Date: Thu, 20 Apr 2023 21:49:09 +0000 Subject: [PATCH] App-wide improvements and refactoring (#19) --- .ruby-version | 1 - Gemfile | 5 +- android/app/build.gradle | 6 +- .../razinj/context_launcher/AppDetails.java | 36 + .../razinj/context_launcher/AppsModule.java | 182 +- .../razinj/context_launcher/Constants.java | 17 + .../context_launcher/MainApplication.java | 7 + .../PackageChangeReceiver.java | 12 +- .../com/razinj/context_launcher/Utils.java | 51 +- android/build.gradle | 2 +- babel.config.js | 2 +- jest-setup.js | 12 - package-lock.json | 2178 +++++++++-------- package.json | 13 +- src/App.tsx | 29 +- src/Home.tsx | 36 +- src/components/AllApps.tsx | 56 +- src/components/AllAppsIcon.test.tsx | 26 +- src/components/AllAppsIcon.tsx | 34 +- src/components/AllAppsLetterIndex.test.tsx | 24 +- src/components/AllAppsLetterIndex.tsx | 22 +- src/components/AppItem.tsx | 119 +- src/components/AppItemMenu.tsx | 357 ++- src/components/CustomView.tsx | 16 - src/components/FavoriteApps.tsx | 63 +- src/components/FilteredApps.tsx | 70 +- src/components/HighlightText.test.tsx | 38 +- src/components/HighlightText.tsx | 18 +- src/components/PinnedApps.tsx | 81 +- src/components/RecentApps.tsx | 84 +- src/components/Search.test.tsx | 91 +- src/components/Search.tsx | 66 +- src/components/Settings/Settings.test.tsx | 36 + .../{SettingsBottomSheet.tsx => Settings.tsx} | 58 +- .../Settings/SettingsBottomSheet.test.tsx | 36 - src/components/Settings/SettingsHeader.tsx | 33 +- src/components/Settings/SettingsIcon.test.tsx | 25 +- src/components/Settings/SettingsIcon.tsx | 34 +- .../__snapshots__/Settings.test.tsx.snap | 626 +++++ .../SettingsBottomSheet.test.tsx.snap | 490 ---- .../__snapshots__/SettingsIcon.test.tsx.snap | 215 +- .../sections/AdvancedSettings.test.tsx | 55 +- .../Settings/sections/AdvancedSettings.tsx | 26 +- .../sections/FavoriteAppsSettings.test.tsx | 182 +- .../sections/FavoriteAppsSettings.tsx | 29 +- .../Settings/sections/PinnedAppsSettings.tsx | 109 +- .../sections/RecentAppsSettings.test.tsx | 56 +- .../Settings/sections/RecentAppsSettings.tsx | 18 +- .../AdvancedSettings.test.tsx.snap | 216 +- .../FavoriteAppsSettings.test.tsx.snap | 312 +-- .../RecentAppsSettings.test.tsx.snap | 186 +- .../shared/SettingsItemLabel.test.tsx | 5 +- .../Settings/shared/SettingsItemLabel.tsx | 15 +- .../Settings/shared/ToggleSettings.tsx | 14 +- .../SettingsItemLabel.test.tsx.snap | 106 +- src/components/Settings/shared/values.ts | 2 - src/components/SortableFavoriteApps.tsx | 78 +- src/components/SortablePinnedApps.tsx | 136 + .../SortableTemporaryPinnedApps.tsx | 136 + src/components/TemporaryPinnedApps.tsx | 81 +- .../__snapshots__/AllAppsIcon.test.tsx.snap | 215 +- .../AllAppsLetterIndex.test.tsx.snap | 298 ++- .../__snapshots__/HighlightText.test.tsx.snap | 102 +- .../__snapshots__/Search.test.tsx.snap | 308 ++- src/components/shared/AppIcon.tsx | 13 + src/components/shared/CustomIcon.tsx | 11 +- src/components/shared/EmptyListComponent.tsx | 17 + src/constants/app.ts | 19 +- src/containers/BottomContainer.tsx | 15 +- src/containers/TopContainer.tsx | 54 +- src/contexts/GlobalContext.ts | 8 - src/contexts/GlobalContextWrapper.tsx | 77 - src/contexts/SearchContext.ts | 12 +- src/contexts/SearchContextWrapper.tsx | 46 +- src/hooks/useBackHandler.ts | 7 +- src/hooks/useKeyboard.ts | 24 - src/hooks/usePackageChange.ts | 4 - src/hooks/useTimeBasedRendering.ts | 23 +- src/models/app-details.ts | 7 +- src/models/context.ts | 39 - src/models/favorite-app.ts | 5 +- src/models/global-state.ts | 14 - src/models/native-module.ts | 14 - src/models/pinned-app.ts | 9 +- src/models/props.ts | 57 - src/models/recent-app.ts | 7 +- src/native-modules/AppsModule.ts | 17 +- src/rootSaga.ts | 7 + src/shared/bottom-container/index.ts | 26 +- src/shared/styles.ts | 25 +- src/slices/appState.ts | 107 + src/slices/appStateHandler.ts | 80 + src/slices/appsList.ts | 20 +- src/slices/appsListHandler.ts | 35 + src/slices/appsSearch.ts | 41 - src/slices/favoriteApps.ts | 13 +- src/slices/pinnedApps.ts | 71 +- src/slices/preferences.ts | 7 +- src/slices/recentApps.ts | 18 +- src/store.ts | 76 +- src/utils/alphabet-list.test.ts | 48 +- src/utils/alphabet-list.ts | 5 +- src/utils/apps-module.test.ts | 2 +- src/utils/apps-module.ts | 1 - src/utils/apps.ts | 24 +- src/utils/date.ts | 4 +- src/utils/keyboard.ts | 1 - src/utils/string.test.ts | 2 +- src/utils/toast.ts | 1 - utils/test/data.ts | 45 +- utils/test/utils.tsx | 38 +- 111 files changed, 5136 insertions(+), 4052 deletions(-) delete mode 100644 .ruby-version create mode 100644 android/app/src/main/java/com/razinj/context_launcher/AppDetails.java create mode 100644 android/app/src/main/java/com/razinj/context_launcher/Constants.java delete mode 100644 src/components/CustomView.tsx create mode 100644 src/components/Settings/Settings.test.tsx rename src/components/Settings/{SettingsBottomSheet.tsx => Settings.tsx} (51%) delete mode 100644 src/components/Settings/SettingsBottomSheet.test.tsx create mode 100644 src/components/Settings/__snapshots__/Settings.test.tsx.snap delete mode 100644 src/components/Settings/__snapshots__/SettingsBottomSheet.test.tsx.snap create mode 100644 src/components/SortablePinnedApps.tsx create mode 100644 src/components/SortableTemporaryPinnedApps.tsx create mode 100644 src/components/shared/AppIcon.tsx create mode 100644 src/components/shared/EmptyListComponent.tsx delete mode 100644 src/contexts/GlobalContext.ts delete mode 100644 src/contexts/GlobalContextWrapper.tsx delete mode 100644 src/hooks/useKeyboard.ts delete mode 100644 src/models/context.ts delete mode 100644 src/models/global-state.ts delete mode 100644 src/models/native-module.ts delete mode 100644 src/models/props.ts create mode 100644 src/rootSaga.ts create mode 100644 src/slices/appState.ts create mode 100644 src/slices/appStateHandler.ts create mode 100644 src/slices/appsListHandler.ts delete mode 100644 src/slices/appsSearch.ts diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 49cdd66..0000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.7.6 diff --git a/Gemfile b/Gemfile index 7c5d99b..1142b1b 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,6 @@ source 'https://rubygems.org' # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version -ruby File.read(File.join(__dir__, '.ruby-version')).strip -gem 'cocoapods', '~> 1.11', '>= 1.11.3' +ruby '>= 2.6.10' + +gem 'cocoapods', '>= 1.11.3' diff --git a/android/app/build.gradle b/android/app/build.gradle index 312e411..72b766e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -95,8 +95,8 @@ android { applicationId "com.razinj.context_launcher" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 13 - versionName "1.5.0" + versionCode 14 + versionName "2.0.0" archivesBaseName = "context-launcher-v$versionName-$versionCode" } @@ -160,7 +160,7 @@ android { dependencies { // The version of react-native is set by the React Native Gradle Plugin implementation("com.facebook.react:react-android") - implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0") + implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { exclude group: 'com.squareup.okhttp3', module: 'okhttp' diff --git a/android/app/src/main/java/com/razinj/context_launcher/AppDetails.java b/android/app/src/main/java/com/razinj/context_launcher/AppDetails.java new file mode 100644 index 0000000..66ce9f8 --- /dev/null +++ b/android/app/src/main/java/com/razinj/context_launcher/AppDetails.java @@ -0,0 +1,36 @@ +package com.razinj.context_launcher; + +import android.util.Log; + +import androidx.annotation.NonNull; + +import org.json.JSONException; +import org.json.JSONObject; + +public class AppDetails { + String packageName; + String name; + String icon; + + AppDetails(String packageName, String name, String icon) { + this.packageName = packageName; + this.name = name; + this.icon = icon; + } + + @NonNull + public String toString() { + try { + JSONObject appDetails = new JSONObject(); + + appDetails.put("packageName", this.packageName); + appDetails.put("name", this.name); + appDetails.put("icon", this.icon); + + return appDetails.toString(); + } catch (JSONException e) { + Log.e("AppsModule", "Couldn't construct app details JSON: " + e.getMessage()); + throw new RuntimeException(e); + } + } +} diff --git a/android/app/src/main/java/com/razinj/context_launcher/AppsModule.java b/android/app/src/main/java/com/razinj/context_launcher/AppsModule.java index 3a0f85d..5bd26c0 100644 --- a/android/app/src/main/java/com/razinj/context_launcher/AppsModule.java +++ b/android/app/src/main/java/com/razinj/context_launcher/AppsModule.java @@ -1,18 +1,27 @@ package com.razinj.context_launcher; +import static com.razinj.context_launcher.Constants.PACKAGE_CHANGE_EVENT; +import static com.razinj.context_launcher.Constants.PACKAGE_CHANGE_IS_REMOVED; +import static com.razinj.context_launcher.Constants.PACKAGE_CHANGE_NAME; +import static com.razinj.context_launcher.Constants.PACKAGE_UPDATE_ACTION; +import static com.razinj.context_launcher.Constants.SHORT_NOT_AVAILABLE; +import static com.razinj.context_launcher.Utils.getPackageInfo; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.provider.Settings; import androidx.annotation.NonNull; import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; @@ -23,168 +32,131 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; public class AppsModule extends ReactContextBaseJavaModule { private final ReactApplicationContext reactContext; - - private BroadcastReceiver packageChangeBroadcastReceiver; - private static DeviceEventManagerModule.RCTDeviceEventEmitter rctDeviceEventEmitter; - - // TODO: Can these values be in a separate file? - // Package change intent action - public static String PACKAGE_UPDATE_ACTION = "packageUpdateAction"; - // Package change event - public static String PACKAGE_CHANGE_EVENT = "packageChange"; - public static String PACKAGE_CHANGE_NAME = "packageName"; - public static String PACKAGE_CHANGE_IS_REMOVED = "isRemoved"; + private static BroadcastReceiver packageChangeBroadcastReceiver; AppsModule(ReactApplicationContext reactContext) { super(reactContext); this.reactContext = reactContext; - initializePackageChangeBroadcastReceiver(reactContext); + initializePackageChangeBroadcastReceiver(); } - private void initializePackageChangeBroadcastReceiver(ReactApplicationContext reactContext) { - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(PACKAGE_UPDATE_ACTION); + @NonNull + @Override + public String getName() { + return "AppsModule"; + } + @Override + public void onCatalystInstanceDestroy() { + reactContext.unregisterReceiver(packageChangeBroadcastReceiver); + } + + private void initializePackageChangeBroadcastReceiver() { packageChangeBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (rctDeviceEventEmitter == null) { - rctDeviceEventEmitter = reactContext.getJSModule((DeviceEventManagerModule.RCTDeviceEventEmitter.class)); - } + if (!reactContext.hasActiveReactInstance()) return; Bundle extras = intent.getExtras(); - sendPackageChangeEvent((String) extras.get(PACKAGE_CHANGE_NAME), (Boolean) extras.get(PACKAGE_CHANGE_IS_REMOVED)); + WritableMap map = Arguments.createMap(); + + map.putString(PACKAGE_CHANGE_NAME, extras.getString(PACKAGE_CHANGE_NAME)); + map.putBoolean(PACKAGE_CHANGE_IS_REMOVED, extras.getBoolean(PACKAGE_CHANGE_IS_REMOVED)); + + reactContext.getJSModule((DeviceEventManagerModule.RCTDeviceEventEmitter.class)).emit(PACKAGE_CHANGE_EVENT, map); } }; - this.reactContext.registerReceiver(packageChangeBroadcastReceiver, intentFilter); - } + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(PACKAGE_UPDATE_ACTION); - @Override - public void onCatalystInstanceDestroy() { - this.reactContext.unregisterReceiver(packageChangeBroadcastReceiver); + reactContext.registerReceiver(packageChangeBroadcastReceiver, intentFilter); } - @NonNull - @Override - public String getName() { - return "AppsModule"; - } - - private static class AppDetails { - CharSequence name; - CharSequence label; + @ReactMethod + public void getApplications(Promise promise) { + PackageManager pm = reactContext.getPackageManager(); + List apps = new ArrayList<>(); + List packages; - AppDetails(CharSequence name, CharSequence label) { - this.name = name; - this.label = label; + // Get installed packages + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + packages = pm.getInstalledPackages(PackageManager.PackageInfoFlags.of(0)); + } else { + packages = pm.getInstalledPackages(0); } - @NonNull - public String toString() { - return "{\"label\":\"" + this.label + "\",\"name\":\"" + this.name + "\"}"; - } - } + // Filter and map to AppDetails + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + apps = packages.stream().filter(packageInfo -> Objects.nonNull(pm.getLaunchIntentForPackage(packageInfo.packageName))).map(packageInfo -> new AppDetails(packageInfo.packageName, packageInfo.applicationInfo.loadLabel(pm).toString(), Utils.getEncodedIcon(pm, packageInfo.packageName))).collect(Collectors.toList()); + } else { + for (PackageInfo packageInfo : packages) { + if (Objects.isNull(pm.getLaunchIntentForPackage(packageInfo.packageName))) continue; - @ReactMethod - public void getApplications(Callback callBack) { - List apps = new ArrayList<>(); - List packages = this.reactContext - .getPackageManager() - .getInstalledPackages(0); - - for (final PackageInfo packageInfo : packages) { - // TODO: Output a warning when package intent is null - if (getPackageLaunchIntent(packageInfo.packageName) == null) continue; - - apps.add(new AppDetails( - packageInfo.packageName, - packageInfo.applicationInfo.loadLabel(this.reactContext.getPackageManager()) - )); + apps.add(new AppDetails(packageInfo.packageName, packageInfo.applicationInfo.loadLabel(pm).toString(), Utils.getEncodedIcon(pm, packageInfo.packageName))); + } } - callBack.invoke(apps.toString()); + promise.resolve(apps.toString()); } @ReactMethod private void launchApplication(String packageName) { - Intent intent = getPackageLaunchIntent(packageName); + Intent intent = reactContext.getPackageManager().getLaunchIntentForPackage(packageName); - // TODO: Output error message and/or return an error to RN - if (intent == null) return; - - this.reactContext.startActivity(intent); + reactContext.startActivity(intent); } @ReactMethod public void showApplicationDetails(String packageName) { - startActivity(packageName, new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)); - } + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).setData(Uri.fromParts("package", packageName, null)); - @ReactMethod - public void requestApplicationUninstall(String packageName) { - startActivity(packageName, new Intent(Intent.ACTION_DELETE)); + reactContext.startActivity(intent); } @ReactMethod - public void getApplicationIcon(String packageName, Callback callBack) { - callBack.invoke(Utils.getEncodedIcon(this.reactContext, packageName)); - } - - public static void sendPackageChangeEvent(String packageName, Boolean isRemoved) { - WritableMap map = Arguments.createMap(); - map.putString(PACKAGE_CHANGE_NAME, packageName); - map.putBoolean(PACKAGE_CHANGE_IS_REMOVED, isRemoved); - - rctDeviceEventEmitter.emit(PACKAGE_CHANGE_EVENT, map); - } - - private void startActivity(String packageName, Intent intent) { - // TODO: Check if the flag can be removed or not (read more about the behavior) - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setData(Uri.fromParts("package", packageName, null)); - - this.reactContext.startActivity(intent); - } + public void requestApplicationUninstall(String packageName) { + Intent intent = new Intent(Intent.ACTION_DELETE).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).setData(Uri.fromParts("package", packageName, null)); - private Intent getPackageLaunchIntent(String packageName) { - return this.reactContext.getPackageManager().getLaunchIntentForPackage(packageName); + reactContext.startActivity(intent); } @ReactMethod public void addListener(String eventName) { - // TODO: Should this method exist? - // Set up any upstream listeners or background tasks as necessary + // Required for NativeEventEmitter } @ReactMethod public void removeListeners(Integer count) { - // TODO: Should this method exist? - // Remove upstream listeners, stop unnecessary background tasks - } - - private PackageInfo getPackageInfo() throws Exception { - return getReactApplicationContext().getPackageManager().getPackageInfo(getReactApplicationContext().getPackageName(), 0); + // Required for NativeEventEmitter } @Override public Map getConstants() { - String appVersion, buildNumber, packageName; + String appVersion; + String buildNumber; + String packageName; try { - appVersion = getPackageInfo().versionName; - buildNumber = Integer.toString(getPackageInfo().versionCode); + appVersion = getPackageInfo(reactContext).versionName; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + buildNumber = Long.toString(getPackageInfo(reactContext).getLongVersionCode()); + } else { + buildNumber = Long.toString(getPackageInfo(reactContext).versionCode); + } packageName = getReactApplicationContext().getPackageName(); - } catch (Exception e) { - appVersion = "unknown"; - buildNumber = "unknown"; - packageName = "unknown"; + } catch (PackageManager.NameNotFoundException e) { + appVersion = SHORT_NOT_AVAILABLE; + buildNumber = SHORT_NOT_AVAILABLE; + packageName = SHORT_NOT_AVAILABLE; } - final Map constants = new HashMap<>(); + Map constants = new HashMap<>(); constants.put("appVersion", appVersion); constants.put("buildNumber", buildNumber); diff --git a/android/app/src/main/java/com/razinj/context_launcher/Constants.java b/android/app/src/main/java/com/razinj/context_launcher/Constants.java new file mode 100644 index 0000000..36a3dea --- /dev/null +++ b/android/app/src/main/java/com/razinj/context_launcher/Constants.java @@ -0,0 +1,17 @@ +package com.razinj.context_launcher; + +public class Constants { + + private Constants() { + super(); + } + + // Package change intent action + public static final String PACKAGE_UPDATE_ACTION = "packageUpdateAction"; + // Package change event + public static final String PACKAGE_CHANGE_EVENT = "packageChange"; + public static final String PACKAGE_CHANGE_NAME = "packageName"; + public static final String PACKAGE_CHANGE_IS_REMOVED = "isRemoved"; + // Misc + public static final String SHORT_NOT_AVAILABLE = "N/A"; +} diff --git a/android/app/src/main/java/com/razinj/context_launcher/MainApplication.java b/android/app/src/main/java/com/razinj/context_launcher/MainApplication.java index dd8cc23..30cefa0 100644 --- a/android/app/src/main/java/com/razinj/context_launcher/MainApplication.java +++ b/android/app/src/main/java/com/razinj/context_launcher/MainApplication.java @@ -4,6 +4,8 @@ import android.content.Intent; import android.os.Build; +import androidx.annotation.NonNull; + import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; @@ -12,6 +14,8 @@ import com.facebook.react.defaults.DefaultReactNativeHost; import com.facebook.soloader.SoLoader; +import org.jetbrains.annotations.Contract; + import java.util.List; public class MainApplication extends Application implements ReactApplication { @@ -22,6 +26,7 @@ public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } + @NonNull @Override protected List getPackages() { List packages = new PackageList(this).getPackages(); @@ -29,6 +34,8 @@ protected List getPackages() { return packages; } + @NonNull + @Contract(pure = true) @Override protected String getJSMainModuleName() { return "index"; diff --git a/android/app/src/main/java/com/razinj/context_launcher/PackageChangeReceiver.java b/android/app/src/main/java/com/razinj/context_launcher/PackageChangeReceiver.java index 6869f94..6e61cd1 100644 --- a/android/app/src/main/java/com/razinj/context_launcher/PackageChangeReceiver.java +++ b/android/app/src/main/java/com/razinj/context_launcher/PackageChangeReceiver.java @@ -1,17 +1,19 @@ package com.razinj.context_launcher; -import static com.razinj.context_launcher.AppsModule.PACKAGE_CHANGE_IS_REMOVED; -import static com.razinj.context_launcher.AppsModule.PACKAGE_CHANGE_NAME; -import static com.razinj.context_launcher.AppsModule.PACKAGE_UPDATE_ACTION; +import static com.razinj.context_launcher.Constants.PACKAGE_CHANGE_IS_REMOVED; +import static com.razinj.context_launcher.Constants.PACKAGE_CHANGE_NAME; +import static com.razinj.context_launcher.Constants.PACKAGE_UPDATE_ACTION; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import androidx.annotation.NonNull; + public class PackageChangeReceiver extends BroadcastReceiver { @Override - public void onReceive(Context context, Intent intent) { + public void onReceive(@NonNull Context context, @NonNull Intent intent) { String packageName = intent.getData().getSchemeSpecificPart(); if (packageName.equalsIgnoreCase(context.getPackageName())) return; @@ -19,7 +21,7 @@ public void onReceive(Context context, Intent intent) { handleEvent(context, intent.getAction(), packageName, intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)); } - public static void handleEvent(Context context, String action, String packageName, boolean replacing) { + public static void handleEvent(Context context, @NonNull String action, String packageName, boolean replacing) { if (!action.equals(Intent.ACTION_PACKAGE_ADDED) && !action.equals(Intent.ACTION_PACKAGE_CHANGED) && !action.equals(Intent.ACTION_PACKAGE_REMOVED)) { return; } diff --git a/android/app/src/main/java/com/razinj/context_launcher/Utils.java b/android/app/src/main/java/com/razinj/context_launcher/Utils.java index 08563c9..bff0bcc 100644 --- a/android/app/src/main/java/com/razinj/context_launcher/Utils.java +++ b/android/app/src/main/java/com/razinj/context_launcher/Utils.java @@ -1,43 +1,64 @@ package com.razinj.context_launcher; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.os.Build; import android.util.Base64; +import androidx.annotation.NonNull; + import com.facebook.react.bridge.ReactApplicationContext; import java.io.ByteArrayOutputStream; public class Utils { - public static String getEncodedIcon(ReactApplicationContext context, String packageName) { + private Utils() { + super(); + } + + public static String getEncodedIcon(@NonNull PackageManager pm, String packageName) { try { - return getEncodedIcon(context.getPackageManager().getApplicationIcon(packageName)); + return getEncodedIcon(pm.getApplicationIcon(packageName)); } catch (PackageManager.NameNotFoundException nameNotFoundException) { - return ""; + return "NOT_FOUND"; } } - public static String getEncodedIcon(Drawable drawableIcon) { - Bitmap icon; + public static String getEncodedIcon(@NonNull Drawable drawable) { + Bitmap bitmap; - if (drawableIcon.getIntrinsicWidth() <= 0 || drawableIcon.getIntrinsicHeight() <= 0) { - // Single color bitmap will be created of 1x1 pixel - icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - } else { - icon = Bitmap.createBitmap(drawableIcon.getIntrinsicWidth(), drawableIcon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - } + // Single color bitmap will be created of 1x1 pixel + bitmap = Bitmap.createBitmap( + drawable.getIntrinsicWidth() > 0 ? drawable.getIntrinsicWidth() : 1, + drawable.getIntrinsicHeight() > 0 ? drawable.getIntrinsicHeight() : 1, + Bitmap.Config.ARGB_8888 + ); - final Canvas canvas = new Canvas(icon); + final Canvas canvas = new Canvas(bitmap); - drawableIcon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawableIcon.draw(canvas); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - icon.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + bitmap.compress(Bitmap.CompressFormat.WEBP_LOSSY, 50, byteArrayOutputStream); + } else { + bitmap.compress(Bitmap.CompressFormat.WEBP, 50, byteArrayOutputStream); + } return Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.NO_WRAP); } + + public static PackageInfo getPackageInfo(ReactApplicationContext reactContext) throws PackageManager.NameNotFoundException { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + return reactContext.getPackageManager().getPackageInfo(reactContext.getPackageName(), PackageManager.PackageInfoFlags.of(0)); + } else { + return reactContext.getPackageManager().getPackageInfo(reactContext.getPackageName(), 0); + } + } } diff --git a/android/build.gradle b/android/build.gradle index 71fa552..2659897 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -14,7 +14,7 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:7.3.1") + classpath("com.android.tools.build:gradle:7.4.2") classpath("com.facebook.react:react-native-gradle-plugin") } } diff --git a/babel.config.js b/babel.config.js index e8505de..9c58494 100644 --- a/babel.config.js +++ b/babel.config.js @@ -7,7 +7,7 @@ module.exports = { ], env: { production: { - plugins: ['transform-remove-console'], + plugins: ['transform-remove-console', 'react-native-paper/babel'], }, }, plugins: ['react-native-reanimated/plugin'], diff --git a/jest-setup.js b/jest-setup.js index 4f93b34..ee1c98d 100644 --- a/jest-setup.js +++ b/jest-setup.js @@ -29,15 +29,3 @@ jest.mock('react-native', () => { return ReactNative }) - -// src: https://github.com/gorhom/react-native-bottom-sheet/issues/326#issuecomment-1117504554 -jest.mock('@gorhom/bottom-sheet', () => { - const { View } = jest.requireActual('react-native') - - return { - __esModule: true, - default: View, - BottomSheetModal: View, - BottomSheetModalProvider: View, - } -}) diff --git a/package-lock.json b/package-lock.json index bfdec5b..86ab0c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,18 +8,20 @@ "name": "context_launcher", "version": "1.5.0", "dependencies": { - "@gorhom/bottom-sheet": "^4.4.5", "@react-native-async-storage/async-storage": "^1.17.11", "@react-native-community/datetimepicker": "^6.7.5", "@reduxjs/toolkit": "^1.9.1", "react": "18.2.0", - "react-native": "0.71.4", + "react-native": "0.71.6", "react-native-draggable-flatlist": "^4.0.0", "react-native-gesture-handler": "^2.9.0", - "react-native-reanimated": "^2.14.4", + "react-native-paper": "^5.4.1", + "react-native-reanimated": "^3.0.2", + "react-native-safe-area-context": "^4.5.0", "react-native-vector-icons": "^9.2.0", "react-redux": "^8.0.5", - "redux-persist": "^6.0.0" + "redux-persist": "^6.0.0", + "redux-saga": "^1.2.3" }, "devDependencies": { "@babel/core": "^7.20.12", @@ -42,18 +44,19 @@ "eslint-config-prettier": "^8.5.0", "eslint-plugin-react-native": "^4.0.0", "jest": "^29.2.1", - "metro-react-native-babel-preset": "0.73.8", + "metro-react-native-babel-preset": "0.73.9", "prettier": "^2.4.1", + "prettier-plugin-organize-imports": "^3.2.2", "ts-jest": "^29.0.5", "typescript": "4.9.4" } }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -61,9 +64,9 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", "dependencies": { "@babel/highlight": "^7.18.6" }, @@ -72,28 +75,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", + "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", - "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", + "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.0", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.0", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", + "@babel/helper-compilation-targets": "^7.21.4", + "@babel/helper-module-transforms": "^7.21.2", "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.0", + "@babel/parser": "^7.21.4", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0", + "@babel/traverse": "^7.21.4", + "@babel/types": "^7.21.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -109,11 +112,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", - "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", + "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", "dependencies": { - "@babel/types": "^7.21.0", + "@babel/types": "^7.21.4", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -122,19 +125,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", @@ -159,12 +149,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", + "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", + "@babel/compat-data": "^7.21.4", + "@babel/helper-validator-option": "^7.21.0", "browserslist": "^4.21.3", "lru-cache": "^5.1.1", "semver": "^6.3.0" @@ -177,9 +167,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", - "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz", + "integrity": "sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", @@ -198,9 +188,9 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz", - "integrity": "sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz", + "integrity": "sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "regexpu-core": "^5.3.1" @@ -282,11 +272,11 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.21.4" }, "engines": { "node": ">=6.9.0" @@ -460,9 +450,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", - "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", + "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -834,11 +824,11 @@ } }, "node_modules/@babel/plugin-syntax-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", - "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz", + "integrity": "sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -885,11 +875,11 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -993,11 +983,11 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1102,9 +1092,9 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", - "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", + "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2" }, @@ -1354,9 +1344,9 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", - "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz", + "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2" }, @@ -1471,11 +1461,11 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz", - "integrity": "sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz", + "integrity": "sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA==", "dependencies": { - "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-module-imports": "^7.21.4", "@babel/helper-plugin-utils": "^7.20.2", "babel-plugin-polyfill-corejs2": "^0.3.3", "babel-plugin-polyfill-corejs3": "^0.6.0", @@ -1561,10 +1551,11 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz", - "integrity": "sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg==", + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", + "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-create-class-features-plugin": "^7.21.0", "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-typescript": "^7.20.0" @@ -1606,30 +1597,30 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.4.tgz", + "integrity": "sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==", "dependencies": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", + "@babel/compat-data": "^7.21.4", + "@babel/helper-compilation-targets": "^7.21.4", "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", + "@babel/helper-validator-option": "^7.21.0", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7", + "@babel/plugin-proposal-async-generator-functions": "^7.20.7", "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.21.0", "@babel/plugin-proposal-dynamic-import": "^7.18.6", "@babel/plugin-proposal-export-namespace-from": "^7.18.9", "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", + "@babel/plugin-proposal-object-rest-spread": "^7.20.7", "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.21.0", "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.21.0", "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1646,40 +1637,40 @@ "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.20.7", + "@babel/plugin-transform-async-to-generator": "^7.20.7", "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", + "@babel/plugin-transform-block-scoping": "^7.21.0", + "@babel/plugin-transform-classes": "^7.21.0", + "@babel/plugin-transform-computed-properties": "^7.20.7", + "@babel/plugin-transform-destructuring": "^7.21.3", "@babel/plugin-transform-dotall-regex": "^7.18.6", "@babel/plugin-transform-duplicate-keys": "^7.18.9", "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-for-of": "^7.21.0", "@babel/plugin-transform-function-name": "^7.18.9", "@babel/plugin-transform-literals": "^7.18.9", "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", + "@babel/plugin-transform-modules-amd": "^7.20.11", + "@babel/plugin-transform-modules-commonjs": "^7.21.2", + "@babel/plugin-transform-modules-systemjs": "^7.20.11", "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5", "@babel/plugin-transform-new-target": "^7.18.6", "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", + "@babel/plugin-transform-parameters": "^7.21.3", "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.20.5", "@babel/plugin-transform-reserved-words": "^7.18.6", "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-spread": "^7.20.7", "@babel/plugin-transform-sticky-regex": "^7.18.6", "@babel/plugin-transform-template-literals": "^7.18.9", "@babel/plugin-transform-typeof-symbol": "^7.18.9", "@babel/plugin-transform-unicode-escapes": "^7.18.10", "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", + "@babel/types": "^7.21.4", "babel-plugin-polyfill-corejs2": "^0.3.3", "babel-plugin-polyfill-corejs3": "^0.6.0", "babel-plugin-polyfill-regenerator": "^0.4.1", @@ -1694,13 +1685,13 @@ } }, "node_modules/@babel/preset-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.18.6.tgz", - "integrity": "sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.21.4.tgz", + "integrity": "sha512-F24cSq4DIBmhq4OzK3dE63NHagb27OPE3eWR+HLekt4Z3Y5MzIIUGF3LlLgV0gN8vzbDViSY7HnrReNVCJXTeA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-flow-strip-types": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-transform-flow-strip-types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -1725,13 +1716,15 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz", - "integrity": "sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz", + "integrity": "sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-transform-typescript": "^7.21.0" + "@babel/plugin-syntax-jsx": "^7.21.4", + "@babel/plugin-transform-modules-commonjs": "^7.21.2", + "@babel/plugin-transform-typescript": "^7.21.3" }, "engines": { "node": ">=6.9.0" @@ -1817,18 +1810,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", - "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", + "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.1", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.2", - "@babel/types": "^7.21.2", + "@babel/parser": "^7.21.4", + "@babel/types": "^7.21.4", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1837,9 +1830,9 @@ } }, "node_modules/@babel/types": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", - "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", + "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", "dependencies": { "@babel/helper-string-parser": "^7.19.4", "@babel/helper-validator-identifier": "^7.19.1", @@ -1855,6 +1848,26 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@callstack/react-theme-provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@callstack/react-theme-provider/-/react-theme-provider-3.0.8.tgz", + "integrity": "sha512-5U231sYY2sqQOaELX0WBCn+iluV8bFaXIS7em03k4W5Xz0AhGvKlnpLIhDGFP8im/SvNW7/2XoR0BsClhn9t6Q==", + "dependencies": { + "deepmerge": "^3.2.0", + "hoist-non-react-statics": "^3.3.0" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, + "node_modules/@callstack/react-theme-provider/node_modules/deepmerge": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", + "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@egjs/hammerjs": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", @@ -1867,9 +1880,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz", - "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { "eslint-visitor-keys": "^3.3.0" @@ -1882,9 +1895,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", - "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", + "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -1946,33 +1959,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@gorhom/bottom-sheet": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@gorhom/bottom-sheet/-/bottom-sheet-4.4.5.tgz", - "integrity": "sha512-Z5Z20wshLUB8lIdtMKoJaRnjd64wBR/q8EeVPThrg+skrcBwBPHfUwZJ2srB0rEszA/01ejSJy/ixyd7Ra7vUA==", - "dependencies": { - "@gorhom/portal": "1.0.14", - "invariant": "^2.2.4" - }, - "peerDependencies": { - "react": "*", - "react-native": "*", - "react-native-gesture-handler": ">=1.10.1", - "react-native-reanimated": ">=2.2.0" - } - }, - "node_modules/@gorhom/portal": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@gorhom/portal/-/portal-1.0.14.tgz", - "integrity": "sha512-MXyL4xvCjmgaORr/rtryDNFy3kU4qUbKlwtQqqsygd0xX3mhKjOLn6mQK8wfu0RkoE0pBE0nAasRoHua+/QZ7A==", - "dependencies": { - "nanoid": "^3.3.1" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -2667,12 +2653,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -2695,41 +2682,33 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2766,27 +2745,27 @@ } }, "node_modules/@react-native-async-storage/async-storage": { - "version": "1.17.11", - "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.17.11.tgz", - "integrity": "sha512-bzs45n5HNcDq6mxXnSsOHysZWn1SbbebNxldBXCQs8dSvF8Aor9KCdpm+TpnnGweK3R6diqsT8lFhX77VX0NFw==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.18.1.tgz", + "integrity": "sha512-70aFW8fVCKl+oA1AKPFDpE6s4t9pulj2QeLX+MabEmzfT3urd/3cckv45WJvtocdoIH/oXA3Y+YcCRJCcNa8mA==", "dependencies": { "merge-options": "^3.0.4" }, "peerDependencies": { - "react-native": "^0.0.0-0 || 0.60 - 0.71 || 1000.0.0" + "react-native": "^0.0.0-0 || 0.60 - 0.72 || 1000.0.0" } }, "node_modules/@react-native-community/cli": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-10.2.0.tgz", - "integrity": "sha512-QH7AFBz5FX2zTZRH/o3XehHrZ0aZZEL5Sh+23nSEFgSj3bLFfvjjZhuoiRSAo7iiBdvAoXrfxQ8TXgg4Xf/7fw==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-10.2.2.tgz", + "integrity": "sha512-aZVcVIqj+OG6CrliR/Yn8wHxrvyzbFBY9cj7n0MvRw/P54QUru2nNqUTSSbqv0Qaa297yHJbe6kFDojDMSTM8Q==", "dependencies": { "@react-native-community/cli-clean": "^10.1.1", "@react-native-community/cli-config": "^10.1.1", "@react-native-community/cli-debugger-ui": "^10.0.0", - "@react-native-community/cli-doctor": "^10.2.0", + "@react-native-community/cli-doctor": "^10.2.2", "@react-native-community/cli-hermes": "^10.2.0", - "@react-native-community/cli-plugin-metro": "^10.2.0", + "@react-native-community/cli-plugin-metro": "^10.2.2", "@react-native-community/cli-server-api": "^10.1.1", "@react-native-community/cli-tools": "^10.1.1", "@react-native-community/cli-types": "^10.0.0", @@ -3083,12 +3062,12 @@ } }, "node_modules/@react-native-community/cli-doctor": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-10.2.0.tgz", - "integrity": "sha512-yLxJazUmNSPslHxeeev0gLvsK0nQan8BmGWbtqPz2WwbIbD89vbytC7G96OxiQXr46iWEWAwEJiTTdgA7jlA5Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-10.2.2.tgz", + "integrity": "sha512-49Ep2aQOF0PkbAR/TcyMjOm9XwBa8VQr+/Zzf4SJeYwiYLCT1NZRAVAVjYRXl0xqvq5S5mAGZZShS4AQl4WsZw==", "dependencies": { "@react-native-community/cli-config": "^10.1.1", - "@react-native-community/cli-platform-ios": "^10.2.0", + "@react-native-community/cli-platform-ios": "^10.2.1", "@react-native-community/cli-tools": "^10.1.1", "chalk": "^4.1.2", "command-exists": "^1.2.8", @@ -3557,9 +3536,9 @@ } }, "node_modules/@react-native-community/cli-platform-ios": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.0.tgz", - "integrity": "sha512-hIPK3iL/mL+0ChXmQ9uqqzNOKA48H+TAzg+hrxQLll/6dNMxDeK9/wZpktcsh8w+CyhqzKqVernGcQs7tPeKGw==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.1.tgz", + "integrity": "sha512-hz4zu4Y6eyj7D0lnZx8Mf2c2si8y+zh/zUTgCTaPPLzQD8jSZNNBtUUiA1cARm2razpe8marCZ1QbTMAGbf3mg==", "dependencies": { "@react-native-community/cli-tools": "^10.1.1", "chalk": "^4.1.2", @@ -3742,20 +3721,20 @@ } }, "node_modules/@react-native-community/cli-plugin-metro": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.0.tgz", - "integrity": "sha512-9eiJrKYuauEDkQLCrjJUh7tS9T0oaMQqVUSSSuyDG6du7HQcfaR4mSf21wK75jvhKiwcQLpsFmMdctAb+0v+Cg==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.2.tgz", + "integrity": "sha512-sTGjZlD3OGqbF9v1ajwUIXhGmjw9NyJ/14Lo0sg7xH8Pv4qUd5ZvQ6+DWYrQn3IKFUMfGFWYyL81ovLuPylrpw==", "dependencies": { "@react-native-community/cli-server-api": "^10.1.1", "@react-native-community/cli-tools": "^10.1.1", "chalk": "^4.1.2", "execa": "^1.0.0", - "metro": "0.73.8", - "metro-config": "0.73.8", - "metro-core": "0.73.8", - "metro-react-native-babel-transformer": "0.73.8", - "metro-resolver": "0.73.8", - "metro-runtime": "0.73.8", + "metro": "0.73.9", + "metro-config": "0.73.9", + "metro-core": "0.73.9", + "metro-react-native-babel-transformer": "0.73.9", + "metro-resolver": "0.73.9", + "metro-runtime": "0.73.9", "readline": "^1.3.0" } }, @@ -4573,9 +4552,9 @@ } }, "node_modules/@react-native-community/eslint-config/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4614,6 +4593,57 @@ "resolved": "https://registry.npmjs.org/@react-native/polyfills/-/polyfills-2.0.0.tgz", "integrity": "sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==" }, + "node_modules/@redux-saga/core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.2.3.tgz", + "integrity": "sha512-U1JO6ncFBAklFTwoQ3mjAeQZ6QGutsJzwNBjgVLSWDpZTRhobUzuVDS1qH3SKGJD8fvqoaYOjp6XJ3gCmeZWgA==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@redux-saga/deferred": "^1.2.1", + "@redux-saga/delay-p": "^1.2.1", + "@redux-saga/is": "^1.1.3", + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1", + "redux": "^4.0.4", + "typescript-tuple": "^2.2.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/redux-saga" + } + }, + "node_modules/@redux-saga/deferred": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.2.1.tgz", + "integrity": "sha512-cmin3IuuzMdfQjA0lG4B+jX+9HdTgHZZ+6u3jRAOwGUxy77GSlTi4Qp2d6PM1PUoTmQUR5aijlA39scWWPF31g==" + }, + "node_modules/@redux-saga/delay-p": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.2.1.tgz", + "integrity": "sha512-MdiDxZdvb1m+Y0s4/hgdcAXntpUytr9g0hpcOO1XFVyyzkrDu3SKPgBFOtHn7lhu7n24ZKIAT1qtKyQjHqRd+w==", + "dependencies": { + "@redux-saga/symbols": "^1.1.3" + } + }, + "node_modules/@redux-saga/is": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.1.3.tgz", + "integrity": "sha512-naXrkETG1jLRfVfhOx/ZdLj0EyAzHYbgJWkXbB3qFliPcHKiWbv/ULQryOAEKyjrhiclmr6AMdgsXFyx7/yE6Q==", + "dependencies": { + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1" + } + }, + "node_modules/@redux-saga/symbols": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.1.3.tgz", + "integrity": "sha512-hCx6ZvU4QAEUojETnX8EVg4ubNLBFl1Lps4j2tX7o45x/2qg37m3c6v+kSp8xjDJY+2tJw4QB3j8o8dsl1FDXg==" + }, + "node_modules/@redux-saga/types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", + "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==" + }, "node_modules/@reduxjs/toolkit": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.3.tgz", @@ -4882,9 +4912,9 @@ } }, "node_modules/@types/jest": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.1.tgz", - "integrity": "sha512-zDQSWXG+ZkEvs2zFFMszePhx4euKz+Yt3Gg1P+RHjfJBinTTr6L2DEyovO4V/WrKXuF0Dgn56GWGZPDa6TW9eQ==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -4898,9 +4928,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.15.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.2.tgz", - "integrity": "sha512-sDPHm2wfx2QhrMDK0pOt2J4KLJMAcerqWNvnED0itPRJWvI+bK+uNHzcH1dFsBlf7G3u8tqXmRF3wkvL9yUwMw==" + "version": "18.15.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==" }, "node_modules/@types/prettier": { "version": "2.7.2", @@ -4914,9 +4944,9 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/react": { - "version": "18.0.28", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", - "integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", + "version": "18.0.35", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.35.tgz", + "integrity": "sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4924,9 +4954,9 @@ } }, "node_modules/@types/react-native": { - "version": "0.71.3", - "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.71.3.tgz", - "integrity": "sha512-0Uqw1YZ0qbVla0MMWFTANFm6W8KYWNvGQmYfucdecbXivLMcQ2v4PovuYFKr7bE6Bc5nDCUEaga962Y8gcDF7A==", + "version": "0.71.5", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.71.5.tgz", + "integrity": "sha512-Tp5druh7DGwNDvWYH09PCE++hbH4zYz0OOvGFb3/QFIFKXgfezaT/txJeKlBkbiqs45QJzllp9S0qo0WpWyijA==", "dev": true, "dependencies": { "@types/react": "*" @@ -4943,18 +4973,18 @@ } }, "node_modules/@types/react-native-vector-icons/node_modules/@types/react-native": { - "version": "0.70.11", - "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.11.tgz", - "integrity": "sha512-FobPtzoNPNHugBKMfzs4Li0Q9ei4tgU8SI1M5Ayg7+t5/+noCm2sknI8uwij22wMkcHcefv8RFx4q28nNVJtCQ==", + "version": "0.70.13", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.13.tgz", + "integrity": "sha512-VnC/ny8Eynk3fvY4cnNKXpo/0zUhA2gO64RX51yzVofblOP6TR6jciga0kIjI4c+2eUyWNGrahmiolNm+QU6Cw==", "dev": true, "dependencies": { "@types/react": "*" } }, "node_modules/@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, "node_modules/@types/semver": { "version": "7.3.13", @@ -4973,9 +5003,9 @@ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" }, "node_modules/@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", "dependencies": { "@types/yargs-parser": "*" } @@ -4986,15 +5016,15 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz", - "integrity": "sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.58.0.tgz", + "integrity": "sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/type-utils": "5.55.0", - "@typescript-eslint/utils": "5.55.0", + "@typescript-eslint/scope-manager": "5.58.0", + "@typescript-eslint/type-utils": "5.58.0", + "@typescript-eslint/utils": "5.58.0", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -5032,9 +5062,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5154,9 +5184,9 @@ } }, "node_modules/@typescript-eslint/experimental-utils/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5175,14 +5205,14 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.55.0.tgz", - "integrity": "sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.58.0.tgz", + "integrity": "sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/typescript-estree": "5.55.0", + "@typescript-eslint/scope-manager": "5.58.0", + "@typescript-eslint/types": "5.58.0", + "@typescript-eslint/typescript-estree": "5.58.0", "debug": "^4.3.4" }, "engines": { @@ -5202,13 +5232,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz", - "integrity": "sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.58.0.tgz", + "integrity": "sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/visitor-keys": "5.55.0" + "@typescript-eslint/types": "5.58.0", + "@typescript-eslint/visitor-keys": "5.58.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -5219,13 +5249,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz", - "integrity": "sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.58.0.tgz", + "integrity": "sha512-FF5vP/SKAFJ+LmR9PENql7fQVVgGDOS+dq3j+cKl9iW/9VuZC/8CFmzIP0DLKXfWKpRHawJiG70rVH+xZZbp8w==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.55.0", - "@typescript-eslint/utils": "5.55.0", + "@typescript-eslint/typescript-estree": "5.58.0", + "@typescript-eslint/utils": "5.58.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -5246,9 +5276,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.55.0.tgz", - "integrity": "sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.58.0.tgz", + "integrity": "sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -5259,13 +5289,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz", - "integrity": "sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.58.0.tgz", + "integrity": "sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/visitor-keys": "5.55.0", + "@typescript-eslint/types": "5.58.0", + "@typescript-eslint/visitor-keys": "5.58.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -5298,9 +5328,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5319,17 +5349,17 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.55.0.tgz", - "integrity": "sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.58.0.tgz", + "integrity": "sha512-gAmLOTFXMXOC+zP1fsqm3VceKSBQJNzV385Ok3+yzlavNHZoedajjS4UyS21gabJYcobuigQPs/z71A9MdJFqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/typescript-estree": "5.55.0", + "@typescript-eslint/scope-manager": "5.58.0", + "@typescript-eslint/types": "5.58.0", + "@typescript-eslint/typescript-estree": "5.58.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -5357,9 +5387,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5378,12 +5408,12 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz", - "integrity": "sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.58.0.tgz", + "integrity": "sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.55.0", + "@typescript-eslint/types": "5.58.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -6258,9 +6288,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001465", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001465.tgz", - "integrity": "sha512-HvjgL3MYAJjceTDCcjRnQGjwUz/5qec9n7JPOzUursUoOTIsYCSDOb1l7RsnZE8mjbxG78zVRCKfrBXyvChBag==", + "version": "1.0.30001478", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz", + "integrity": "sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw==", "funding": [ { "type": "opencollective", @@ -6269,6 +6299,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -6416,9 +6450,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.8.0.tgz", + "integrity": "sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ==", "engines": { "node": ">=6" }, @@ -6488,6 +6522,15 @@ "node": ">=0.10.0" } }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -6501,6 +6544,15 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", @@ -6616,9 +6668,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.29.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.1.tgz", - "integrity": "sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==", + "version": "3.30.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.1.tgz", + "integrity": "sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw==", "dependencies": { "browserslist": "^4.21.5" }, @@ -6693,9 +6745,9 @@ } }, "node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/dayjs": { "version": "1.11.7", @@ -6747,9 +6799,9 @@ "dev": true }, "node_modules/deepmerge": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", - "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "engines": { "node": ">=0.10.0" @@ -6874,9 +6926,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.328", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.328.tgz", - "integrity": "sha512-DE9tTy2PNmy1v55AZAO542ui+MLC2cvINMK4P2LXGsJdput/ThVG9t+QGecPuAZZSgC8XoI+Jh9M1OG9IoNSCw==" + "version": "1.4.365", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.365.tgz", + "integrity": "sha512-FRHZO+1tUNO4TOPXmlxetkoaIY8uwHzd1kKopK/Gx2SKn1L47wJXWD44wxP5CGRyyP98z/c8e1eBzJrgPeiBOg==" }, "node_modules/emittery": { "version": "0.13.1", @@ -7129,9 +7181,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz", - "integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -7338,12 +7390,15 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", + "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/@babel/code-frame": { @@ -7471,9 +7526,9 @@ } }, "node_modules/eslint/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -7881,9 +7936,9 @@ "dev": true }, "node_modules/fast-xml-parser": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.3.tgz", - "integrity": "sha512-LsNDahCiCcJPe8NO7HijcnukHB24tKbfDDA5IILx9dmW3Frb52lhbeX6MPNUSvyGNfav2VTYpJ/OqkRoVLrh2Q==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.0.tgz", + "integrity": "sha512-+zVQv4aVTO+o8oRUyRL7PjgeVo1J6oP8Cw2+a8UTZQcj5V0yUK5T63gTN0ldgiHDPghUjKc4OpT6SwMTwnOQug==", "dependencies": { "strnum": "^1.0.5" }, @@ -8380,9 +8435,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/grapheme-splitter": { "version": "1.0.4", @@ -8650,9 +8705,9 @@ } }, "node_modules/immer": { - "version": "9.0.19", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz", - "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==", + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -8836,9 +8891,9 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", + "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", "dependencies": { "has": "^1.0.3" }, @@ -10474,9 +10529,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -10814,9 +10869,9 @@ } }, "node_modules/joi": { - "version": "17.8.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.8.3.tgz", - "integrity": "sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w==", + "version": "17.9.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.1.tgz", + "integrity": "sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw==", "dependencies": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", @@ -11535,9 +11590,9 @@ } }, "node_modules/metro": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.73.8.tgz", - "integrity": "sha512-2EMJME9w5x7Uzn+DnQ4hzWr33u/aASaOBGdpf4lxbrlk6/vl4UBfX1sru6KU535qc/0Z1BMt4Vq9qsP3ZGFmWg==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.73.9.tgz", + "integrity": "sha512-BlYbPmTF60hpetyNdKhdvi57dSqutb+/oK0u3ni4emIh78PiI0axGo7RfdsZ/mn3saASXc94tDbpC5yn7+NpEg==", "dependencies": { "@babel/code-frame": "^7.0.0", "@babel/core": "^7.20.0", @@ -11561,23 +11616,23 @@ "invariant": "^2.2.4", "jest-worker": "^27.2.0", "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.73.8", - "metro-cache": "0.73.8", - "metro-cache-key": "0.73.8", - "metro-config": "0.73.8", - "metro-core": "0.73.8", - "metro-file-map": "0.73.8", - "metro-hermes-compiler": "0.73.8", - "metro-inspector-proxy": "0.73.8", - "metro-minify-terser": "0.73.8", - "metro-minify-uglify": "0.73.8", - "metro-react-native-babel-preset": "0.73.8", - "metro-resolver": "0.73.8", - "metro-runtime": "0.73.8", - "metro-source-map": "0.73.8", - "metro-symbolicate": "0.73.8", - "metro-transform-plugins": "0.73.8", - "metro-transform-worker": "0.73.8", + "metro-babel-transformer": "0.73.9", + "metro-cache": "0.73.9", + "metro-cache-key": "0.73.9", + "metro-config": "0.73.9", + "metro-core": "0.73.9", + "metro-file-map": "0.73.9", + "metro-hermes-compiler": "0.73.9", + "metro-inspector-proxy": "0.73.9", + "metro-minify-terser": "0.73.9", + "metro-minify-uglify": "0.73.9", + "metro-react-native-babel-preset": "0.73.9", + "metro-resolver": "0.73.9", + "metro-runtime": "0.73.9", + "metro-source-map": "0.73.9", + "metro-symbolicate": "0.73.9", + "metro-transform-plugins": "0.73.9", + "metro-transform-worker": "0.73.9", "mime-types": "^2.1.27", "node-fetch": "^2.2.0", "nullthrows": "^1.1.1", @@ -11595,41 +11650,41 @@ } }, "node_modules/metro-babel-transformer": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.73.8.tgz", - "integrity": "sha512-GO6H/W2RjZ0/gm1pIvdO9EP34s3XN6kzoeyxqmfqKfYhJmYZf1SzXbyiIHyMbJNwJVrsKuHqu32+GopTlKscWw==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.73.9.tgz", + "integrity": "sha512-DlYwg9wwYIZTHtic7dyD4BP0SDftoltZ3clma76nHu43blMWsCnrImHeHsAVne3XsQ+RJaSRxhN5nkG2VyVHwA==", "dependencies": { "@babel/core": "^7.20.0", "hermes-parser": "0.8.0", - "metro-source-map": "0.73.8", + "metro-source-map": "0.73.9", "nullthrows": "^1.1.1" } }, "node_modules/metro-cache": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.73.8.tgz", - "integrity": "sha512-/uFbTIw813Rvb8kSAIHvax9gWl41dtgjY2SpJLNIBLdQ6oFZ3CVo3ahZIiEZOrCeHl9xfGn5tmvNb8CEFa/Q5w==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.73.9.tgz", + "integrity": "sha512-upiRxY8rrQkUWj7ieACD6tna7xXuXdu2ZqrheksT79ePI0aN/t0memf6WcyUtJUMHZetke3j+ppELNvlmp3tOw==", "dependencies": { - "metro-core": "0.73.8", + "metro-core": "0.73.9", "rimraf": "^3.0.2" } }, "node_modules/metro-cache-key": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.73.8.tgz", - "integrity": "sha512-VzFGu4kJGIkLjyDgVoM2ZxIHlMdCZWMqVIux9N+EeyMVMvGXTiXW8eGROgxzDhVjyR58IjfMsYpRCKz5dR+2ew==" + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.73.9.tgz", + "integrity": "sha512-uJg+6Al7UoGIuGfoxqPBy6y1Ewq7Y8/YapGYIDh6sohInwt/kYKnPZgLDYHIPvY2deORnQ/2CYo4tOeBTnhCXQ==" }, "node_modules/metro-config": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.73.8.tgz", - "integrity": "sha512-sAYq+llL6ZAfro64U99ske8HcKKswxX4wIZbll9niBKG7TkWm7tfMY1jO687XEmE4683rHncZeBRav9pLngIzg==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.73.9.tgz", + "integrity": "sha512-NiWl1nkYtjqecDmw77tbRbXnzIAwdO6DXGZTuKSkH+H/c1NKq1eizO8Fe+NQyFtwR9YLqn8Q0WN1nmkwM1j8CA==", "dependencies": { "cosmiconfig": "^5.0.5", "jest-validate": "^26.5.2", - "metro": "0.73.8", - "metro-cache": "0.73.8", - "metro-core": "0.73.8", - "metro-runtime": "0.73.8" + "metro": "0.73.9", + "metro-cache": "0.73.9", + "metro-core": "0.73.9", + "metro-runtime": "0.73.9" } }, "node_modules/metro-config/node_modules/@jest/types": { @@ -11774,18 +11829,18 @@ } }, "node_modules/metro-core": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.73.8.tgz", - "integrity": "sha512-Aew4dthbZf8bRRjlYGL3cnai3+LKYTf6mc7YS2xLQRWtgGZ1b/H8nQtBvXZpfRYFcS84UeEQ10vwIf5eR3qPdQ==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.73.9.tgz", + "integrity": "sha512-1NTs0IErlKcFTfYyRT3ljdgrISWpl1nys+gaHkXapzTSpvtX9F1NQNn5cgAuE+XIuTJhbsCdfIJiM2JXbrJQaQ==", "dependencies": { "lodash.throttle": "^4.1.1", - "metro-resolver": "0.73.8" + "metro-resolver": "0.73.9" } }, "node_modules/metro-file-map": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.73.8.tgz", - "integrity": "sha512-CM552hUO9om02jJdLszOCIDADKNaaeVz8CjYXItndvgr5jmFlQYAR+UMvaDzeT8oYdAV1DXAljma2CS2UBymPg==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.73.9.tgz", + "integrity": "sha512-R/Wg3HYeQhYY3ehWtfedw8V0ne4lpufG7a21L3GWer8tafnC9pmjoCKEbJz9XZkVj9i1FtxE7UTbrtZNeIILxQ==", "dependencies": { "abort-controller": "^3.0.0", "anymatch": "^3.0.3", @@ -11957,14 +12012,14 @@ } }, "node_modules/metro-hermes-compiler": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-hermes-compiler/-/metro-hermes-compiler-0.73.8.tgz", - "integrity": "sha512-2d7t+TEoQLk+jyXgBykmAtPPJK2B46DB3qUYIMKDFDDaKzCljrojyVuGgQq6SM1f95fe6HDAQ3K9ihTjeB90yw==" + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-hermes-compiler/-/metro-hermes-compiler-0.73.9.tgz", + "integrity": "sha512-5B3vXIwQkZMSh3DQQY23XpTCpX9kPLqZbA3rDuAcbGW0tzC3f8dCenkyBb0GcCzyTDncJeot/A7oVCVK6zapwg==" }, "node_modules/metro-inspector-proxy": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.73.8.tgz", - "integrity": "sha512-F0QxwDTox0TDeXVRN7ZmI7BknBjPDVKQ1ZeKznFBiMa0SXiD1kzoksfpDbZ6hTEKrhVM9Ep0YQmC7avwZouOnA==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.73.9.tgz", + "integrity": "sha512-B3WrWZnlYhtTrv0IaX3aUAhi2qVILPAZQzb5paO1e+xrz4YZHk9c7dXv7qe7B/IQ132e3w46y3AL7rFo90qVjA==", "dependencies": { "connect": "^3.6.5", "debug": "^2.2.0", @@ -12009,25 +12064,25 @@ } }, "node_modules/metro-minify-terser": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.73.8.tgz", - "integrity": "sha512-pnagyXAoMPhihWrHRIWqCxrP6EJ8Hfugv5RXBb6HbOANmwajn2uQuzeu18+dXaN1yPoDCMCgpg/UA4ibFN5jtQ==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.73.9.tgz", + "integrity": "sha512-MTGPu2qV5qtzPJ2SqH6s58awHDtZ4jd7lmmLR+7TXDwtZDjIBA0YVfI0Zak2Haby2SqoNKrhhUns/b4dPAQAVg==", "dependencies": { "terser": "^5.15.0" } }, "node_modules/metro-minify-uglify": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.73.8.tgz", - "integrity": "sha512-9wZqKfraVfmtMXdOzRyan+6r1woQXqqa4KeXfVh7+Mxl+5+J0Lmw6EvTrWawsaOEpvpn32q9MfoHC1d8plDJwA==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.73.9.tgz", + "integrity": "sha512-gzxD/7WjYcnCNGiFJaA26z34rjOp+c/Ft++194Wg91lYep3TeWQ0CnH8t2HRS7AYDHU81SGWgvD3U7WV0g4LGA==", "dependencies": { "uglify-es": "^3.1.9" } }, "node_modules/metro-react-native-babel-preset": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.8.tgz", - "integrity": "sha512-spNrcQJTbQntEIqJnCA6yL4S+dzV9fXCk7U+Rm7yJasZ4o4Frn7jP23isu7FlZIp1Azx1+6SbP7SgQM+IP5JgQ==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.9.tgz", + "integrity": "sha512-AoD7v132iYDV4K78yN2OLgTPwtAKn0XlD2pOhzyBxiI8PeXzozhbKyPV7zUOJUPETj+pcEVfuYj5ZN/8+bhbCw==", "dependencies": { "@babel/core": "^7.20.0", "@babel/plugin-proposal-async-generator-functions": "^7.0.0", @@ -12073,16 +12128,16 @@ } }, "node_modules/metro-react-native-babel-transformer": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.8.tgz", - "integrity": "sha512-oH/LCCJPauteAE28c0KJAiSrkV+1VJbU0PwA9UwaWnle+qevs/clpKQ8LrIr33YbBj4CiI1kFoVRuNRt5h4NFg==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.9.tgz", + "integrity": "sha512-DSdrEHuQ22ixY7DyipyKkIcqhOJrt5s6h6X7BYJCP9AMUfXOwLe2biY3BcgJz5GOXv8/Akry4vTCvQscVS1otQ==", "dependencies": { "@babel/core": "^7.20.0", "babel-preset-fbjs": "^3.4.0", "hermes-parser": "0.8.0", - "metro-babel-transformer": "0.73.8", - "metro-react-native-babel-preset": "0.73.8", - "metro-source-map": "0.73.8", + "metro-babel-transformer": "0.73.9", + "metro-react-native-babel-preset": "0.73.9", + "metro-source-map": "0.73.9", "nullthrows": "^1.1.1" }, "peerDependencies": { @@ -12090,33 +12145,33 @@ } }, "node_modules/metro-resolver": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.73.8.tgz", - "integrity": "sha512-GiBWont7/OgAftkkj2TiEp+Gf1PYZUk8xV4MbtnQjIKyy3MlGY3GbpMQ1BHih9GUQqlF0n9jsUlC2K5P0almXQ==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.73.9.tgz", + "integrity": "sha512-Ej3wAPOeNRPDnJmkK0zk7vJ33iU07n+oPhpcf5L0NFkWneMmSM2bflMPibI86UjzZGmRfn0AhGhs8yGeBwQ/Xg==", "dependencies": { "absolute-path": "^0.0.0" } }, "node_modules/metro-runtime": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.73.8.tgz", - "integrity": "sha512-M+Bg9M4EN5AEpJ8NkiUsawD75ifYvYfHi05w6QzHXaqOrsTeaRbbeLuOGCYxU2f/tPg17wQV97/rqUQzs9qEtA==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.73.9.tgz", + "integrity": "sha512-d5Hs83FpKB9r8q8Vb95+fa6ESpwysmPr4lL1I2rM2qXAFiO7OAPT9Bc23WmXgidkBtD0uUFdB2lG+H1ATz8rZg==", "dependencies": { "@babel/runtime": "^7.0.0", "react-refresh": "^0.4.0" } }, "node_modules/metro-source-map": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.73.8.tgz", - "integrity": "sha512-wozFXuBYMAy7b8BCYwC+qoXsvayVJBHWtSTlSLva99t+CoUSG9JO9kg1umzbOz28YYPxKmvb/wbnLMkHdas2cA==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.73.9.tgz", + "integrity": "sha512-l4VZKzdqafipriETYR6lsrwtavCF1+CMhCOY9XbyWeTrpGSNgJQgdeJpttzEZTHQQTLR0csQo0nD1ef3zEP6IQ==", "dependencies": { "@babel/traverse": "^7.20.0", "@babel/types": "^7.20.0", "invariant": "^2.2.4", - "metro-symbolicate": "0.73.8", + "metro-symbolicate": "0.73.9", "nullthrows": "^1.1.1", - "ob1": "0.73.8", + "ob1": "0.73.9", "source-map": "^0.5.6", "vlq": "^1.0.0" } @@ -12130,12 +12185,12 @@ } }, "node_modules/metro-symbolicate": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.73.8.tgz", - "integrity": "sha512-xkBAcceYYp0GGdCCuMzkCF1ejHsd0lYlbKBkjSRgM0Nlj80VapPaSwumYoAvSaDxcbkvS7/sCjURGp5DsSFgRQ==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.73.9.tgz", + "integrity": "sha512-4TUOwxRHHqbEHxRqRJ3wZY5TA8xq7AHMtXrXcjegMH9FscgYztsrIG9aNBUBS+VLB6g1qc6BYbfIgoAnLjCDyw==", "dependencies": { "invariant": "^2.2.4", - "metro-source-map": "0.73.8", + "metro-source-map": "0.73.9", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "through2": "^2.0.1", @@ -12157,9 +12212,9 @@ } }, "node_modules/metro-transform-plugins": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.73.8.tgz", - "integrity": "sha512-IxjlnB5eA49M0WfvPEzvRikK3Rr6bECUUfcZt/rWpSphq/mttgyLYcHQ+VTZZl0zHolC3cTLwgoDod4IIJBn1A==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.73.9.tgz", + "integrity": "sha512-r9NeiqMngmooX2VOKLJVQrMuV7PAydbqst5bFhdVBPcFpZkxxqyzjzo+kzrszGy2UpSQBZr2P1L6OMjLHwQwfQ==", "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.0", @@ -12169,22 +12224,22 @@ } }, "node_modules/metro-transform-worker": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.73.8.tgz", - "integrity": "sha512-B8kR6lmcvyG4UFSF2QDfr/eEnWJvg0ZadooF8Dg6m/3JSm9OAqfSoC0YrWqAuvtWImNDnbeKWN7/+ns44Hv6tg==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.73.9.tgz", + "integrity": "sha512-Rq4b489sIaTUENA+WCvtu9yvlT/C6zFMWhU4sq+97W29Zj0mPBjdk+qGT5n1ZBgtBIJzZWt1KxeYuc17f4aYtQ==", "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.0", "@babel/parser": "^7.20.0", "@babel/types": "^7.20.0", "babel-preset-fbjs": "^3.4.0", - "metro": "0.73.8", - "metro-babel-transformer": "0.73.8", - "metro-cache": "0.73.8", - "metro-cache-key": "0.73.8", - "metro-hermes-compiler": "0.73.8", - "metro-source-map": "0.73.8", - "metro-transform-plugins": "0.73.8", + "metro": "0.73.9", + "metro-babel-transformer": "0.73.9", + "metro-cache": "0.73.9", + "metro-cache-key": "0.73.9", + "metro-hermes-compiler": "0.73.9", + "metro-source-map": "0.73.9", + "metro-transform-plugins": "0.73.9", "nullthrows": "^1.1.1" } }, @@ -12431,17 +12486,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -12579,9 +12623,9 @@ "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" }, "node_modules/ob1": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.73.8.tgz", - "integrity": "sha512-1F7j+jzD+edS6ohQP7Vg5f3yiIk5i3x1uLrNIHOmLHWzWK1t3zrDpjnoXghccdVlsU+UjbyURnDynm4p0GgXeA==" + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.73.9.tgz", + "integrity": "sha512-kHOzCOFXmAM26fy7V/YuXNKne2TyRiXbFAvPBIbuedJCZZWQZHLdPzMeXJI4Egt6IcfDttRzN3jQ90wOwq1iNw==" }, "node_modules/object-assign": { "version": "4.1.1", @@ -13153,9 +13197,9 @@ } }, "node_modules/prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -13179,6 +13223,26 @@ "node": ">=6.0.0" } }, + "node_modules/prettier-plugin-organize-imports": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.2.tgz", + "integrity": "sha512-e97lE6odGSiHonHJMTYC0q0iLXQyw0u5z/PJpvP/3vRy6/Zi9kLBwFAbEGjDzIowpjQv8b+J04PDamoUSQbzGA==", + "dev": true, + "peerDependencies": { + "@volar/vue-language-plugin-pug": "^1.0.4", + "@volar/vue-typescript": "^1.0.4", + "prettier": ">=2.0", + "typescript": ">=2.9" + }, + "peerDependenciesMeta": { + "@volar/vue-language-plugin-pug": { + "optional": true + }, + "@volar/vue-typescript": { + "optional": true + } + } + }, "node_modules/pretty-format": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", @@ -13326,9 +13390,9 @@ } }, "node_modules/react-devtools-core": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.27.2.tgz", - "integrity": "sha512-8SzmIkpO87alD7Xr6gWIEa1jHkMjawOZ+6egjazlnjB4UUcbnzGDf/vBJ4BzGuWWEM+pzrxuzsPpcMqlQkYK2g==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.27.4.tgz", + "integrity": "sha512-dvZjrAJjahd6NNl7dDwEk5TyHsWJxDpYL7VnD9jdEr98EEEsVhw9G8JDX54Nrb3XIIOBlJDpjo3AuBuychX9zg==", "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" @@ -13360,14 +13424,14 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/react-native": { - "version": "0.71.4", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.71.4.tgz", - "integrity": "sha512-3hSYqvWrOdKhpV3HpEKp1/CkWx8Sr/N/miCrmUIAsVTSJUR7JW0VvIsrV9urDhUj/s6v2WF4n7qIEEJsmTCrPw==", + "version": "0.71.6", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.71.6.tgz", + "integrity": "sha512-gHrDj7qaAaiE41JwaFCh3AtvOqOLuRgZtHKzNiwxakG/wvPAYmG73ECfWHGxjxIx/QT17Hp37Da3ipCei/CayQ==", "dependencies": { "@jest/create-cache-key-function": "^29.2.1", - "@react-native-community/cli": "10.2.0", + "@react-native-community/cli": "10.2.2", "@react-native-community/cli-platform-android": "10.2.0", - "@react-native-community/cli-platform-ios": "10.2.0", + "@react-native-community/cli-platform-ios": "10.2.1", "@react-native/assets": "1.0.0", "@react-native/normalize-color": "2.1.0", "@react-native/polyfills": "2.0.0", @@ -13380,16 +13444,16 @@ "jest-environment-node": "^29.2.1", "jsc-android": "^250231.0.0", "memoize-one": "^5.0.0", - "metro-react-native-babel-transformer": "0.73.8", - "metro-runtime": "0.73.8", - "metro-source-map": "0.73.8", + "metro-react-native-babel-transformer": "0.73.9", + "metro-runtime": "0.73.9", + "metro-source-map": "0.73.9", "mkdirp": "^0.5.1", "nullthrows": "^1.1.1", "pretty-format": "^26.5.2", "promise": "^8.3.0", "react-devtools-core": "^4.26.1", "react-native-codegen": "^0.71.5", - "react-native-gradle-plugin": "^0.71.16", + "react-native-gradle-plugin": "^0.71.17", "react-refresh": "^0.4.0", "react-shallow-renderer": "^16.15.0", "regenerator-runtime": "^0.13.2", @@ -13450,14 +13514,30 @@ } }, "node_modules/react-native-gradle-plugin": { - "version": "0.71.16", - "resolved": "https://registry.npmjs.org/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.16.tgz", - "integrity": "sha512-H2BjG2zk7B7Wii9sXvd9qhCVRQYDAHSWdMw9tscmZBqSP62DkIWEQSk4/B2GhQ4aK9ydVXgtqR6tBeg3yy8TSA==" + "version": "0.71.17", + "resolved": "https://registry.npmjs.org/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.17.tgz", + "integrity": "sha512-OXXYgpISEqERwjSlaCiaQY6cTY5CH6j73gdkWpK0hedxtiWMWgH+i5TOi4hIGYitm9kQBeyDu+wim9fA8ROFJA==" + }, + "node_modules/react-native-paper": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.6.0.tgz", + "integrity": "sha512-X/MhAN/k1iwsp8N42MoSUfGbgCt4Lw8IlsoToIPoSoYRxbEq2OYOPRH7JsktPtFY0a1SAJPqVHFFS76FU8eSFg==", + "dependencies": { + "@callstack/react-theme-provider": "^3.0.8", + "color": "^3.1.2", + "use-latest-callback": "^0.1.5" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-safe-area-context": "*", + "react-native-vector-icons": "*" + } }, "node_modules/react-native-reanimated": { - "version": "2.14.4", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-2.14.4.tgz", - "integrity": "sha512-DquSbl7P8j4SAmc+kRdd75Ianm8G+IYQ9T4AQ6lrpLVeDkhZmjWI0wkutKWnp6L7c5XNVUrFDUf69dwETLCItQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.0.2.tgz", + "integrity": "sha512-8Et90yTI9yxchGbDP79k391XZqc/64zNbASbGy8X3Vgv4EbZ1M3IkKwcIbZmbVwpA804VJ6V9nJAGUh9fP0LrA==", "dependencies": { "@babel/plugin-transform-object-assign": "^7.16.7", "@babel/preset-typescript": "^7.16.7", @@ -13473,6 +13553,15 @@ "react-native": "*" } }, + "node_modules/react-native-safe-area-context": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.5.1.tgz", + "integrity": "sha512-bKcwk6zZvyz+VLoG6Uia1oiYU1jSbv1ysjEKSRLsLtPcDsbixsTc0UgfrPqjZxNTPzvYLMcr8ufA90UQauN4mw==", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-vector-icons": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz", @@ -13761,6 +13850,14 @@ "redux": ">4.0.0" } }, + "node_modules/redux-saga": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.2.3.tgz", + "integrity": "sha512-HDe0wTR5nhd8Xr5xjGzoyTbdAw6rjy1GDplFt3JKtKN8/MnkQSRqK/n6aQQhpw5NI4ekDVOaW+w4sdxPBaCoTQ==", + "dependencies": { + "@redux-saga/core": "^1.2.3" + } + }, "node_modules/redux-thunk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", @@ -13918,11 +14015,11 @@ "integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==" }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz", + "integrity": "sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==", "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.12.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -13961,9 +14058,9 @@ "deprecated": "https://github.com/lydell/resolve-url#deprecated" }, "node_modules/resolve.exports": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.1.tgz", - "integrity": "sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true, "engines": { "node": ">=10" @@ -14253,9 +14350,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.0.tgz", - "integrity": "sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -14279,6 +14376,19 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -15027,9 +15137,9 @@ } }, "node_modules/terser": { - "version": "5.16.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.6.tgz", - "integrity": "sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==", + "version": "5.16.9", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.9.tgz", + "integrity": "sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg==", "dependencies": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -15198,9 +15308,9 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/ts-jest": { - "version": "29.0.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz", - "integrity": "sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==", + "version": "29.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz", + "integrity": "sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -15223,7 +15333,7 @@ "@jest/types": "^29.0.0", "babel-jest": "^29.0.0", "jest": "^29.0.0", - "typescript": ">=4.3" + "typescript": ">=4.3 <6" }, "peerDependenciesMeta": { "@babel/core": { @@ -15253,9 +15363,9 @@ } }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -15358,6 +15468,27 @@ "node": ">=4.2.0" } }, + "node_modules/typescript-compare": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", + "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==", + "dependencies": { + "typescript-logic": "^0.0.0" + } + }, + "node_modules/typescript-logic": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz", + "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==" + }, + "node_modules/typescript-tuple": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz", + "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==", + "dependencies": { + "typescript-compare": "^0.0.2" + } + }, "node_modules/uglify-es": { "version": "3.3.9", "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", @@ -15513,9 +15644,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "funding": [ { "type": "opencollective", @@ -15524,6 +15655,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -15531,7 +15666,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -15560,6 +15695,14 @@ "node": ">=0.10.0" } }, + "node_modules/use-latest-callback": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.6.tgz", + "integrity": "sha512-VO/P91A/PmKH9bcN9a7O3duSuxe6M14ZoYXgA6a8dab8doWNdhiIHzEkX/jFeTTRBsX0Ubk6nG4q2NIjNsj+bg==", + "peerDependencies": { + "react": ">=16.8" + } + }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -15846,42 +15989,42 @@ }, "dependencies": { "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "requires": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", "requires": { "@babel/highlight": "^7.18.6" } }, "@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==" + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", + "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==" }, "@babel/core": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", - "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", + "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.0", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.0", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", + "@babel/helper-compilation-targets": "^7.21.4", + "@babel/helper-module-transforms": "^7.21.2", "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.0", + "@babel/parser": "^7.21.4", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0", + "@babel/traverse": "^7.21.4", + "@babel/types": "^7.21.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -15890,26 +16033,14 @@ } }, "@babel/generator": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", - "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", + "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", "requires": { - "@babel/types": "^7.21.0", + "@babel/types": "^7.21.4", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } } }, "@babel/helper-annotate-as-pure": { @@ -15930,21 +16061,21 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", + "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", + "@babel/compat-data": "^7.21.4", + "@babel/helper-validator-option": "^7.21.0", "browserslist": "^4.21.3", "lru-cache": "^5.1.1", "semver": "^6.3.0" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", - "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz", + "integrity": "sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", @@ -15957,9 +16088,9 @@ } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz", - "integrity": "sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz", + "integrity": "sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "regexpu-core": "^5.3.1" @@ -16017,11 +16148,11 @@ } }, "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.21.4" } }, "@babel/helper-module-transforms": { @@ -16147,9 +16278,9 @@ } }, "@babel/parser": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", - "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==" + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", + "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -16380,11 +16511,11 @@ } }, "@babel/plugin-syntax-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", - "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz", + "integrity": "sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-syntax-import-assertions": { @@ -16413,11 +16544,11 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -16485,11 +16616,11 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", "requires": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-arrow-functions": { @@ -16552,9 +16683,9 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", - "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", + "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", "requires": { "@babel/helper-plugin-utils": "^7.20.2" } @@ -16702,9 +16833,9 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", - "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz", + "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==", "requires": { "@babel/helper-plugin-utils": "^7.20.2" } @@ -16771,11 +16902,11 @@ } }, "@babel/plugin-transform-runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz", - "integrity": "sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz", + "integrity": "sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA==", "requires": { - "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-module-imports": "^7.21.4", "@babel/helper-plugin-utils": "^7.20.2", "babel-plugin-polyfill-corejs2": "^0.3.3", "babel-plugin-polyfill-corejs3": "^0.6.0", @@ -16825,10 +16956,11 @@ } }, "@babel/plugin-transform-typescript": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz", - "integrity": "sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg==", + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", + "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-create-class-features-plugin": "^7.21.0", "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-typescript": "^7.20.0" @@ -16852,30 +16984,30 @@ } }, "@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.4.tgz", + "integrity": "sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==", "requires": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", + "@babel/compat-data": "^7.21.4", + "@babel/helper-compilation-targets": "^7.21.4", "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", + "@babel/helper-validator-option": "^7.21.0", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7", + "@babel/plugin-proposal-async-generator-functions": "^7.20.7", "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.21.0", "@babel/plugin-proposal-dynamic-import": "^7.18.6", "@babel/plugin-proposal-export-namespace-from": "^7.18.9", "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", + "@babel/plugin-proposal-object-rest-spread": "^7.20.7", "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.21.0", "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.21.0", "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -16892,40 +17024,40 @@ "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.20.7", + "@babel/plugin-transform-async-to-generator": "^7.20.7", "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", + "@babel/plugin-transform-block-scoping": "^7.21.0", + "@babel/plugin-transform-classes": "^7.21.0", + "@babel/plugin-transform-computed-properties": "^7.20.7", + "@babel/plugin-transform-destructuring": "^7.21.3", "@babel/plugin-transform-dotall-regex": "^7.18.6", "@babel/plugin-transform-duplicate-keys": "^7.18.9", "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-for-of": "^7.21.0", "@babel/plugin-transform-function-name": "^7.18.9", "@babel/plugin-transform-literals": "^7.18.9", "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", + "@babel/plugin-transform-modules-amd": "^7.20.11", + "@babel/plugin-transform-modules-commonjs": "^7.21.2", + "@babel/plugin-transform-modules-systemjs": "^7.20.11", "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5", "@babel/plugin-transform-new-target": "^7.18.6", "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", + "@babel/plugin-transform-parameters": "^7.21.3", "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.20.5", "@babel/plugin-transform-reserved-words": "^7.18.6", "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-spread": "^7.20.7", "@babel/plugin-transform-sticky-regex": "^7.18.6", "@babel/plugin-transform-template-literals": "^7.18.9", "@babel/plugin-transform-typeof-symbol": "^7.18.9", "@babel/plugin-transform-unicode-escapes": "^7.18.10", "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", + "@babel/types": "^7.21.4", "babel-plugin-polyfill-corejs2": "^0.3.3", "babel-plugin-polyfill-corejs3": "^0.6.0", "babel-plugin-polyfill-regenerator": "^0.4.1", @@ -16934,13 +17066,13 @@ } }, "@babel/preset-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.18.6.tgz", - "integrity": "sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.21.4.tgz", + "integrity": "sha512-F24cSq4DIBmhq4OzK3dE63NHagb27OPE3eWR+HLekt4Z3Y5MzIIUGF3LlLgV0gN8vzbDViSY7HnrReNVCJXTeA==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-flow-strip-types": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-transform-flow-strip-types": "^7.21.0" } }, "@babel/preset-modules": { @@ -16956,13 +17088,15 @@ } }, "@babel/preset-typescript": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz", - "integrity": "sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz", + "integrity": "sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==", "requires": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-transform-typescript": "^7.21.0" + "@babel/plugin-syntax-jsx": "^7.21.4", + "@babel/plugin-transform-modules-commonjs": "^7.21.2", + "@babel/plugin-transform-typescript": "^7.21.3" } }, "@babel/register": { @@ -17026,26 +17160,26 @@ } }, "@babel/traverse": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", - "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", + "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.1", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.2", - "@babel/types": "^7.21.2", + "@babel/parser": "^7.21.4", + "@babel/types": "^7.21.4", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", - "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", + "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", "requires": { "@babel/helper-string-parser": "^7.19.4", "@babel/helper-validator-identifier": "^7.19.1", @@ -17058,6 +17192,22 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@callstack/react-theme-provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@callstack/react-theme-provider/-/react-theme-provider-3.0.8.tgz", + "integrity": "sha512-5U231sYY2sqQOaELX0WBCn+iluV8bFaXIS7em03k4W5Xz0AhGvKlnpLIhDGFP8im/SvNW7/2XoR0BsClhn9t6Q==", + "requires": { + "deepmerge": "^3.2.0", + "hoist-non-react-statics": "^3.3.0" + }, + "dependencies": { + "deepmerge": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", + "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==" + } + } + }, "@egjs/hammerjs": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", @@ -17067,18 +17217,18 @@ } }, "@eslint-community/eslint-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz", - "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "requires": { "eslint-visitor-keys": "^3.3.0" } }, "@eslint-community/regexpp": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", - "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", + "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", "dev": true }, "@eslint/eslintrc": { @@ -17121,23 +17271,6 @@ } } }, - "@gorhom/bottom-sheet": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@gorhom/bottom-sheet/-/bottom-sheet-4.4.5.tgz", - "integrity": "sha512-Z5Z20wshLUB8lIdtMKoJaRnjd64wBR/q8EeVPThrg+skrcBwBPHfUwZJ2srB0rEszA/01ejSJy/ixyd7Ra7vUA==", - "requires": { - "@gorhom/portal": "1.0.14", - "invariant": "^2.2.4" - } - }, - "@gorhom/portal": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@gorhom/portal/-/portal-1.0.14.tgz", - "integrity": "sha512-MXyL4xvCjmgaORr/rtryDNFy3kU4qUbKlwtQqqsygd0xX3mhKjOLn6mQK8wfu0RkoE0pBE0nAasRoHua+/QZ7A==", - "requires": { - "nanoid": "^3.3.1" - } - }, "@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -17667,12 +17800,13 @@ } }, "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@jridgewell/resolve-uri": { @@ -17686,38 +17820,33 @@ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" }, "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", "requires": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } } }, "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "requires": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" + }, + "dependencies": { + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + } } }, "@nodelib/fs.scandir": { @@ -17747,24 +17876,24 @@ } }, "@react-native-async-storage/async-storage": { - "version": "1.17.11", - "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.17.11.tgz", - "integrity": "sha512-bzs45n5HNcDq6mxXnSsOHysZWn1SbbebNxldBXCQs8dSvF8Aor9KCdpm+TpnnGweK3R6diqsT8lFhX77VX0NFw==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.18.1.tgz", + "integrity": "sha512-70aFW8fVCKl+oA1AKPFDpE6s4t9pulj2QeLX+MabEmzfT3urd/3cckv45WJvtocdoIH/oXA3Y+YcCRJCcNa8mA==", "requires": { "merge-options": "^3.0.4" } }, "@react-native-community/cli": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-10.2.0.tgz", - "integrity": "sha512-QH7AFBz5FX2zTZRH/o3XehHrZ0aZZEL5Sh+23nSEFgSj3bLFfvjjZhuoiRSAo7iiBdvAoXrfxQ8TXgg4Xf/7fw==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-10.2.2.tgz", + "integrity": "sha512-aZVcVIqj+OG6CrliR/Yn8wHxrvyzbFBY9cj7n0MvRw/P54QUru2nNqUTSSbqv0Qaa297yHJbe6kFDojDMSTM8Q==", "requires": { "@react-native-community/cli-clean": "^10.1.1", "@react-native-community/cli-config": "^10.1.1", "@react-native-community/cli-debugger-ui": "^10.0.0", - "@react-native-community/cli-doctor": "^10.2.0", + "@react-native-community/cli-doctor": "^10.2.2", "@react-native-community/cli-hermes": "^10.2.0", - "@react-native-community/cli-plugin-metro": "^10.2.0", + "@react-native-community/cli-plugin-metro": "^10.2.2", "@react-native-community/cli-server-api": "^10.1.1", "@react-native-community/cli-tools": "^10.1.1", "@react-native-community/cli-types": "^10.0.0", @@ -18109,12 +18238,12 @@ } }, "@react-native-community/cli-doctor": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-10.2.0.tgz", - "integrity": "sha512-yLxJazUmNSPslHxeeev0gLvsK0nQan8BmGWbtqPz2WwbIbD89vbytC7G96OxiQXr46iWEWAwEJiTTdgA7jlA5Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-10.2.2.tgz", + "integrity": "sha512-49Ep2aQOF0PkbAR/TcyMjOm9XwBa8VQr+/Zzf4SJeYwiYLCT1NZRAVAVjYRXl0xqvq5S5mAGZZShS4AQl4WsZw==", "requires": { "@react-native-community/cli-config": "^10.1.1", - "@react-native-community/cli-platform-ios": "^10.2.0", + "@react-native-community/cli-platform-ios": "^10.2.1", "@react-native-community/cli-tools": "^10.1.1", "chalk": "^4.1.2", "command-exists": "^1.2.8", @@ -18462,9 +18591,9 @@ } }, "@react-native-community/cli-platform-ios": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.0.tgz", - "integrity": "sha512-hIPK3iL/mL+0ChXmQ9uqqzNOKA48H+TAzg+hrxQLll/6dNMxDeK9/wZpktcsh8w+CyhqzKqVernGcQs7tPeKGw==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.1.tgz", + "integrity": "sha512-hz4zu4Y6eyj7D0lnZx8Mf2c2si8y+zh/zUTgCTaPPLzQD8jSZNNBtUUiA1cARm2razpe8marCZ1QbTMAGbf3mg==", "requires": { "@react-native-community/cli-tools": "^10.1.1", "chalk": "^4.1.2", @@ -18598,20 +18727,20 @@ } }, "@react-native-community/cli-plugin-metro": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.0.tgz", - "integrity": "sha512-9eiJrKYuauEDkQLCrjJUh7tS9T0oaMQqVUSSSuyDG6du7HQcfaR4mSf21wK75jvhKiwcQLpsFmMdctAb+0v+Cg==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.2.tgz", + "integrity": "sha512-sTGjZlD3OGqbF9v1ajwUIXhGmjw9NyJ/14Lo0sg7xH8Pv4qUd5ZvQ6+DWYrQn3IKFUMfGFWYyL81ovLuPylrpw==", "requires": { "@react-native-community/cli-server-api": "^10.1.1", "@react-native-community/cli-tools": "^10.1.1", "chalk": "^4.1.2", "execa": "^1.0.0", - "metro": "0.73.8", - "metro-config": "0.73.8", - "metro-core": "0.73.8", - "metro-react-native-babel-transformer": "0.73.8", - "metro-resolver": "0.73.8", - "metro-runtime": "0.73.8", + "metro": "0.73.9", + "metro-config": "0.73.9", + "metro-core": "0.73.9", + "metro-react-native-babel-transformer": "0.73.9", + "metro-resolver": "0.73.9", + "metro-runtime": "0.73.9", "readline": "^1.3.0" }, "dependencies": { @@ -19057,9 +19186,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -19094,6 +19223,53 @@ "resolved": "https://registry.npmjs.org/@react-native/polyfills/-/polyfills-2.0.0.tgz", "integrity": "sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==" }, + "@redux-saga/core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.2.3.tgz", + "integrity": "sha512-U1JO6ncFBAklFTwoQ3mjAeQZ6QGutsJzwNBjgVLSWDpZTRhobUzuVDS1qH3SKGJD8fvqoaYOjp6XJ3gCmeZWgA==", + "requires": { + "@babel/runtime": "^7.6.3", + "@redux-saga/deferred": "^1.2.1", + "@redux-saga/delay-p": "^1.2.1", + "@redux-saga/is": "^1.1.3", + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1", + "redux": "^4.0.4", + "typescript-tuple": "^2.2.1" + } + }, + "@redux-saga/deferred": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.2.1.tgz", + "integrity": "sha512-cmin3IuuzMdfQjA0lG4B+jX+9HdTgHZZ+6u3jRAOwGUxy77GSlTi4Qp2d6PM1PUoTmQUR5aijlA39scWWPF31g==" + }, + "@redux-saga/delay-p": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.2.1.tgz", + "integrity": "sha512-MdiDxZdvb1m+Y0s4/hgdcAXntpUytr9g0hpcOO1XFVyyzkrDu3SKPgBFOtHn7lhu7n24ZKIAT1qtKyQjHqRd+w==", + "requires": { + "@redux-saga/symbols": "^1.1.3" + } + }, + "@redux-saga/is": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.1.3.tgz", + "integrity": "sha512-naXrkETG1jLRfVfhOx/ZdLj0EyAzHYbgJWkXbB3qFliPcHKiWbv/ULQryOAEKyjrhiclmr6AMdgsXFyx7/yE6Q==", + "requires": { + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1" + } + }, + "@redux-saga/symbols": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.1.3.tgz", + "integrity": "sha512-hCx6ZvU4QAEUojETnX8EVg4ubNLBFl1Lps4j2tX7o45x/2qg37m3c6v+kSp8xjDJY+2tJw4QB3j8o8dsl1FDXg==" + }, + "@redux-saga/types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", + "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==" + }, "@reduxjs/toolkit": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.3.tgz", @@ -19315,9 +19491,9 @@ } }, "@types/jest": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.1.tgz", - "integrity": "sha512-zDQSWXG+ZkEvs2zFFMszePhx4euKz+Yt3Gg1P+RHjfJBinTTr6L2DEyovO4V/WrKXuF0Dgn56GWGZPDa6TW9eQ==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", "dev": true, "requires": { "expect": "^29.0.0", @@ -19331,9 +19507,9 @@ "dev": true }, "@types/node": { - "version": "18.15.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.2.tgz", - "integrity": "sha512-sDPHm2wfx2QhrMDK0pOt2J4KLJMAcerqWNvnED0itPRJWvI+bK+uNHzcH1dFsBlf7G3u8tqXmRF3wkvL9yUwMw==" + "version": "18.15.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==" }, "@types/prettier": { "version": "2.7.2", @@ -19347,9 +19523,9 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "@types/react": { - "version": "18.0.28", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", - "integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", + "version": "18.0.35", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.35.tgz", + "integrity": "sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -19357,9 +19533,9 @@ } }, "@types/react-native": { - "version": "0.71.3", - "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.71.3.tgz", - "integrity": "sha512-0Uqw1YZ0qbVla0MMWFTANFm6W8KYWNvGQmYfucdecbXivLMcQ2v4PovuYFKr7bE6Bc5nDCUEaga962Y8gcDF7A==", + "version": "0.71.5", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.71.5.tgz", + "integrity": "sha512-Tp5druh7DGwNDvWYH09PCE++hbH4zYz0OOvGFb3/QFIFKXgfezaT/txJeKlBkbiqs45QJzllp9S0qo0WpWyijA==", "dev": true, "requires": { "@types/react": "*" @@ -19376,9 +19552,9 @@ }, "dependencies": { "@types/react-native": { - "version": "0.70.11", - "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.11.tgz", - "integrity": "sha512-FobPtzoNPNHugBKMfzs4Li0Q9ei4tgU8SI1M5Ayg7+t5/+noCm2sknI8uwij22wMkcHcefv8RFx4q28nNVJtCQ==", + "version": "0.70.13", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.13.tgz", + "integrity": "sha512-VnC/ny8Eynk3fvY4cnNKXpo/0zUhA2gO64RX51yzVofblOP6TR6jciga0kIjI4c+2eUyWNGrahmiolNm+QU6Cw==", "dev": true, "requires": { "@types/react": "*" @@ -19387,9 +19563,9 @@ } }, "@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, "@types/semver": { "version": "7.3.13", @@ -19408,9 +19584,9 @@ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" }, "@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", "requires": { "@types/yargs-parser": "*" } @@ -19421,15 +19597,15 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "@typescript-eslint/eslint-plugin": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz", - "integrity": "sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.58.0.tgz", + "integrity": "sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/type-utils": "5.55.0", - "@typescript-eslint/utils": "5.55.0", + "@typescript-eslint/scope-manager": "5.58.0", + "@typescript-eslint/type-utils": "5.58.0", + "@typescript-eslint/utils": "5.58.0", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -19448,9 +19624,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -19524,9 +19700,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -19541,53 +19717,53 @@ } }, "@typescript-eslint/parser": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.55.0.tgz", - "integrity": "sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.58.0.tgz", + "integrity": "sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/typescript-estree": "5.55.0", + "@typescript-eslint/scope-manager": "5.58.0", + "@typescript-eslint/types": "5.58.0", + "@typescript-eslint/typescript-estree": "5.58.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz", - "integrity": "sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.58.0.tgz", + "integrity": "sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/visitor-keys": "5.55.0" + "@typescript-eslint/types": "5.58.0", + "@typescript-eslint/visitor-keys": "5.58.0" } }, "@typescript-eslint/type-utils": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz", - "integrity": "sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.58.0.tgz", + "integrity": "sha512-FF5vP/SKAFJ+LmR9PENql7fQVVgGDOS+dq3j+cKl9iW/9VuZC/8CFmzIP0DLKXfWKpRHawJiG70rVH+xZZbp8w==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.55.0", - "@typescript-eslint/utils": "5.55.0", + "@typescript-eslint/typescript-estree": "5.58.0", + "@typescript-eslint/utils": "5.58.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.55.0.tgz", - "integrity": "sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.58.0.tgz", + "integrity": "sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz", - "integrity": "sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.58.0.tgz", + "integrity": "sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==", "dev": true, "requires": { - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/visitor-keys": "5.55.0", + "@typescript-eslint/types": "5.58.0", + "@typescript-eslint/visitor-keys": "5.58.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -19605,9 +19781,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -19622,17 +19798,17 @@ } }, "@typescript-eslint/utils": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.55.0.tgz", - "integrity": "sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.58.0.tgz", + "integrity": "sha512-gAmLOTFXMXOC+zP1fsqm3VceKSBQJNzV385Ok3+yzlavNHZoedajjS4UyS21gabJYcobuigQPs/z71A9MdJFqQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/typescript-estree": "5.55.0", + "@typescript-eslint/scope-manager": "5.58.0", + "@typescript-eslint/types": "5.58.0", + "@typescript-eslint/typescript-estree": "5.58.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -19647,9 +19823,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -19664,12 +19840,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz", - "integrity": "sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.58.0.tgz", + "integrity": "sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.55.0", + "@typescript-eslint/types": "5.58.0", "eslint-visitor-keys": "^3.3.0" } }, @@ -20308,9 +20484,9 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "caniuse-lite": { - "version": "1.0.30001465", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001465.tgz", - "integrity": "sha512-HvjgL3MYAJjceTDCcjRnQGjwUz/5qec9n7JPOzUursUoOTIsYCSDOb1l7RsnZE8mjbxG78zVRCKfrBXyvChBag==" + "version": "1.0.30001478", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz", + "integrity": "sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw==" }, "chalk": { "version": "2.4.2", @@ -20420,9 +20596,9 @@ } }, "cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==" + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.8.0.tgz", + "integrity": "sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ==" }, "cliui": { "version": "8.0.1", @@ -20470,6 +20646,15 @@ "object-visit": "^1.0.0" } }, + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -20483,6 +20668,15 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", @@ -20587,9 +20781,9 @@ "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==" }, "core-js-compat": { - "version": "3.29.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.1.tgz", - "integrity": "sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==", + "version": "3.30.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.1.tgz", + "integrity": "sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw==", "requires": { "browserslist": "^4.21.5" } @@ -20647,9 +20841,9 @@ } }, "csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "dayjs": { "version": "1.11.7", @@ -20687,9 +20881,9 @@ "dev": true }, "deepmerge": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", - "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true }, "defaults": { @@ -20780,9 +20974,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.4.328", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.328.tgz", - "integrity": "sha512-DE9tTy2PNmy1v55AZAO542ui+MLC2cvINMK4P2LXGsJdput/ThVG9t+QGecPuAZZSgC8XoI+Jh9M1OG9IoNSCw==" + "version": "1.4.365", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.365.tgz", + "integrity": "sha512-FRHZO+1tUNO4TOPXmlxetkoaIY8uwHzd1kKopK/Gx2SKn1L47wJXWD44wxP5CGRyyP98z/c8e1eBzJrgPeiBOg==" }, "emittery": { "version": "0.13.1", @@ -21069,9 +21263,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -21101,9 +21295,9 @@ } }, "eslint-config-prettier": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz", - "integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", "dev": true, "requires": {} }, @@ -21246,9 +21440,9 @@ } }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", + "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", "dev": true }, "espree": { @@ -21536,9 +21730,9 @@ "dev": true }, "fast-xml-parser": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.3.tgz", - "integrity": "sha512-LsNDahCiCcJPe8NO7HijcnukHB24tKbfDDA5IILx9dmW3Frb52lhbeX6MPNUSvyGNfav2VTYpJ/OqkRoVLrh2Q==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.0.tgz", + "integrity": "sha512-+zVQv4aVTO+o8oRUyRL7PjgeVo1J6oP8Cw2+a8UTZQcj5V0yUK5T63gTN0ldgiHDPghUjKc4OpT6SwMTwnOQug==", "requires": { "strnum": "^1.0.5" } @@ -21899,9 +22093,9 @@ } }, "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "grapheme-splitter": { "version": "1.0.4", @@ -22096,9 +22290,9 @@ "integrity": "sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA==" }, "immer": { - "version": "9.0.19", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz", - "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==" + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==" }, "import-fresh": { "version": "3.3.0", @@ -22232,9 +22426,9 @@ "dev": true }, "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", + "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", "requires": { "has": "^1.0.3" } @@ -23420,9 +23614,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -23671,9 +23865,9 @@ } }, "joi": { - "version": "17.8.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.8.3.tgz", - "integrity": "sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w==", + "version": "17.9.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.1.tgz", + "integrity": "sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw==", "requires": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", @@ -24242,9 +24436,9 @@ "dev": true }, "metro": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.73.8.tgz", - "integrity": "sha512-2EMJME9w5x7Uzn+DnQ4hzWr33u/aASaOBGdpf4lxbrlk6/vl4UBfX1sru6KU535qc/0Z1BMt4Vq9qsP3ZGFmWg==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.73.9.tgz", + "integrity": "sha512-BlYbPmTF60hpetyNdKhdvi57dSqutb+/oK0u3ni4emIh78PiI0axGo7RfdsZ/mn3saASXc94tDbpC5yn7+NpEg==", "requires": { "@babel/code-frame": "^7.0.0", "@babel/core": "^7.20.0", @@ -24268,23 +24462,23 @@ "invariant": "^2.2.4", "jest-worker": "^27.2.0", "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.73.8", - "metro-cache": "0.73.8", - "metro-cache-key": "0.73.8", - "metro-config": "0.73.8", - "metro-core": "0.73.8", - "metro-file-map": "0.73.8", - "metro-hermes-compiler": "0.73.8", - "metro-inspector-proxy": "0.73.8", - "metro-minify-terser": "0.73.8", - "metro-minify-uglify": "0.73.8", - "metro-react-native-babel-preset": "0.73.8", - "metro-resolver": "0.73.8", - "metro-runtime": "0.73.8", - "metro-source-map": "0.73.8", - "metro-symbolicate": "0.73.8", - "metro-transform-plugins": "0.73.8", - "metro-transform-worker": "0.73.8", + "metro-babel-transformer": "0.73.9", + "metro-cache": "0.73.9", + "metro-cache-key": "0.73.9", + "metro-config": "0.73.9", + "metro-core": "0.73.9", + "metro-file-map": "0.73.9", + "metro-hermes-compiler": "0.73.9", + "metro-inspector-proxy": "0.73.9", + "metro-minify-terser": "0.73.9", + "metro-minify-uglify": "0.73.9", + "metro-react-native-babel-preset": "0.73.9", + "metro-resolver": "0.73.9", + "metro-runtime": "0.73.9", + "metro-source-map": "0.73.9", + "metro-symbolicate": "0.73.9", + "metro-transform-plugins": "0.73.9", + "metro-transform-worker": "0.73.9", "mime-types": "^2.1.27", "node-fetch": "^2.2.0", "nullthrows": "^1.1.1", @@ -24393,41 +24587,41 @@ } }, "metro-babel-transformer": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.73.8.tgz", - "integrity": "sha512-GO6H/W2RjZ0/gm1pIvdO9EP34s3XN6kzoeyxqmfqKfYhJmYZf1SzXbyiIHyMbJNwJVrsKuHqu32+GopTlKscWw==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.73.9.tgz", + "integrity": "sha512-DlYwg9wwYIZTHtic7dyD4BP0SDftoltZ3clma76nHu43blMWsCnrImHeHsAVne3XsQ+RJaSRxhN5nkG2VyVHwA==", "requires": { "@babel/core": "^7.20.0", "hermes-parser": "0.8.0", - "metro-source-map": "0.73.8", + "metro-source-map": "0.73.9", "nullthrows": "^1.1.1" } }, "metro-cache": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.73.8.tgz", - "integrity": "sha512-/uFbTIw813Rvb8kSAIHvax9gWl41dtgjY2SpJLNIBLdQ6oFZ3CVo3ahZIiEZOrCeHl9xfGn5tmvNb8CEFa/Q5w==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.73.9.tgz", + "integrity": "sha512-upiRxY8rrQkUWj7ieACD6tna7xXuXdu2ZqrheksT79ePI0aN/t0memf6WcyUtJUMHZetke3j+ppELNvlmp3tOw==", "requires": { - "metro-core": "0.73.8", + "metro-core": "0.73.9", "rimraf": "^3.0.2" } }, "metro-cache-key": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.73.8.tgz", - "integrity": "sha512-VzFGu4kJGIkLjyDgVoM2ZxIHlMdCZWMqVIux9N+EeyMVMvGXTiXW8eGROgxzDhVjyR58IjfMsYpRCKz5dR+2ew==" + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.73.9.tgz", + "integrity": "sha512-uJg+6Al7UoGIuGfoxqPBy6y1Ewq7Y8/YapGYIDh6sohInwt/kYKnPZgLDYHIPvY2deORnQ/2CYo4tOeBTnhCXQ==" }, "metro-config": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.73.8.tgz", - "integrity": "sha512-sAYq+llL6ZAfro64U99ske8HcKKswxX4wIZbll9niBKG7TkWm7tfMY1jO687XEmE4683rHncZeBRav9pLngIzg==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.73.9.tgz", + "integrity": "sha512-NiWl1nkYtjqecDmw77tbRbXnzIAwdO6DXGZTuKSkH+H/c1NKq1eizO8Fe+NQyFtwR9YLqn8Q0WN1nmkwM1j8CA==", "requires": { "cosmiconfig": "^5.0.5", "jest-validate": "^26.5.2", - "metro": "0.73.8", - "metro-cache": "0.73.8", - "metro-core": "0.73.8", - "metro-runtime": "0.73.8" + "metro": "0.73.9", + "metro-cache": "0.73.9", + "metro-core": "0.73.9", + "metro-runtime": "0.73.9" }, "dependencies": { "@jest/types": { @@ -24535,18 +24729,18 @@ } }, "metro-core": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.73.8.tgz", - "integrity": "sha512-Aew4dthbZf8bRRjlYGL3cnai3+LKYTf6mc7YS2xLQRWtgGZ1b/H8nQtBvXZpfRYFcS84UeEQ10vwIf5eR3qPdQ==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.73.9.tgz", + "integrity": "sha512-1NTs0IErlKcFTfYyRT3ljdgrISWpl1nys+gaHkXapzTSpvtX9F1NQNn5cgAuE+XIuTJhbsCdfIJiM2JXbrJQaQ==", "requires": { "lodash.throttle": "^4.1.1", - "metro-resolver": "0.73.8" + "metro-resolver": "0.73.9" } }, "metro-file-map": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.73.8.tgz", - "integrity": "sha512-CM552hUO9om02jJdLszOCIDADKNaaeVz8CjYXItndvgr5jmFlQYAR+UMvaDzeT8oYdAV1DXAljma2CS2UBymPg==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.73.9.tgz", + "integrity": "sha512-R/Wg3HYeQhYY3ehWtfedw8V0ne4lpufG7a21L3GWer8tafnC9pmjoCKEbJz9XZkVj9i1FtxE7UTbrtZNeIILxQ==", "requires": { "abort-controller": "^3.0.0", "anymatch": "^3.0.3", @@ -24681,14 +24875,14 @@ } }, "metro-hermes-compiler": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-hermes-compiler/-/metro-hermes-compiler-0.73.8.tgz", - "integrity": "sha512-2d7t+TEoQLk+jyXgBykmAtPPJK2B46DB3qUYIMKDFDDaKzCljrojyVuGgQq6SM1f95fe6HDAQ3K9ihTjeB90yw==" + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-hermes-compiler/-/metro-hermes-compiler-0.73.9.tgz", + "integrity": "sha512-5B3vXIwQkZMSh3DQQY23XpTCpX9kPLqZbA3rDuAcbGW0tzC3f8dCenkyBb0GcCzyTDncJeot/A7oVCVK6zapwg==" }, "metro-inspector-proxy": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.73.8.tgz", - "integrity": "sha512-F0QxwDTox0TDeXVRN7ZmI7BknBjPDVKQ1ZeKznFBiMa0SXiD1kzoksfpDbZ6hTEKrhVM9Ep0YQmC7avwZouOnA==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.73.9.tgz", + "integrity": "sha512-B3WrWZnlYhtTrv0IaX3aUAhi2qVILPAZQzb5paO1e+xrz4YZHk9c7dXv7qe7B/IQ132e3w46y3AL7rFo90qVjA==", "requires": { "connect": "^3.6.5", "debug": "^2.2.0", @@ -24718,25 +24912,25 @@ } }, "metro-minify-terser": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.73.8.tgz", - "integrity": "sha512-pnagyXAoMPhihWrHRIWqCxrP6EJ8Hfugv5RXBb6HbOANmwajn2uQuzeu18+dXaN1yPoDCMCgpg/UA4ibFN5jtQ==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.73.9.tgz", + "integrity": "sha512-MTGPu2qV5qtzPJ2SqH6s58awHDtZ4jd7lmmLR+7TXDwtZDjIBA0YVfI0Zak2Haby2SqoNKrhhUns/b4dPAQAVg==", "requires": { "terser": "^5.15.0" } }, "metro-minify-uglify": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.73.8.tgz", - "integrity": "sha512-9wZqKfraVfmtMXdOzRyan+6r1woQXqqa4KeXfVh7+Mxl+5+J0Lmw6EvTrWawsaOEpvpn32q9MfoHC1d8plDJwA==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.73.9.tgz", + "integrity": "sha512-gzxD/7WjYcnCNGiFJaA26z34rjOp+c/Ft++194Wg91lYep3TeWQ0CnH8t2HRS7AYDHU81SGWgvD3U7WV0g4LGA==", "requires": { "uglify-es": "^3.1.9" } }, "metro-react-native-babel-preset": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.8.tgz", - "integrity": "sha512-spNrcQJTbQntEIqJnCA6yL4S+dzV9fXCk7U+Rm7yJasZ4o4Frn7jP23isu7FlZIp1Azx1+6SbP7SgQM+IP5JgQ==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.9.tgz", + "integrity": "sha512-AoD7v132iYDV4K78yN2OLgTPwtAKn0XlD2pOhzyBxiI8PeXzozhbKyPV7zUOJUPETj+pcEVfuYj5ZN/8+bhbCw==", "requires": { "@babel/core": "^7.20.0", "@babel/plugin-proposal-async-generator-functions": "^7.0.0", @@ -24779,47 +24973,47 @@ } }, "metro-react-native-babel-transformer": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.8.tgz", - "integrity": "sha512-oH/LCCJPauteAE28c0KJAiSrkV+1VJbU0PwA9UwaWnle+qevs/clpKQ8LrIr33YbBj4CiI1kFoVRuNRt5h4NFg==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.9.tgz", + "integrity": "sha512-DSdrEHuQ22ixY7DyipyKkIcqhOJrt5s6h6X7BYJCP9AMUfXOwLe2biY3BcgJz5GOXv8/Akry4vTCvQscVS1otQ==", "requires": { "@babel/core": "^7.20.0", "babel-preset-fbjs": "^3.4.0", "hermes-parser": "0.8.0", - "metro-babel-transformer": "0.73.8", - "metro-react-native-babel-preset": "0.73.8", - "metro-source-map": "0.73.8", + "metro-babel-transformer": "0.73.9", + "metro-react-native-babel-preset": "0.73.9", + "metro-source-map": "0.73.9", "nullthrows": "^1.1.1" } }, "metro-resolver": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.73.8.tgz", - "integrity": "sha512-GiBWont7/OgAftkkj2TiEp+Gf1PYZUk8xV4MbtnQjIKyy3MlGY3GbpMQ1BHih9GUQqlF0n9jsUlC2K5P0almXQ==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.73.9.tgz", + "integrity": "sha512-Ej3wAPOeNRPDnJmkK0zk7vJ33iU07n+oPhpcf5L0NFkWneMmSM2bflMPibI86UjzZGmRfn0AhGhs8yGeBwQ/Xg==", "requires": { "absolute-path": "^0.0.0" } }, "metro-runtime": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.73.8.tgz", - "integrity": "sha512-M+Bg9M4EN5AEpJ8NkiUsawD75ifYvYfHi05w6QzHXaqOrsTeaRbbeLuOGCYxU2f/tPg17wQV97/rqUQzs9qEtA==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.73.9.tgz", + "integrity": "sha512-d5Hs83FpKB9r8q8Vb95+fa6ESpwysmPr4lL1I2rM2qXAFiO7OAPT9Bc23WmXgidkBtD0uUFdB2lG+H1ATz8rZg==", "requires": { "@babel/runtime": "^7.0.0", "react-refresh": "^0.4.0" } }, "metro-source-map": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.73.8.tgz", - "integrity": "sha512-wozFXuBYMAy7b8BCYwC+qoXsvayVJBHWtSTlSLva99t+CoUSG9JO9kg1umzbOz28YYPxKmvb/wbnLMkHdas2cA==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.73.9.tgz", + "integrity": "sha512-l4VZKzdqafipriETYR6lsrwtavCF1+CMhCOY9XbyWeTrpGSNgJQgdeJpttzEZTHQQTLR0csQo0nD1ef3zEP6IQ==", "requires": { "@babel/traverse": "^7.20.0", "@babel/types": "^7.20.0", "invariant": "^2.2.4", - "metro-symbolicate": "0.73.8", + "metro-symbolicate": "0.73.9", "nullthrows": "^1.1.1", - "ob1": "0.73.8", + "ob1": "0.73.9", "source-map": "^0.5.6", "vlq": "^1.0.0" }, @@ -24832,12 +25026,12 @@ } }, "metro-symbolicate": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.73.8.tgz", - "integrity": "sha512-xkBAcceYYp0GGdCCuMzkCF1ejHsd0lYlbKBkjSRgM0Nlj80VapPaSwumYoAvSaDxcbkvS7/sCjURGp5DsSFgRQ==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.73.9.tgz", + "integrity": "sha512-4TUOwxRHHqbEHxRqRJ3wZY5TA8xq7AHMtXrXcjegMH9FscgYztsrIG9aNBUBS+VLB6g1qc6BYbfIgoAnLjCDyw==", "requires": { "invariant": "^2.2.4", - "metro-source-map": "0.73.8", + "metro-source-map": "0.73.9", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "through2": "^2.0.1", @@ -24852,9 +25046,9 @@ } }, "metro-transform-plugins": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.73.8.tgz", - "integrity": "sha512-IxjlnB5eA49M0WfvPEzvRikK3Rr6bECUUfcZt/rWpSphq/mttgyLYcHQ+VTZZl0zHolC3cTLwgoDod4IIJBn1A==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.73.9.tgz", + "integrity": "sha512-r9NeiqMngmooX2VOKLJVQrMuV7PAydbqst5bFhdVBPcFpZkxxqyzjzo+kzrszGy2UpSQBZr2P1L6OMjLHwQwfQ==", "requires": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.0", @@ -24864,22 +25058,22 @@ } }, "metro-transform-worker": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.73.8.tgz", - "integrity": "sha512-B8kR6lmcvyG4UFSF2QDfr/eEnWJvg0ZadooF8Dg6m/3JSm9OAqfSoC0YrWqAuvtWImNDnbeKWN7/+ns44Hv6tg==", + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.73.9.tgz", + "integrity": "sha512-Rq4b489sIaTUENA+WCvtu9yvlT/C6zFMWhU4sq+97W29Zj0mPBjdk+qGT5n1ZBgtBIJzZWt1KxeYuc17f4aYtQ==", "requires": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.0", "@babel/parser": "^7.20.0", "@babel/types": "^7.20.0", "babel-preset-fbjs": "^3.4.0", - "metro": "0.73.8", - "metro-babel-transformer": "0.73.8", - "metro-cache": "0.73.8", - "metro-cache-key": "0.73.8", - "metro-hermes-compiler": "0.73.8", - "metro-source-map": "0.73.8", - "metro-transform-plugins": "0.73.8", + "metro": "0.73.9", + "metro-babel-transformer": "0.73.9", + "metro-cache": "0.73.9", + "metro-cache-key": "0.73.9", + "metro-hermes-compiler": "0.73.9", + "metro-source-map": "0.73.9", + "metro-transform-plugins": "0.73.9", "nullthrows": "^1.1.1" } }, @@ -24956,11 +25150,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" - }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -25062,9 +25251,9 @@ "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" }, "ob1": { - "version": "0.73.8", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.73.8.tgz", - "integrity": "sha512-1F7j+jzD+edS6ohQP7Vg5f3yiIk5i3x1uLrNIHOmLHWzWK1t3zrDpjnoXghccdVlsU+UjbyURnDynm4p0GgXeA==" + "version": "0.73.9", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.73.9.tgz", + "integrity": "sha512-kHOzCOFXmAM26fy7V/YuXNKne2TyRiXbFAvPBIbuedJCZZWQZHLdPzMeXJI4Egt6IcfDttRzN3jQ90wOwq1iNw==" }, "object-assign": { "version": "4.1.1", @@ -25467,9 +25656,9 @@ "dev": true }, "prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", "dev": true }, "prettier-linter-helpers": { @@ -25481,6 +25670,13 @@ "fast-diff": "^1.1.2" } }, + "prettier-plugin-organize-imports": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.2.tgz", + "integrity": "sha512-e97lE6odGSiHonHJMTYC0q0iLXQyw0u5z/PJpvP/3vRy6/Zi9kLBwFAbEGjDzIowpjQv8b+J04PDamoUSQbzGA==", + "dev": true, + "requires": {} + }, "pretty-format": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", @@ -25584,9 +25780,9 @@ } }, "react-devtools-core": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.27.2.tgz", - "integrity": "sha512-8SzmIkpO87alD7Xr6gWIEa1jHkMjawOZ+6egjazlnjB4UUcbnzGDf/vBJ4BzGuWWEM+pzrxuzsPpcMqlQkYK2g==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.27.4.tgz", + "integrity": "sha512-dvZjrAJjahd6NNl7dDwEk5TyHsWJxDpYL7VnD9jdEr98EEEsVhw9G8JDX54Nrb3XIIOBlJDpjo3AuBuychX9zg==", "requires": { "shell-quote": "^1.6.1", "ws": "^7" @@ -25606,14 +25802,14 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "react-native": { - "version": "0.71.4", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.71.4.tgz", - "integrity": "sha512-3hSYqvWrOdKhpV3HpEKp1/CkWx8Sr/N/miCrmUIAsVTSJUR7JW0VvIsrV9urDhUj/s6v2WF4n7qIEEJsmTCrPw==", + "version": "0.71.6", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.71.6.tgz", + "integrity": "sha512-gHrDj7qaAaiE41JwaFCh3AtvOqOLuRgZtHKzNiwxakG/wvPAYmG73ECfWHGxjxIx/QT17Hp37Da3ipCei/CayQ==", "requires": { "@jest/create-cache-key-function": "^29.2.1", - "@react-native-community/cli": "10.2.0", + "@react-native-community/cli": "10.2.2", "@react-native-community/cli-platform-android": "10.2.0", - "@react-native-community/cli-platform-ios": "10.2.0", + "@react-native-community/cli-platform-ios": "10.2.1", "@react-native/assets": "1.0.0", "@react-native/normalize-color": "2.1.0", "@react-native/polyfills": "2.0.0", @@ -25626,16 +25822,16 @@ "jest-environment-node": "^29.2.1", "jsc-android": "^250231.0.0", "memoize-one": "^5.0.0", - "metro-react-native-babel-transformer": "0.73.8", - "metro-runtime": "0.73.8", - "metro-source-map": "0.73.8", + "metro-react-native-babel-transformer": "0.73.9", + "metro-runtime": "0.73.9", + "metro-source-map": "0.73.9", "mkdirp": "^0.5.1", "nullthrows": "^1.1.1", "pretty-format": "^26.5.2", "promise": "^8.3.0", "react-devtools-core": "^4.26.1", "react-native-codegen": "^0.71.5", - "react-native-gradle-plugin": "^0.71.16", + "react-native-gradle-plugin": "^0.71.17", "react-refresh": "^0.4.0", "react-shallow-renderer": "^16.15.0", "regenerator-runtime": "^0.13.2", @@ -25759,14 +25955,24 @@ } }, "react-native-gradle-plugin": { - "version": "0.71.16", - "resolved": "https://registry.npmjs.org/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.16.tgz", - "integrity": "sha512-H2BjG2zk7B7Wii9sXvd9qhCVRQYDAHSWdMw9tscmZBqSP62DkIWEQSk4/B2GhQ4aK9ydVXgtqR6tBeg3yy8TSA==" + "version": "0.71.17", + "resolved": "https://registry.npmjs.org/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.17.tgz", + "integrity": "sha512-OXXYgpISEqERwjSlaCiaQY6cTY5CH6j73gdkWpK0hedxtiWMWgH+i5TOi4hIGYitm9kQBeyDu+wim9fA8ROFJA==" + }, + "react-native-paper": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.6.0.tgz", + "integrity": "sha512-X/MhAN/k1iwsp8N42MoSUfGbgCt4Lw8IlsoToIPoSoYRxbEq2OYOPRH7JsktPtFY0a1SAJPqVHFFS76FU8eSFg==", + "requires": { + "@callstack/react-theme-provider": "^3.0.8", + "color": "^3.1.2", + "use-latest-callback": "^0.1.5" + } }, "react-native-reanimated": { - "version": "2.14.4", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-2.14.4.tgz", - "integrity": "sha512-DquSbl7P8j4SAmc+kRdd75Ianm8G+IYQ9T4AQ6lrpLVeDkhZmjWI0wkutKWnp6L7c5XNVUrFDUf69dwETLCItQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.0.2.tgz", + "integrity": "sha512-8Et90yTI9yxchGbDP79k391XZqc/64zNbASbGy8X3Vgv4EbZ1M3IkKwcIbZmbVwpA804VJ6V9nJAGUh9fP0LrA==", "requires": { "@babel/plugin-transform-object-assign": "^7.16.7", "@babel/preset-typescript": "^7.16.7", @@ -25777,6 +25983,12 @@ "string-hash-64": "^1.0.3" } }, + "react-native-safe-area-context": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.5.1.tgz", + "integrity": "sha512-bKcwk6zZvyz+VLoG6Uia1oiYU1jSbv1ysjEKSRLsLtPcDsbixsTc0UgfrPqjZxNTPzvYLMcr8ufA90UQauN4mw==", + "requires": {} + }, "react-native-vector-icons": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz", @@ -25906,6 +26118,14 @@ "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", "requires": {} }, + "redux-saga": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.2.3.tgz", + "integrity": "sha512-HDe0wTR5nhd8Xr5xjGzoyTbdAw6rjy1GDplFt3JKtKN8/MnkQSRqK/n6aQQhpw5NI4ekDVOaW+w4sdxPBaCoTQ==", + "requires": { + "@redux-saga/core": "^1.2.3" + } + }, "redux-thunk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", @@ -26024,11 +26244,11 @@ "integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==" }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz", + "integrity": "sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==", "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.12.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -26054,9 +26274,9 @@ "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==" }, "resolve.exports": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.1.tgz", - "integrity": "sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true }, "restore-cursor": { @@ -26274,9 +26494,9 @@ "dev": true }, "shell-quote": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.0.tgz", - "integrity": "sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==" + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==" }, "side-channel": { "version": "1.0.4", @@ -26294,6 +26514,21 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -26870,9 +27105,9 @@ } }, "terser": { - "version": "5.16.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.6.tgz", - "integrity": "sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==", + "version": "5.16.9", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.9.tgz", + "integrity": "sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg==", "requires": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -27014,9 +27249,9 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "ts-jest": { - "version": "29.0.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz", - "integrity": "sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==", + "version": "29.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz", + "integrity": "sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==", "dev": true, "requires": { "bs-logger": "0.x", @@ -27039,9 +27274,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", + "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -27114,6 +27349,27 @@ "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true }, + "typescript-compare": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", + "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==", + "requires": { + "typescript-logic": "^0.0.0" + } + }, + "typescript-logic": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz", + "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==" + }, + "typescript-tuple": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz", + "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==", + "requires": { + "typescript-compare": "^0.0.2" + } + }, "uglify-es": { "version": "3.3.9", "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", @@ -27231,9 +27487,9 @@ } }, "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "requires": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -27258,6 +27514,12 @@ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, + "use-latest-callback": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.6.tgz", + "integrity": "sha512-VO/P91A/PmKH9bcN9a7O3duSuxe6M14ZoYXgA6a8dab8doWNdhiIHzEkX/jFeTTRBsX0Ubk6nG4q2NIjNsj+bg==", + "requires": {} + }, "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", diff --git a/package.json b/package.json index 1f7c298..3e328a7 100644 --- a/package.json +++ b/package.json @@ -15,18 +15,20 @@ "format": "prettier --write ." }, "dependencies": { - "@gorhom/bottom-sheet": "^4.4.5", "@react-native-async-storage/async-storage": "^1.17.11", "@react-native-community/datetimepicker": "^6.7.5", "@reduxjs/toolkit": "^1.9.1", "react": "18.2.0", - "react-native": "0.71.4", + "react-native": "0.71.6", "react-native-draggable-flatlist": "^4.0.0", "react-native-gesture-handler": "^2.9.0", - "react-native-reanimated": "^2.14.4", + "react-native-paper": "^5.4.1", + "react-native-reanimated": "^3.0.2", + "react-native-safe-area-context": "^4.5.0", "react-native-vector-icons": "^9.2.0", "react-redux": "^8.0.5", - "redux-persist": "^6.0.0" + "redux-persist": "^6.0.0", + "redux-saga": "^1.2.3" }, "devDependencies": { "@babel/core": "^7.20.12", @@ -49,8 +51,9 @@ "eslint-config-prettier": "^8.5.0", "eslint-plugin-react-native": "^4.0.0", "jest": "^29.2.1", - "metro-react-native-babel-preset": "0.73.8", + "metro-react-native-babel-preset": "0.73.9", "prettier": "^2.4.1", + "prettier-plugin-organize-imports": "^3.2.2", "ts-jest": "^29.0.5", "typescript": "4.9.4" }, diff --git a/src/App.tsx b/src/App.tsx index e81059f..075efcc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,21 +1,14 @@ -// React import React, { useEffect } from 'react' -// React Native import { StatusBar } from 'react-native' -// Redux +import { GestureHandlerRootView } from 'react-native-gesture-handler' +import { Provider as PaperProvider } from 'react-native-paper' +import { Provider as StoreProvider } from 'react-redux' import { PersistGate } from 'redux-persist/integration/react' -import { Provider } from 'react-redux' -// Contexts wrappers -import GlobalContextWrapper from './contexts/GlobalContextWrapper' +import AppItemMenu from './components/AppItemMenu' +import Settings from './components/Settings/Settings' import SearchContextWrapper from './contexts/SearchContextWrapper' -// Components import Home from './Home' -import AppItemMenu from './components/AppItemMenu' -import Settings from './components/Settings/SettingsBottomSheet' -// State import { persistor, store } from './store' -// Gesture Handler -import { GestureHandlerRootView } from 'react-native-gesture-handler' const App = () => { useEffect(() => { @@ -24,19 +17,19 @@ const App = () => { }, []) return ( - + - - + + - - + + - + ) } diff --git a/src/Home.tsx b/src/Home.tsx index 16e3033..a7f7bf8 100644 --- a/src/Home.tsx +++ b/src/Home.tsx @@ -1,52 +1,36 @@ -// React import React, { useContext, useEffect, useState } from 'react' -// React Native -import { View, StyleSheet, StatusBar } from 'react-native' -// Redux +import { StatusBar, StyleSheet, View } from 'react-native' import { useDispatch } from 'react-redux' -import { setAppsList } from './slices/appsList' -import { removeRecentApp } from './slices/recentApps' -import { removeFavoriteApp } from './slices/favoriteApps' -// Components -import TopContainer from './containers/TopContainer' import BottomContainer from './containers/BottomContainer' -// Contexts -import GlobalContext from './contexts/GlobalContext' +import TopContainer from './containers/TopContainer' import SearchContext from './contexts/SearchContext' -// Custom hooks import { useBackHandler } from './hooks/useBackHandler' import { usePackageChange } from './hooks/usePackageChange' -// Native modules -import AppsModule from './native-modules/AppsModule' -// Models -import { AppDetails } from './models/app-details' import { PackageChange } from './models/event' +import { appRemovedAction, getAppsListAction } from './slices/appsList' +import { setDisplayAllApps } from './slices/appState' -const initialLoadValue = 'INITIAL_LOAD' +const initialLoadPackageName = 'INITIAL_LOAD' const packageChangedInitialValue = { - packageName: initialLoadValue, + packageName: initialLoadPackageName, isRemoved: false, } const Home = () => { const dispatch = useDispatch() const [packageChanged, setPackageChanged] = useState(packageChangedInitialValue) - const { hideAllApps } = useContext(GlobalContext) const { searchInputRef } = useContext(SearchContext) useEffect(() => { - if (packageChanged.isRemoved && packageChanged.packageName !== initialLoadValue) { - dispatch(removeRecentApp(packageChanged.packageName)) - dispatch(removeFavoriteApp(packageChanged.packageName)) + if (packageChanged.isRemoved && packageChanged.packageName !== initialLoadPackageName) { + dispatch(appRemovedAction(packageChanged.packageName)) } - AppsModule.getApplications((applications: string) => { - dispatch(setAppsList(JSON.parse(applications) as AppDetails[])) - }) + dispatch(getAppsListAction()) }, [packageChanged]) useBackHandler(() => { - hideAllApps() + dispatch(setDisplayAllApps(false)) if (searchInputRef?.current?.isFocused()) searchInputRef?.current?.blur() // TODO: Read more about the return here: https://github.com/react-native-community/hooks#usebackhandler diff --git a/src/components/AllApps.tsx b/src/components/AllApps.tsx index 5aa8e1f..f3a067b 100644 --- a/src/components/AllApps.tsx +++ b/src/components/AllApps.tsx @@ -1,61 +1,43 @@ -// React -import React, { MutableRefObject, useRef } from 'react' -// React Native +import React, { MutableRefObject, useCallback, useRef } from 'react' import { FlatList, ListRenderItem, ListRenderItemInfo, StyleSheet } from 'react-native' -// Components -import AppItem from './AppItem' -import CustomView from './CustomView' -import AllAppsLetterIndex from './AllAppsLetterIndex' -// Redux +import Animated, { SlideInDown } from 'react-native-reanimated' import { useSelector } from 'react-redux' -import { selectAppsListMemoized } from '../slices/appsList' -// Reanimated -import { SlideInDown } from 'react-native-reanimated' -// Constants -import { APP_ITEM_HEIGHT_ICON_DISPLAYED, BACKGROUND_COLOR } from '../constants' -// Models -import { RenderedIn } from '../models/rendered-in' +import { BACKGROUND_COLOR } from '../constants' import { AppDetails } from '../models/app-details' - -const keyExtractor = ({ name }: AppDetails) => name -const getItemLayout = (_data: unknown, index: number) => ({ - length: APP_ITEM_HEIGHT_ICON_DISPLAYED, - offset: APP_ITEM_HEIGHT_ICON_DISPLAYED * index, - index, -}) +import { RenderedIn } from '../models/rendered-in' +import { selectAppsListMemoized } from '../slices/appsList' +import { getListItemLayout, getListKey } from '../utils/apps' +import AllAppsLetterIndex from './AllAppsLetterIndex' +import AppItem from './AppItem' const AllApps = () => { const apps = useSelector(selectAppsListMemoized) const listRef: MutableRefObject | null> = useRef(null) - const scrollToIndex = (index: number) => { - const currentListRef = listRef.current as FlatList - currentListRef.scrollToIndex({ index, animated: true }) - } + const scrollToIndex = useCallback( + (index: number) => listRef.current?.scrollToIndex({ index, animated: true }), + [listRef] + ) const renderItem: ListRenderItem = ({ item }: ListRenderItemInfo) => ( - + ) return ( - + - + ) } diff --git a/src/components/AllAppsIcon.test.tsx b/src/components/AllAppsIcon.test.tsx index f25d329..16a956e 100644 --- a/src/components/AllAppsIcon.test.tsx +++ b/src/components/AllAppsIcon.test.tsx @@ -1,10 +1,16 @@ -import React from 'react' import { fireEvent, screen } from '@testing-library/react-native' -import { defaultGlobalContextValue } from '../../utils/test/data' +import React from 'react' import { renderWithProvider, renderWithProviderAndContexts } from '../../utils/test/utils' -import { GlobalContextType } from '../models/context' +import { toogleAllApps } from '../slices/appState' import AllAppsIcon from './AllAppsIcon' +const useDispatchMock = jest.fn() + +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useDispatch: () => useDispatchMock, +})) + describe(' Tests', () => { it('should render correctly and match snapshot', () => { renderWithProvider() @@ -14,17 +20,7 @@ describe(' Tests', () => { }) it('should call function to toggle all apps display when pressed', () => { - const toggleDisplayAllAppsFn = jest.fn() - - const customGlobalContextValue: GlobalContextType = { - ...defaultGlobalContextValue, - displayAllApps: false, - toggleDisplayAllApps: toggleDisplayAllAppsFn, - } - - renderWithProviderAndContexts(, { - globalContextValue: customGlobalContextValue, - }) + renderWithProviderAndContexts(, {}) const allAppsButton = screen.getByTestId('all-apps-toggle-button') @@ -32,6 +28,6 @@ describe(' Tests', () => { fireEvent.press(allAppsButton) - expect(toggleDisplayAllAppsFn).toBeCalled() + expect(useDispatchMock).toBeCalledWith(toogleAllApps()) }) }) diff --git a/src/components/AllAppsIcon.tsx b/src/components/AllAppsIcon.tsx index 2f0f803..982956a 100644 --- a/src/components/AllAppsIcon.tsx +++ b/src/components/AllAppsIcon.tsx @@ -1,24 +1,26 @@ -// React -import React, { useContext } from 'react' -// React Native -import { Pressable, View } from 'react-native' -// Components -import CustomIcon from './shared/CustomIcon' -// Contexts -import GlobalContext from '../contexts/GlobalContext' -// Constants +import React from 'react' +import { IconButton } from 'react-native-paper' +import { useDispatch } from 'react-redux' import { PRIMARY_COLOR } from '../constants' -import { iconsStyle, iconsPressableConfig } from '../shared/bottom-container' +import { iconButtonStyle } from '../shared/bottom-container' +import { toogleAllApps } from '../slices/appState' const AllAppsIcon = () => { - const { toggleDisplayAllApps } = useContext(GlobalContext) + const dispatch = useDispatch() + + const _toogleAllApps = () => { + dispatch(toogleAllApps()) + } return ( - - - - - + ) } diff --git a/src/components/AllAppsLetterIndex.test.tsx b/src/components/AllAppsLetterIndex.test.tsx index 0e46b9d..fba35b4 100644 --- a/src/components/AllAppsLetterIndex.test.tsx +++ b/src/components/AllAppsLetterIndex.test.tsx @@ -1,8 +1,9 @@ -import React from 'react' import { fireEvent, screen } from '@testing-library/react-native' +import React from 'react' +import { initialStoreState } from '../../utils/test/data' import { renderWithProvider } from '../../utils/test/utils' +import { RootState } from '../store' import AllAppsLetterIndex from './AllAppsLetterIndex' -import { initialStoreState } from '../../utils/test/data' describe(' Tests', () => { const onPressFn = jest.fn() @@ -16,13 +17,14 @@ describe(' Tests', () => { }) it('should render correctly and match snapshot - populated list', () => { - const customInitialState = { + const customInitialState: RootState = { ...initialStoreState, appsList: { list: [ { - name: 'com.google.chrome', - label: 'Chrome', + packageName: 'com.google.chrome', + name: 'Chrome', + icon: 'ICON', }, ], }, @@ -44,17 +46,19 @@ describe(' Tests', () => { }) it('should call passed function when element is pressed', () => { - const customInitialState = { + const customInitialState: RootState = { ...initialStoreState, appsList: { list: [ { - name: 'com.google.chrome', - label: 'Chrome', + packageName: 'com.google.chrome', + name: 'Chrome', + icon: 'ICON', }, { - name: 'com.google.maps', - label: 'Maps', + packageName: 'com.google.maps', + name: 'Maps', + icon: 'ICON', }, ], }, diff --git a/src/components/AllAppsLetterIndex.tsx b/src/components/AllAppsLetterIndex.tsx index e566ece..90b0e69 100644 --- a/src/components/AllAppsLetterIndex.tsx +++ b/src/components/AllAppsLetterIndex.tsx @@ -1,22 +1,22 @@ -// React import React from 'react' -// React Native import { - Text, - View, FlatList, - Pressable, - StyleSheet, ListRenderItem, ListRenderItemInfo, + Pressable, PressableAndroidRippleConfig, + StyleSheet, + Text, + View, } from 'react-native' -// Redux import { useSelector } from 'react-redux' -import { selectAppsLetterListMemoized } from '../slices/appsList' -// Models +import { WHITE_COLOR } from '../constants' import { AppLetterIndex } from '../models/list-letter-index' -import { AllAppsLetterIndexProps as Props } from '../models/props' +import { selectAppsLetterListMemoized } from '../slices/appsList' + +type Props = { + onPress: (letterIndex: number) => void +} const rippleConfig: PressableAndroidRippleConfig = { borderless: false, @@ -72,7 +72,7 @@ const styles = StyleSheet.create({ justifyContent: 'center', }, letterIndexLabel: { - color: '#fff', + color: WHITE_COLOR, fontSize: 12, fontWeight: '600', textShadowColor: 'rgba(0, 0, 0, 0.75)', diff --git a/src/components/AppItem.tsx b/src/components/AppItem.tsx index 6381d64..4cc1879 100644 --- a/src/components/AppItem.tsx +++ b/src/components/AppItem.tsx @@ -1,90 +1,85 @@ -// React -import React, { useContext, useEffect, useMemo, useState } from 'react' -// React Native -import { Image, Pressable, StyleSheet, View, StyleProp, ViewStyle } from 'react-native' -// Components -import HighlightText from './HighlightText' -// Redux +import React, { useContext } from 'react' +import { Pressable, StyleProp, StyleSheet, View, ViewStyle } from 'react-native' +import { List } from 'react-native-paper' import { useDispatch } from 'react-redux' -import { addRecentApp } from '../slices/recentApps' -import { resetAppsSearchState } from '../slices/appsSearch' -// Utils -import { launchApp } from '../utils/apps-module' -// Native modules -import AppsModule from '../native-modules/AppsModule' -// Contexts +import { APP_ITEM_HEIGHT, PRESSABLE_RIPPLE_COLOR } from '../constants' import SearchContext from '../contexts/SearchContext' -import GlobalContext from '../contexts/GlobalContext' -// Models +import { AppDetails } from '../models/app-details' import { RenderedIn } from '../models/rendered-in' -import { AppItemProps as Props } from '../models/props' +import { appLaunch, setDisplayAppMenu, setMenuAppDetails } from '../slices/appState' +import { launchApp } from '../utils/apps-module' +import HighlightText from './HighlightText' +import AppIcon from './shared/AppIcon' -const AppItem = ({ appDetails, renderedIn, appIcon, wrapperStyle, pressableStyle }: Props) => { +type Props = { + appDetails: AppDetails + renderedIn: RenderedIn + listItemStyle?: StyleProp +} + +const AppItem = ({ appDetails, renderedIn }: Props) => { const dispatch = useDispatch() - const [icon, setIcon] = useState(undefined) - const { searchAppLaunchProcedure, searchInputRef } = useContext(SearchContext) - const { setAppItemMenuDetails, displayAppItemMenuBottomSheet, globalAppLaunchProcedure } = useContext(GlobalContext) + const { searchAppLaunchProcedure } = useContext(SearchContext) + + const displayLabel = ![RenderedIn.PINNED_APPS, RenderedIn.FAVORITE_APPS].includes(renderedIn) const onPress = () => { - // Reset views and values - globalAppLaunchProcedure() + launchApp(appDetails.packageName) + + // Reset app state searchAppLaunchProcedure() - searchInputRef?.current?.clear() - dispatch(resetAppsSearchState()) - // Add app to recent apps list - if (renderedIn === RenderedIn.FILTERED_APPS || renderedIn === RenderedIn.ALL_APPS) { - dispatch(addRecentApp({ ...appDetails, icon })) - } - // Launch app - launchApp(appDetails.name) + // Clean up state and launch app + dispatch(appLaunch({ renderedIn, appDetails })) } - const displayLabel = useMemo( - () => renderedIn !== RenderedIn.FAVORITE_APPS && renderedIn !== RenderedIn.PINNED_APPS, - [renderedIn] - ) - const onLongPress = () => { - setAppItemMenuDetails({ ...appDetails, icon }) - displayAppItemMenuBottomSheet() + dispatch(setMenuAppDetails(appDetails)) + dispatch(setDisplayAppMenu(true)) } - const pressableStyles = ({ pressed }: { pressed: boolean }): StyleProp => { - return [{ backgroundColor: pressed ? 'rgba(255, 255, 255, .25)' : 'transparent' }, styles.pressable, pressableStyle] - } + const getAppIconElement = () => + const getAppTitle = () => - useEffect(() => { - if ( - renderedIn === RenderedIn.RECENT_APPS || - renderedIn === RenderedIn.FAVORITE_APPS || - renderedIn === RenderedIn.PINNED_APPS - ) { - setIcon(appIcon) - } else if (renderedIn === RenderedIn.ALL_APPS || renderedIn === RenderedIn.FILTERED_APPS) { - AppsModule.getApplicationIcon(appDetails.name, (nativeAppIcon: string) => setIcon(nativeAppIcon)) - } - }, []) + if (displayLabel) { + return ( + + ) + } return ( - - - - {displayLabel && } + + + {getAppIconElement()} ) } const styles = StyleSheet.create({ + listItem: { + paddingLeft: 10, + paddingVertical: 0, + height: APP_ITEM_HEIGHT, + alignItems: 'center', + justifyContent: 'center', + }, pressable: { padding: 5, + }, + pressableWrapper: { borderRadius: 5, - alignItems: 'center', - flexDirection: 'row', + height: APP_ITEM_HEIGHT, }, icon: { width: 50, diff --git a/src/components/AppItemMenu.tsx b/src/components/AppItemMenu.tsx index 95f59f3..e82e15f 100644 --- a/src/components/AppItemMenu.tsx +++ b/src/components/AppItemMenu.tsx @@ -1,54 +1,36 @@ -// React -import React, { useContext, useEffect, useState } from 'react' -// React Native -import { View, Text, Pressable, StyleSheet, PressableAndroidRippleConfig, Image } from 'react-native' -// Components -import SettingsItemLabel from './Settings/shared/SettingsItemLabel' -import CustomIcon from './shared/CustomIcon' -// Redux +import React, { useEffect, useState } from 'react' +import { StyleSheet, Text, View } from 'react-native' +import { IconButton, Modal, Portal, ToggleButton } from 'react-native-paper' import { useDispatch, useSelector } from 'react-redux' -import { resetAppsSearchState } from '../slices/appsSearch' import { - selectDisplayFavoriteAppsMemoized, - selectDisplayPinnedAppsMemoized, - selectDisplayTemporaryPinnedAppsMemoized, -} from '../slices/preferences' + BOTTOM_CONTAINER_HEIGHT_WITH_PADDINGS, + DISABLED_COLOR, + NEUTRAL_COLOR, + PRIMARY_COLOR, + WHITE_COLOR, +} from '../constants' +import { resetAppsSearchState, selectDisplayAppMenu, selectMenuAppDetails, setDisplayAppMenu } from '../slices/appState' import { - selectFavoriteAppsMemoized, addFavoriteApp, removeFavoriteApp, selectFavoriteAppsCountMemoized, + selectFavoriteAppsMemoized, } from '../slices/favoriteApps' import { - updateOrRemovePinnedApp, - selectTemporaryPinnedAppsMemoized, - selectAllPinnedAppsMemoized, + addPinnedApp, + removePinnedApp, selectPinnedAppsMemoized, + selectTemporaryPinnedAppsMemoized, } from '../slices/pinnedApps' -// Contexts -import GlobalContext from '../contexts/GlobalContext' -// Bottom sheet -import { BottomSheetModalProvider, BottomSheetModal } from '@gorhom/bottom-sheet' -// Utils +import { + selectDisplayFavoriteAppsMemoized, + selectDisplayPinnedAppsMemoized, + selectDisplayTemporaryPinnedAppsMemoized, +} from '../slices/preferences' +import { getAppIndex } from '../utils/apps' import { requestAppUninstall, showAppDetails } from '../utils/apps-module' -import { getPinnedAppByName } from '../utils/apps' -// Model -import { FavoriteApp } from '../models/favorite-app' -import { PinnedApp } from '../models/pinned-app' -import { AppDetailsWithIcon } from '../models/app-details' - -const appInfoIconRippleConfig: PressableAndroidRippleConfig = { - borderless: true, - foreground: true, - color: '#e5e5e5', - radius: 18, -} - -const settingItemButtonRippleConfig: PressableAndroidRippleConfig = { - borderless: false, - foreground: true, - color: '#e5e5e5', -} +import AppIcon from './shared/AppIcon' +import CustomIcon from './shared/CustomIcon' const AppItemMenu = () => { const dispatch = useDispatch() @@ -56,222 +38,207 @@ const AppItemMenu = () => { const [isPinnedApp, setIsPinnedApp] = useState(false) const [isTemporarilyPinnedApp, setIsTemporarilyPinnedApp] = useState(false) const favoriteApps = useSelector(selectFavoriteAppsMemoized) - const allPinnedApps = useSelector(selectAllPinnedAppsMemoized) const pinnedApps = useSelector(selectPinnedAppsMemoized) const temporaryPinnedApps = useSelector(selectTemporaryPinnedAppsMemoized) const favoriteAppsCount = useSelector(selectFavoriteAppsCountMemoized) const displayFavoriteAppsValue = useSelector(selectDisplayFavoriteAppsMemoized) const displayPinnedAppsValue = useSelector(selectDisplayPinnedAppsMemoized) const displayTemporaryPinnedApps = useSelector(selectDisplayTemporaryPinnedAppsMemoized) - const { appItemMenuBottomSheetRef, appItemMenuDetails } = useContext(GlobalContext) - useEffect(() => { - if (!appItemMenuDetails) return - - const appIndex = favoriteApps.findIndex(({ name }: FavoriteApp) => name === appItemMenuDetails.name) - setIsFavoriteApp(appIndex !== -1) - }, [appItemMenuDetails, favoriteApps]) + const appDetails = useSelector(selectMenuAppDetails) + const displayAppMenu = useSelector(selectDisplayAppMenu) useEffect(() => { - if (!appItemMenuDetails) return + if (!appDetails) return - const appIndex = pinnedApps.findIndex(({ name }: PinnedApp) => name === appItemMenuDetails.name) - setIsPinnedApp(appIndex !== -1) - }, [appItemMenuDetails, pinnedApps]) - - useEffect(() => { - if (!appItemMenuDetails) return + setIsPinnedApp(getAppIndex(pinnedApps, appDetails.packageName) !== -1) + setIsFavoriteApp(getAppIndex(favoriteApps, appDetails.packageName) !== -1) + setIsTemporarilyPinnedApp(getAppIndex(temporaryPinnedApps, appDetails.packageName) !== -1) + }, [appDetails, favoriteApps, pinnedApps, temporaryPinnedApps]) - const appIndex = temporaryPinnedApps.findIndex(({ name }: PinnedApp) => name === appItemMenuDetails.name) - setIsTemporarilyPinnedApp(appIndex !== -1) - }, [appItemMenuDetails, temporaryPinnedApps]) + if (!appDetails) return null - const addToFavoriteApps = () => { - if (!appItemMenuDetails || !appItemMenuDetails.icon) return - - dispatch(addFavoriteApp({ ...appItemMenuDetails, icon: appItemMenuDetails.icon })) - closeMenu() + const openAppInfo = () => { + dispatch(resetAppsSearchState()) + showAppDetails(appDetails.packageName) + dismissMenu() } - const removeFromFavoriteApps = () => { - if (!appItemMenuDetails?.icon) return - - dispatch(removeFavoriteApp(appItemMenuDetails.name)) - closeMenu() + const uninstallApp = () => { + dispatch(resetAppsSearchState()) + requestAppUninstall(appDetails.packageName) + dismissMenu() } - const updatePinnedApp = (isPermanent: boolean) => { - if (!appItemMenuDetails || !appItemMenuDetails.icon) return - - dispatch( - updateOrRemovePinnedApp({ - ...getPinnedAppByName(allPinnedApps, appItemMenuDetails as AppDetailsWithIcon), - isPermanent, - }) - ) - closeMenu() + const toggleFavoriteApp = () => { + if (isFavoriteApp) dispatch(removeFavoriteApp(appDetails.packageName)) + else dispatch(addFavoriteApp(appDetails)) } - const updateTemporaryPinnedApp = (isTemporary: boolean) => { - if (!appItemMenuDetails || !appItemMenuDetails.icon) return - - dispatch( - updateOrRemovePinnedApp({ - ...getPinnedAppByName(allPinnedApps, appItemMenuDetails as AppDetailsWithIcon), - isTemporary, - }) - ) - closeMenu() + const togglePinnedApp = () => { + if (isPinnedApp) { + dispatch(removePinnedApp({ app: appDetails, isPermanent: true })) + } else { + dispatch(addPinnedApp({ app: appDetails, isPermanent: true })) + } } - const openAppInfo = () => { - if (!appItemMenuDetails) return + const toggleTemporarilyPinnedApp = () => { + if (isTemporarilyPinnedApp) { + dispatch(removePinnedApp({ app: appDetails, isPermanent: false })) + } else { + dispatch(addPinnedApp({ app: appDetails, isPermanent: false })) + } + } - dispatch(resetAppsSearchState()) - showAppDetails(appItemMenuDetails.name) - closeMenu() + const getToggleButtonIcon = (iconName: string, isActive: boolean, isDisabled: boolean) => { + if (isDisabled) return + return } - const uninstallApp = () => { - if (!appItemMenuDetails) return + const getToggleButtonStatus = (isChecked: boolean) => (isChecked ? 'checked' : 'unchecked') - dispatch(resetAppsSearchState()) - requestAppUninstall(appItemMenuDetails.name) - closeMenu() - } + const getTextOpacityStyle = (isDisabled: boolean) => ({ opacity: isDisabled ? 0.5 : 1 }) - const closeMenu = () => { - appItemMenuBottomSheetRef?.current?.dismiss() - } + const isPinButtonDisabled = !displayPinnedAppsValue + const isTemporaryPinButtonDisabled = !displayTemporaryPinnedApps + const isFavoriteButtonDisabled = !displayFavoriteAppsValue || (!isFavoriteApp && favoriteAppsCount === 5) - const pressableStyles = ({ pressed }: { pressed: boolean }) => { - return { - flex: 1, - backgroundColor: pressed ? 'rgba(255, 255, 255, .25)' : 'transparent', - } + const dismissMenu = () => { + dispatch(setDisplayAppMenu(false)) } return ( - - - {/* Wrapper */} - - {/* Header wrapper */} - - - - {appItemMenuDetails?.label} - - - - + + + + + + {appDetails.name} + + - - [ - pressableStyles({ pressed }), - { opacity: !displayFavoriteAppsValue || (!isFavoriteApp && favoriteAppsCount === 5) ? 0.5 : 1 }, - ]} - disabled={!displayFavoriteAppsValue || (!isFavoriteApp && favoriteAppsCount === 5)} - android_disableSound={true} - android_ripple={settingItemButtonRippleConfig}> - - + + + getToggleButtonIcon('star', isFavoriteApp, isFavoriteButtonDisabled)} + status={getToggleButtonStatus(isFavoriteApp)} + disabled={isFavoriteButtonDisabled} + /> + Favorite - - updatePinnedApp(false) : () => updatePinnedApp(true)} - style={({ pressed }: { pressed: boolean }) => [ - pressableStyles({ pressed }), - { opacity: !displayPinnedAppsValue ? 0.5 : 1 }, - ]} - disabled={!displayPinnedAppsValue} - android_disableSound={true} - android_ripple={settingItemButtonRippleConfig}> - - + + getToggleButtonIcon('pin', isPinnedApp, isPinButtonDisabled)} + status={getToggleButtonStatus(isPinnedApp)} + disabled={isPinButtonDisabled} + /> + Pin - - updateTemporaryPinnedApp(false) : () => updateTemporaryPinnedApp(true) - } - style={({ pressed }: { pressed: boolean }) => [ - pressableStyles({ pressed }), - { opacity: !displayTemporaryPinnedApps ? 0.5 : 1 }, - ]} - disabled={!displayTemporaryPinnedApps} - android_disableSound={true} - android_ripple={settingItemButtonRippleConfig}> - - + + getToggleButtonIcon('timer', isTemporarilyPinnedApp, isTemporaryPinButtonDisabled)} + status={getToggleButtonStatus(isTemporarilyPinnedApp)} + disabled={isTemporaryPinButtonDisabled} + /> + Temp. Pin - - + - - + /> + Info - - + - - + /> + Uninstall - - + + ) } const styles = StyleSheet.create({ - settingsWrapper: { - paddingHorizontal: 10, - }, - itemContainer: { - flexDirection: 'row', - borderBottomWidth: 1, - borderBottomColor: '#e5e5e5', + contentContainerStyle: { + position: 'absolute', + left: 5, + right: 5, + bottom: BOTTOM_CONTAINER_HEIGHT_WITH_PADDINGS, + padding: 10, + borderRadius: 5, + backgroundColor: 'white', }, headerWrapper: { - paddingBottom: 10, + paddingBottom: 5, alignItems: 'center', flexDirection: 'row', justifyContent: 'space-between', }, + headerIconAndTitleWrapper: { + flexDirection: 'row', + alignItems: 'center', + }, headerTitle: { fontSize: 20, - color: '#808080', - }, - bottomSheetModal: { - marginHorizontal: 5, + color: NEUTRAL_COLOR, }, appIcon: { width: 40, height: 40, marginRight: 10, }, + dismissIcon: { + margin: 0, + }, + buttonsWrapper: { + flexDirection: 'row', + justifyContent: 'space-around', + }, + toggleButtonWrapper: { + alignItems: 'center', + justifyContent: 'space-between', + }, + toggleButton: { + backgroundColor: WHITE_COLOR, + }, + iconButtonWrapper: { + alignItems: 'center', + }, + iconButton: { + margin: 0, + borderRadius: 5, + }, + buttonText: { + color: NEUTRAL_COLOR, + }, }) export default AppItemMenu diff --git a/src/components/CustomView.tsx b/src/components/CustomView.tsx deleted file mode 100644 index 462101d..0000000 --- a/src/components/CustomView.tsx +++ /dev/null @@ -1,16 +0,0 @@ -// React -import React from 'react' -// Reanimated -import Animated from 'react-native-reanimated' -// Models -import { CustomViewProps as Props } from '../models/props' - -const CustomView = ({ children, style, entryAnimation, exitAnimation }: Props) => { - return ( - - {children} - - ) -} - -export default CustomView diff --git a/src/components/FavoriteApps.tsx b/src/components/FavoriteApps.tsx index ac5efaa..d31e1fe 100644 --- a/src/components/FavoriteApps.tsx +++ b/src/components/FavoriteApps.tsx @@ -1,65 +1,56 @@ -// React import React, { useMemo } from 'react' -// React Native import { StyleSheet, Text, View } from 'react-native' -// Components -import AppItem from './AppItem' -// Redux import { useSelector } from 'react-redux' -import { selectFavoriteAppsMemoized } from '../slices/favoriteApps' -// Constants -import { BACKGROUND_COLOR } from '../constants' -import { singleRowAppsViewStyle, whiteTextColorStyle } from '../shared/styles' -// Models -import { RenderedIn } from '../models/rendered-in' import { FavoriteApp } from '../models/favorite-app' +import { RenderedIn } from '../models/rendered-in' +import { + noAppsViewStyle, + sectionHeaderLabelStyle, + sectionHeaderWrapperStyle, + sectionWrapper, + whiteTextColorStyle, +} from '../shared/styles' +import { selectFavoriteAppsMemoized } from '../slices/favoriteApps' +import AppItem from './AppItem' const FavoriteApps = () => { const apps = useSelector(selectFavoriteAppsMemoized) + const noAppsFound = useMemo(() => apps.length === 0, [apps]) + const favoriteApps = useMemo( () => apps.map((app: FavoriteApp) => ( - + )), [apps] ) return ( - - - Favorite + + + Favorite - {apps.length > 0 ? ( - {favoriteApps} - ) : ( - + + {noAppsFound ? ( No favorite apps yet - - )} + ) : ( + + {favoriteApps} + + )} + ) } const styles = StyleSheet.create({ - wrapper: { - borderRadius: 5, - backgroundColor: BACKGROUND_COLOR, - }, - headerLabel: { - color: 'rgba(255,255,255,0.75)', - fontSize: 12, - }, - headerWrapper: { - paddingVertical: 2.5, - paddingHorizontal: 10, - borderBottomWidth: 1, - borderBottomColor: 'rgba(255,255,255,0.5)', + appsViewWrapper: { + padding: 5, }, - horizontalAppsWrapper: { + appsWrapper: { flexDirection: 'row', justifyContent: 'space-evenly', - paddingVertical: 5, }, }) diff --git a/src/components/FilteredApps.tsx b/src/components/FilteredApps.tsx index 77015cd..3b9a1aa 100644 --- a/src/components/FilteredApps.tsx +++ b/src/components/FilteredApps.tsx @@ -1,59 +1,35 @@ -// React -import React, { useContext } from 'react' -// React Native -import { FlatList, ListRenderItem, ListRenderItemInfo, StyleSheet, Text, View } from 'react-native' -// Components -import AppItem from './AppItem' -// Redux +import React from 'react' +import { FlatList, ListRenderItem, ListRenderItemInfo, StyleSheet, View } from 'react-native' import { useSelector } from 'react-redux' -import { selectAppsSearchQuery, selectAppsSearchResult } from '../slices/appsSearch' -// Contexts -import SearchContext from '../contexts/SearchContext' -// Constants -import { APP_ITEM_HEIGHT_ICON_DISPLAYED, BACKGROUND_COLOR } from '../constants' -// Utils -import { truncateString } from '../utils/string' -// Models -import { RenderedIn } from '../models/rendered-in' import { AppDetails } from '../models/app-details' - -const keyExtractor = ({ name }: AppDetails) => name -const getItemLayout = (_data: unknown, index: number) => ({ - length: APP_ITEM_HEIGHT_ICON_DISPLAYED, - offset: APP_ITEM_HEIGHT_ICON_DISPLAYED * index, - index, -}) +import { RenderedIn } from '../models/rendered-in' +import { sectionWrapper } from '../shared/styles' +import { selectAppsSearchQuery, selectAppsSearchResult } from '../slices/appState' +import { getListItemLayout, getListKey } from '../utils/apps' +import { truncateString } from '../utils/string' +import AppItem from './AppItem' +import EmptyListComponent from './shared/EmptyListComponent' const FilteredApps = () => { const apps = useSelector(selectAppsSearchResult) - const { invalidCharacters } = useContext(SearchContext) const searchQuery = useSelector(selectAppsSearchQuery) - if (apps.length === 0 || invalidCharacters) { - return ( - - No application found for "{truncateString(searchQuery, 20)}" - - ) - } - const renderItem: ListRenderItem = ({ item }: ListRenderItemInfo) => ( - + ) return ( - + + } /> ) @@ -61,19 +37,7 @@ const FilteredApps = () => { const styles = StyleSheet.create({ wrapper: { - borderRadius: 5, paddingVertical: 5, - backgroundColor: BACKGROUND_COLOR, - }, - noAppsWrapper: { - height: 70, - paddingVertical: 0, - alignItems: 'center', - flexDirection: 'row', - justifyContent: 'center', - }, - noAppsWrapperText: { - color: '#fff', }, }) diff --git a/src/components/HighlightText.test.tsx b/src/components/HighlightText.test.tsx index 48d750f..fd78bdc 100644 --- a/src/components/HighlightText.test.tsx +++ b/src/components/HighlightText.test.tsx @@ -1,16 +1,20 @@ -import React from 'react' import { screen } from '@testing-library/react-native' -import { renderWithProvider } from '../../utils/test/utils' +import React from 'react' import { initialStoreState } from '../../utils/test/data' +import { renderWithProvider } from '../../utils/test/utils' +import { RootState } from '../store' import HighlightText from './HighlightText' describe(' Tests', () => { it('should render correctly and match snapshot - without search query', () => { - const customInitialState = { + const customInitialState: RootState = { ...initialStoreState, - appsSearch: { - query: undefined, - result: [], + appState: { + ...initialStoreState.appState, + search: { + query: undefined, + result: [], + }, }, } @@ -24,11 +28,14 @@ describe(' Tests', () => { }) it('should render correctly and match snapshot - with search query', () => { - const customInitialState = { + const customInitialState: RootState = { ...initialStoreState, - appsSearch: { - query: 'chro', - result: [], // left empty because result list doesn't matter in this component. + appState: { + ...initialStoreState.appState, + search: { + query: 'chro', + result: [], // left empty because result list doesn't matter in this component. + }, }, } @@ -42,11 +49,14 @@ describe(' Tests', () => { }) it('should highlight query in text correctly', () => { - const customInitialState = { + const customInitialState: RootState = { ...initialStoreState, - appsSearch: { - query: 'o', - result: [], // left empty because result list doesn't matter in this component. + appState: { + ...initialStoreState.appState, + search: { + query: 'o', + result: [], // left empty because result list doesn't matter in this component. + }, }, } diff --git a/src/components/HighlightText.tsx b/src/components/HighlightText.tsx index 04f0d96..092fc75 100644 --- a/src/components/HighlightText.tsx +++ b/src/components/HighlightText.tsx @@ -1,17 +1,13 @@ -// React import React from 'react' -// React Native import { StyleSheet, Text } from 'react-native' -// Redux import { useSelector } from 'react-redux' -// Slices -import { selectAppsSearchQuery } from '../slices/appsSearch' -// Constants -import { PRIMARY_COLOR } from '../constants' -// Utils +import { PRIMARY_COLOR, WHITE_COLOR } from '../constants' +import { selectAppsSearchQuery } from '../slices/appState' import { createKeyForHighlightTextElement } from '../utils/string' -// Models -import { HighlightTextProps as Props } from '../models/props' + +type Props = { + text: string +} const HighlightText = ({ text }: Props) => { const searchQuery = useSelector(selectAppsSearchQuery) @@ -48,7 +44,7 @@ const styles = StyleSheet.create({ textShadowRadius: 2.5, }, normalText: { - color: '#fff', + color: WHITE_COLOR, fontWeight: '600', textShadowColor: 'rgba(0, 0, 0, 0.75)', textShadowRadius: 2.5, diff --git a/src/components/PinnedApps.tsx b/src/components/PinnedApps.tsx index 6763663..0183591 100644 --- a/src/components/PinnedApps.tsx +++ b/src/components/PinnedApps.tsx @@ -1,76 +1,45 @@ -// React Native +import React from 'react' import { FlatList, ListRenderItem, ListRenderItemInfo, StyleSheet, Text, View } from 'react-native' -// Components -import AppItem from './AppItem' -// Redux import { useSelector } from 'react-redux' -import { selectPinnedAppsMemoized } from '../slices/pinnedApps' -// Constants -import { APP_ITEM_HEIGHT_ICON_DISPLAYED, BACKGROUND_COLOR } from '../constants' -import { singleRowAppsViewStyle, whiteTextColorStyle } from '../shared/styles' -// Models -import { RenderedIn } from '../models/rendered-in' import { PinnedApp } from '../models/pinned-app' -import { AppDetails } from '../models/app-details' - -const keyExtractor = ({ name }: AppDetails) => name -const getItemLayout = (_data: unknown, index: number) => ({ - length: APP_ITEM_HEIGHT_ICON_DISPLAYED, - offset: APP_ITEM_HEIGHT_ICON_DISPLAYED * index, - index, -}) +import { RenderedIn } from '../models/rendered-in' +import { sectionHeaderLabelStyle, sectionHeaderWrapperStyle, sectionWrapper } from '../shared/styles' +import { selectPinnedAppsMemoized } from '../slices/pinnedApps' +import { getListItemLayout, getListKey } from '../utils/apps' +import AppItem from './AppItem' +import EmptyListComponent from './shared/EmptyListComponent' const PinnedApps = () => { const apps = useSelector(selectPinnedAppsMemoized) const renderItem: ListRenderItem = ({ item }: ListRenderItemInfo) => ( - + ) return ( - - - Pinned + + + Pinned + + + } + /> - {apps.length > 0 ? ( - - - - ) : ( - - No pinned apps yet - - )} ) } const styles = StyleSheet.create({ - wrapper: { - borderRadius: 5, - backgroundColor: BACKGROUND_COLOR, - }, - headerLabel: { - color: 'rgba(255,255,255,0.75)', - fontSize: 12, - }, - headerWrapper: { - paddingVertical: 2.5, - paddingHorizontal: 10, - borderBottomWidth: 1, - borderBottomColor: 'rgba(255,255,255,0.5)', - }, - verticalAppsWrapper: { - flexDirection: 'row', - justifyContent: 'flex-start', + appsWrapper: { padding: 5, }, }) diff --git a/src/components/RecentApps.tsx b/src/components/RecentApps.tsx index a1c7b83..a2722e7 100644 --- a/src/components/RecentApps.tsx +++ b/src/components/RecentApps.tsx @@ -1,82 +1,44 @@ -// React import React from 'react' -// React Native import { FlatList, ListRenderItem, ListRenderItemInfo, StyleSheet, Text, View } from 'react-native' -// Redux import { useSelector } from 'react-redux' +import { RecentApp } from '../models/recent-app' +import { RenderedIn } from '../models/rendered-in' +import { sectionHeaderLabelStyle, sectionHeaderWrapperStyle, sectionWrapper } from '../shared/styles' import { selectRecentAppsMemoized } from '../slices/recentApps' -// Components +import { getListItemLayout, getListKey } from '../utils/apps' import AppItem from './AppItem' -// Constants -import { APP_ITEM_HEIGHT_ICON_DISPLAYED, BACKGROUND_COLOR } from '../constants' -import { singleRowAppsViewStyle, whiteTextColorStyle } from '../shared/styles' -// Models -import { RenderedIn } from '../models/rendered-in' -import { RecentAppDetails } from '../models/recent-app' - -const keyExtractor = ({ name }: RecentAppDetails) => name -const getItemLayout = (_data: unknown, index: number) => ({ - length: APP_ITEM_HEIGHT_ICON_DISPLAYED, - offset: APP_ITEM_HEIGHT_ICON_DISPLAYED * index, - index, -}) +import EmptyListComponent from './shared/EmptyListComponent' const RecentApps = () => { const apps = useSelector(selectRecentAppsMemoized) - const renderItem: ListRenderItem = ({ item }: ListRenderItemInfo) => ( - + const renderItem: ListRenderItem = ({ item }: ListRenderItemInfo) => ( + ) return ( - - - Recent + + + Recent + + + } + /> - {apps.length > 0 ? ( - - - - ) : ( - - No recent apps yet - - )} ) } const styles = StyleSheet.create({ - wrapper: { - borderRadius: 5, - backgroundColor: BACKGROUND_COLOR, - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-between', - }, - headerLabel: { - color: 'rgba(255,255,255,0.75)', - fontSize: 12, - }, - headerWrapper: { - paddingVertical: 2.5, - paddingHorizontal: 10, - borderBottomWidth: 1, - borderBottomColor: 'rgba(255,255,255,0.5)', - }, - verticalAppsWrapper: { + appsWrapper: { paddingVertical: 5, }, }) diff --git a/src/components/Search.test.tsx b/src/components/Search.test.tsx index ff21686..c01a200 100644 --- a/src/components/Search.test.tsx +++ b/src/components/Search.test.tsx @@ -1,7 +1,8 @@ -import React from 'react' import { fireEvent, screen } from '@testing-library/react-native' +import React from 'react' import { initialStoreState } from '../../utils/test/data' -import { renderWithProviderAndContexts, renderWithProvider } from '../../utils/test/utils' +import { renderWithProvider, renderWithProviderAndContexts } from '../../utils/test/utils' +import { RootState } from '../store' import Search from './Search' describe(' Tests', () => { @@ -14,11 +15,14 @@ describe(' Tests', () => { }) it('should render correctly and match snapshot with clear button', () => { - const customInitialState = { + const customInitialState: RootState = { ...initialStoreState, - appsSearch: { - ...initialStoreState.appsSearch, - query: 'a-search-query', + appState: { + ...initialStoreState.appState, + search: { + query: 'a-search-query', + result: [], + }, }, } @@ -30,11 +34,14 @@ describe(' Tests', () => { }) it('should update query when text changes and clear query when clear button is pressed', () => { - const customInitialState = { + const customInitialState: RootState = { ...initialStoreState, - appsSearch: { - ...initialStoreState.appsSearch, - query: undefined, + appState: { + ...initialStoreState.appState, + search: { + query: undefined, + result: [], + }, }, } @@ -44,9 +51,9 @@ describe(' Tests', () => { expect(searchInput).toBeOnTheScreen() - fireEvent.changeText(searchInput, 'a-search-query') + fireEvent.changeText(searchInput, 'A search token') - expect(store.getState().appsSearch.query).toBe('a-search-query') + expect(store.getState().appState.search.query).toBe('A search token') const searchInputClearButton = screen.getByTestId('search-input-clear-button') @@ -54,27 +61,32 @@ describe(' Tests', () => { fireEvent.press(searchInputClearButton) - expect(store.getState().appsSearch.query).toBeUndefined() + expect(store.getState().appState.search.query).toBeUndefined() }) it('should set correct result list when using correct query and reset search values when clear button is pressed', () => { - const customInitialState = { + const customInitialState: RootState = { ...initialStoreState, appsList: { list: [ { - name: 'com.google.chrome', - label: 'Chrome', + packageName: 'com.google.chrome', + name: 'Chrome', + icon: 'ICON', }, { - name: 'com.google.maps', - label: 'Maps', + packageName: 'com.google.maps', + name: 'Maps', + icon: 'ICON', }, ], }, - appsSearch: { - query: undefined, - result: [], + appState: { + ...initialStoreState.appState, + search: { + query: undefined, + result: [], + }, }, } @@ -88,11 +100,12 @@ describe(' Tests', () => { let currentState = store.getState() - expect(currentState.appsSearch.query).toBe('chr') - expect(currentState.appsSearch.result).toEqual([ + expect(currentState.appState.search.query).toBe('chr') + expect(currentState.appState.search.result).toEqual([ { - name: 'com.google.chrome', - label: 'Chrome', + packageName: 'com.google.chrome', + name: 'Chrome', + icon: 'ICON', }, ]) @@ -104,24 +117,28 @@ describe(' Tests', () => { currentState = { ...store.getState() } - expect(currentState.appsSearch.query).toBeUndefined() - expect(currentState.appsSearch.result).toEqual([]) + expect(currentState.appState.search.query).toBeUndefined() + expect(currentState.appState.search.result).toEqual([]) }) it('should set correct result list when using wrong query and reset search values when clear button is pressed', () => { - const customInitialState = { + const customInitialState: RootState = { ...initialStoreState, appsList: { list: [ { - name: 'com.google.chrome', - label: 'Chrome', + packageName: 'com.google.chrome', + name: 'Chrome', + icon: 'ICON', }, ], }, - appsSearch: { - query: undefined, - result: [], + appState: { + ...initialStoreState.appState, + search: { + query: undefined, + result: [], + }, }, } @@ -135,8 +152,8 @@ describe(' Tests', () => { let currentState = store.getState() - expect(currentState.appsSearch.query).toBe('youtube') - expect(currentState.appsSearch.result).toEqual([]) + expect(currentState.appState.search.query).toBe('youtube') + expect(currentState.appState.search.result).toEqual([]) const searchInputClearButton = screen.getByTestId('search-input-clear-button') @@ -146,8 +163,8 @@ describe(' Tests', () => { currentState = { ...store.getState() } - expect(currentState.appsSearch.query).toBeUndefined() - expect(currentState.appsSearch.result).toEqual([]) + expect(currentState.appState.search.query).toBeUndefined() + expect(currentState.appState.search.result).toEqual([]) }) test.todo('cover all possible cases for the search functionality') diff --git a/src/components/Search.tsx b/src/components/Search.tsx index e20144e..7d9002d 100644 --- a/src/components/Search.tsx +++ b/src/components/Search.tsx @@ -1,39 +1,31 @@ -// React import React, { useCallback, useContext, useMemo } from 'react' -// React Native -import { Pressable, PressableAndroidRippleConfig, StyleSheet, TextInput, View } from 'react-native' -// Components -import CustomIcon from './shared/CustomIcon' -// Redux +import { StyleSheet, TextInput, View } from 'react-native' +import { IconButton } from 'react-native-paper' import { useDispatch, useSelector } from 'react-redux' +import { NEUTRAL_COLOR, PRIMARY_COLOR, SECONDARY_COLOR } from '../constants' +import SearchContext from '../contexts/SearchContext' +import { iconButtonStyle } from '../shared/bottom-container' +import { selectAppsListMemoized } from '../slices/appsList' import { + appLaunchFromSearch, resetAppsSearchState, selectAppsSearchQuery, + selectDisplayAllApps, setAppsSearchQuery, setAppsSearchResult, -} from '../slices/appsSearch' -import { selectAppsListMemoized } from '../slices/appsList' -// Contexts -import SearchContext from '../contexts/SearchContext' -import GlobalContext from '../contexts/GlobalContext' -// Constants -import { PRIMARY_COLOR, SECONDARY_COLOR } from '../constants' -// Utils + setDisplayAllApps, +} from '../slices/appState' import { getAppsByLabel } from '../utils/apps' -const clearButtonRippleConfig: PressableAndroidRippleConfig = { - borderless: true, -} - const Search = () => { const dispatch = useDispatch() const apps = useSelector(selectAppsListMemoized) const searchQuery = useSelector(selectAppsSearchQuery) - const { displayAllApps, hideAllApps } = useContext(GlobalContext) - const { searchInputRef, invalidCharacters, setInvalidCharacters } = useContext(SearchContext) + const displayAllApps = useSelector(selectDisplayAllApps) + const { searchInputRef, clearSearchInput, searchAppLaunchProcedure } = useContext(SearchContext) const onQueryChange = (query: string) => { - hideAllApps() + dispatch(setDisplayAllApps(false)) const trimmedQuery = query.trim().replace(/\./g, '\\.') @@ -44,20 +36,17 @@ const Search = () => { // Check for only ASCII based characters if (!trimmedQuery.match(/[A-z\s\d]/gi)) { - setInvalidCharacters(true) + dispatch(setAppsSearchResult([])) dispatch(setAppsSearchQuery(trimmedQuery)) return } - // Reset invalid characters when it's valid (passes the above check) - if (invalidCharacters) setInvalidCharacters(false) - dispatch(setAppsSearchResult(getAppsByLabel(apps, trimmedQuery))) dispatch(setAppsSearchQuery(trimmedQuery)) } const clearInputAndSearchState = useCallback(() => { - searchInputRef?.current?.clear() + clearSearchInput() dispatch(resetAppsSearchState()) }, [searchInputRef]) @@ -65,13 +54,14 @@ const Search = () => { if (!searchQuery) return null return ( - - - + testID='search-input-clear-button' + /> ) } @@ -80,6 +70,13 @@ const Search = () => { [displayAllApps] ) + const onSubmit = () => { + // Reset app state + searchAppLaunchProcedure() + // Clean up state and launch app + dispatch(appLaunchFromSearch()) + } + return ( { style={styles.searchInput} placeholder='Search' placeholderTextColor={placeholderTextColor} - returnKeyType='search' + returnKeyType='go' autoCapitalize='words' onChangeText={onQueryChange} + onSubmitEditing={onSubmit} /> {clearButton()} @@ -105,7 +103,7 @@ const styles = StyleSheet.create({ }, searchInput: { flex: 1, - color: '#000', + color: NEUTRAL_COLOR, }, }) diff --git a/src/components/Settings/Settings.test.tsx b/src/components/Settings/Settings.test.tsx new file mode 100644 index 0000000..3f37b82 --- /dev/null +++ b/src/components/Settings/Settings.test.tsx @@ -0,0 +1,36 @@ +import { screen, within } from '@testing-library/react-native' +import React from 'react' +import { initialStoreState } from '../../../utils/test/data' +import { renderWithProvider } from '../../../utils/test/utils' +import { RootState } from '../../store' +import Settings from './Settings' + +describe(' Tests', () => { + it('should not render modal when display settings value is false (default)', () => { + renderWithProvider() + + expect(screen.queryByTestId('settings-wrapper')).toBeNull() + }) + + it('should render modal correctly and match snapshot when display settings value is false', () => { + const customInitialState: RootState = { + ...initialStoreState, + appState: { + ...initialStoreState.appState, + displaySettings: true, + }, + } + + renderWithProvider(, { preloadedState: customInitialState }) + + expect(screen.toJSON()).toMatchSnapshot() + + const settingsWrapper = screen.getByTestId('settings-wrapper') + + expect(settingsWrapper).toBeOnTheScreen() + expect(within(settingsWrapper).getByText('Recent Apps')).toBeOnTheScreen() + expect(within(settingsWrapper).getByText('Pinned Apps')).toBeOnTheScreen() + expect(within(settingsWrapper).getByText('Favorite Apps')).toBeOnTheScreen() + expect(within(settingsWrapper).getByText('Advanced Settings')).toBeOnTheScreen() + }) +}) diff --git a/src/components/Settings/SettingsBottomSheet.tsx b/src/components/Settings/Settings.tsx similarity index 51% rename from src/components/Settings/SettingsBottomSheet.tsx rename to src/components/Settings/Settings.tsx index bd25243..16ae4f9 100644 --- a/src/components/Settings/SettingsBottomSheet.tsx +++ b/src/components/Settings/Settings.tsx @@ -1,29 +1,28 @@ -// React -import React, { useContext } from 'react' -// React Native +import React from 'react' import { StyleSheet, View } from 'react-native' -// Components +import { Modal, Portal } from 'react-native-paper' +import { useDispatch, useSelector } from 'react-redux' +import { BOTTOM_CONTAINER_HEIGHT_WITH_PADDINGS } from '../../constants' +import { selectDisplaySettings, setDisplaySettings } from '../../slices/appState' +import AdvancedSettings from './sections/AdvancedSettings' +import FavoriteAppsSettings from './sections/FavoriteAppsSettings' +import PinnedAppsSettings from './sections/PinnedAppsSettings' +import RecentAppsSettings from './sections/RecentAppsSettings' import SettingsHeader from './SettingsHeader' import ToggleSettings from './shared/ToggleSettings' -import RecentAppsSettings from './sections/RecentAppsSettings' -import PinnedAppsSettings from './sections/PinnedAppsSettings' -import FavoriteAppsSettings from './sections/FavoriteAppsSettings' -import AdvancedSettings from './sections/AdvancedSettings' -// Contexts -import GlobalContext from '../../contexts/GlobalContext' -// BottomSheet -import { BottomSheetModal, BottomSheetModalProvider } from '@gorhom/bottom-sheet' -const SettingsBottomSheet = () => { - const { settingsBottomSheetRef } = useContext(GlobalContext) +const Settings = () => { + const dispatch = useDispatch() + const displaySettings = useSelector(selectDisplaySettings) + + const closeSettings = () => { + dispatch(setDisplaySettings(false)) + } return ( - - - + + + @@ -42,18 +41,21 @@ const SettingsBottomSheet = () => { - - + + ) } const styles = StyleSheet.create({ - bottomSheetModal: { - marginHorizontal: 5, - }, - settingsWrapper: { - paddingHorizontal: 10, + contentContainerStyle: { + position: 'absolute', + left: 5, + right: 5, + bottom: BOTTOM_CONTAINER_HEIGHT_WITH_PADDINGS, + padding: 10, + borderRadius: 5, + backgroundColor: 'white', }, }) -export default SettingsBottomSheet +export default Settings diff --git a/src/components/Settings/SettingsBottomSheet.test.tsx b/src/components/Settings/SettingsBottomSheet.test.tsx deleted file mode 100644 index 237ccc7..0000000 --- a/src/components/Settings/SettingsBottomSheet.test.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' -import { fireEvent, screen } from '@testing-library/react-native' -import { renderWithProvider } from '../../../utils/test/utils' -import { CONTEXT_LAUNCHER_APP_ID } from '../../../utils/test/data' -import SettingsBottomSheet from './SettingsBottomSheet' -import * as AppsModuleUtils from '../../utils/apps-module' - -describe(' Tests', () => { - beforeAll(() => { - jest.spyOn(AppsModuleUtils, 'showAppDetails') - }) - - it('should render correctly and match snapshot', () => { - renderWithProvider() - - expect(screen.toJSON()).toMatchSnapshot() - // Wrappers - expect(screen.getByTestId('settings-bottom-sheet-wrapper')).toBeOnTheScreen() - expect(screen.getByTestId('settings-bottom-sheet-header-wrapper')).toBeOnTheScreen() - // Elements - expect(screen.getByTestId('settings-bottom-sheet-header-app-info-button')).toBeOnTheScreen() - }) - - it('should call native module function to open app info when pressed', () => { - renderWithProvider() - - const appInfoButton = screen.getByTestId('settings-bottom-sheet-header-app-info-button') - - expect(appInfoButton).toBeOnTheScreen() - - fireEvent.press(appInfoButton) - - expect(AppsModuleUtils.showAppDetails).toBeCalledWith(CONTEXT_LAUNCHER_APP_ID) - // TODO: Make sure 'settingsBottomSheetRef?.current?.dismiss()' is called - }) -}) diff --git a/src/components/Settings/SettingsHeader.tsx b/src/components/Settings/SettingsHeader.tsx index fb0c999..c3bdc74 100644 --- a/src/components/Settings/SettingsHeader.tsx +++ b/src/components/Settings/SettingsHeader.tsx @@ -1,19 +1,10 @@ -// React -import React, { useContext } from 'react' -// React Native -import { View, Text, Pressable, StyleSheet, PressableAndroidRippleConfig } from 'react-native' -// Components -import CustomIcon from '../shared/CustomIcon' -// Contexts -import GlobalContext from '../../contexts/GlobalContext' -// Utils +import React from 'react' +import { Pressable, PressableAndroidRippleConfig, StyleSheet, Text, View } from 'react-native' +import { useDispatch } from 'react-redux' +import { APP_BUILD_NUMBER, APP_ID, APP_VERSION } from '../../constants' +import { setDisplaySettings } from '../../slices/appState' import { showAppDetails } from '../../utils/apps-module' -// Constants -import { - CONTEXT_LAUNCHER_APP_VERSION, - CONTEXT_LAUNCHER_APP_BUILD_NUMBER, - CONTEXT_LAUNCHER_APP_ID, -} from '../../constants' +import CustomIcon from '../shared/CustomIcon' const appInfoIconRippleConfig: PressableAndroidRippleConfig = { borderless: true, @@ -23,26 +14,26 @@ const appInfoIconRippleConfig: PressableAndroidRippleConfig = { } const SettingsHeader = () => { - const { settingsBottomSheetRef } = useContext(GlobalContext) + const dispatch = useDispatch() const onAppInfoClick = () => { - showAppDetails(CONTEXT_LAUNCHER_APP_ID) - settingsBottomSheetRef?.current?.dismiss() + showAppDetails(APP_ID) + dispatch(setDisplaySettings(false)) } return ( - + Context Settings -   v{CONTEXT_LAUNCHER_APP_VERSION} ({CONTEXT_LAUNCHER_APP_BUILD_NUMBER}) +   v{APP_VERSION} ({APP_BUILD_NUMBER}) + testID='settings-header-app-info-button'> diff --git a/src/components/Settings/SettingsIcon.test.tsx b/src/components/Settings/SettingsIcon.test.tsx index c27f81a..8e97522 100644 --- a/src/components/Settings/SettingsIcon.test.tsx +++ b/src/components/Settings/SettingsIcon.test.tsx @@ -1,10 +1,16 @@ -import React from 'react' import { fireEvent, screen } from '@testing-library/react-native' -import { defaultGlobalContextValue } from '../../../utils/test/data' +import React from 'react' import { renderWithProvider, renderWithProviderAndContexts } from '../../../utils/test/utils' -import { GlobalContextType } from '../../models/context' +import { setDisplaySettings } from '../../slices/appState' import SettingsIcon from './SettingsIcon' +const useDispatchMock = jest.fn() + +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useDispatch: () => useDispatchMock, +})) + describe(' Tests', () => { it('should render correctly and match snapshot', () => { renderWithProvider() @@ -14,16 +20,7 @@ describe(' Tests', () => { }) it('should call function to display settings bottom sheet when pressed', () => { - const displaySettingsBottomSheetFn = jest.fn() - - const customGlobalContextValue: GlobalContextType = { - ...defaultGlobalContextValue, - displaySettingsBottomSheet: displaySettingsBottomSheetFn, - } - - renderWithProviderAndContexts(, { - globalContextValue: customGlobalContextValue, - }) + renderWithProviderAndContexts() const settingsButton = screen.getByTestId('settings-button') @@ -31,6 +28,6 @@ describe(' Tests', () => { fireEvent.press(settingsButton) - expect(displaySettingsBottomSheetFn).toBeCalled() + expect(useDispatchMock).toBeCalledWith(setDisplaySettings(true)) }) }) diff --git a/src/components/Settings/SettingsIcon.tsx b/src/components/Settings/SettingsIcon.tsx index 853b15b..3e9dd81 100644 --- a/src/components/Settings/SettingsIcon.tsx +++ b/src/components/Settings/SettingsIcon.tsx @@ -1,24 +1,26 @@ -// React -import React, { useContext } from 'react' -// React Native -import { Pressable, View } from 'react-native' -// Components -import CustomIcon from '../shared/CustomIcon' -// Contexts -import GlobalContext from '../../contexts/GlobalContext' -// Constants +import React from 'react' +import { IconButton } from 'react-native-paper' +import { useDispatch } from 'react-redux' import { PRIMARY_COLOR } from '../../constants' -import { iconsStyle, iconsPressableConfig } from '../../shared/bottom-container' +import { iconButtonStyle } from '../../shared/bottom-container' +import { setDisplaySettings } from '../../slices/appState' const SettingsIcon = () => { - const { displaySettingsBottomSheet } = useContext(GlobalContext) + const dispatch = useDispatch() + + const displaySettings = () => { + dispatch(setDisplaySettings(true)) + } return ( - - - - - + ) } diff --git a/src/components/Settings/__snapshots__/Settings.test.tsx.snap b/src/components/Settings/__snapshots__/Settings.test.tsx.snap new file mode 100644 index 0000000..0951a91 --- /dev/null +++ b/src/components/Settings/__snapshots__/Settings.test.tsx.snap @@ -0,0 +1,626 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` Tests should render modal correctly and match snapshot when display settings value is false 1`] = ` + + + + + + + + + + + + Context Settings + +   v + 1.4.8 + ( + 12 + ) + + + + + ó°‹½ + + + + + + + + Recent Apps + + + + ó°…€ + + + + + + + + Pinned Apps + + + + ó°…€ + + + + + + + + Favorite Apps + + + + ó°…€ + + + + + + + + Advanced Settings + + + + ó°…€ + + + + + + + + + + +`; diff --git a/src/components/Settings/__snapshots__/SettingsBottomSheet.test.tsx.snap b/src/components/Settings/__snapshots__/SettingsBottomSheet.test.tsx.snap deleted file mode 100644 index 19b0e19..0000000 --- a/src/components/Settings/__snapshots__/SettingsBottomSheet.test.tsx.snap +++ /dev/null @@ -1,490 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` Tests should render correctly and match snapshot 1`] = ` - - - - - - Context Settings - -   v - 1.4.8 - ( - 12 - ) - - - - - ó°‹½ - - - - - - - - Recent Apps - - - - ó°…€ - - - - - - - - Pinned Apps - - - - ó°…€ - - - - - - - - Favorite Apps - - - - ó°…€ - - - - - - - - Advanced Settings - - - - ó°…€ - - - - - - -`; diff --git a/src/components/Settings/__snapshots__/SettingsIcon.test.tsx.snap b/src/components/Settings/__snapshots__/SettingsIcon.test.tsx.snap index 7979fdc..db85b85 100644 --- a/src/components/Settings/__snapshots__/SettingsIcon.test.tsx.snap +++ b/src/components/Settings/__snapshots__/SettingsIcon.test.tsx.snap @@ -1,69 +1,180 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` Tests should render correctly and match snapshot 1`] = ` - - - 󰇙 - + + + + 󰇙 + + + + - + `; diff --git a/src/components/Settings/sections/AdvancedSettings.test.tsx b/src/components/Settings/sections/AdvancedSettings.test.tsx index 06139b9..d4da8da 100644 --- a/src/components/Settings/sections/AdvancedSettings.test.tsx +++ b/src/components/Settings/sections/AdvancedSettings.test.tsx @@ -1,69 +1,54 @@ -import React from 'react' import { fireEvent, screen } from '@testing-library/react-native' -import { initialStoreState } from '../../../../utils/test/data' -import { renderWithProvider } from '../../../../utils/test/utils' +import React from 'react' import { ToastAndroid } from 'react-native' -import AppsModule from '../../../native-modules/AppsModule' +import { renderWithProvider } from '../../../../utils/test/utils' +import { getAppsListAction } from '../../../slices/appsList' +import { resetPreferences } from '../../../slices/preferences' import AdvancedSettings from './AdvancedSettings' -import { PreferencesState } from '../../../slices/preferences' + +const useDispatchMock = jest.fn() + +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useDispatch: () => useDispatchMock, +})) describe(' Tests', () => { beforeEach(() => { jest.spyOn(ToastAndroid, 'show') }) - afterEach(() => { - jest.resetAllMocks() - }) - it('should render correctly and match snapshot', () => { renderWithProvider() expect(screen.toJSON()).toMatchSnapshot() - expect(screen.getByTestId('advanced-settings-reload-all-apps-button')).toBeOnTheScreen() - expect(screen.getByTestId('advanced-settings-reset-preferences-button')).toBeOnTheScreen() + expect(screen.getByTestId('reload-all-apps-button')).toBeOnTheScreen() + expect(screen.getByTestId('reset-preferences-button')).toBeOnTheScreen() }) - // TODO: Find a way to mock internal dispatched action - it.todo('should call AppsModule method to get all applications and set apps list') - - it('should call AppsModule method to get all applications', () => { + it('should dispatch action to get all applications', () => { renderWithProvider() - const reloadAllAppsButton = screen.getByTestId('advanced-settings-reload-all-apps-button') + const reloadAllAppsButton = screen.getByTestId('reload-all-apps-button') expect(reloadAllAppsButton).toBeOnTheScreen() fireEvent.press(reloadAllAppsButton) - expect(AppsModule.getApplications).toBeCalled() + expect(useDispatchMock).toBeCalledWith(getAppsListAction()) + expect(ToastAndroid.show).toBeCalledWith('All apps reloaded successfully!', ToastAndroid.LONG) }) it('should reset preferences apps when button is pressed', () => { - const customInitialState = { - ...initialStoreState, - preferences: { - displayRecentApps: false, - displayFavoriteApps: true, - displayPinnedApps: false, - displayTemporaryPinnedApps: true, - } as PreferencesState, - } - - const { store } = renderWithProvider(, { preloadedState: customInitialState }) + renderWithProvider() - const resetPreferencesButton = screen.getByTestId('advanced-settings-reset-preferences-button') + const resetPreferencesButton = screen.getByTestId('reset-preferences-button') expect(resetPreferencesButton).toBeOnTheScreen() fireEvent.press(resetPreferencesButton) - expect(store.getState().preferences).toEqual({ - displayRecentApps: false, - displayFavoriteApps: true, - displayPinnedApps: false, - displayTemporaryPinnedApps: true, - }) + expect(useDispatchMock).toBeCalledWith(resetPreferences()) expect(ToastAndroid.show).toBeCalledWith('Settings reset successfully!', ToastAndroid.LONG) }) }) diff --git a/src/components/Settings/sections/AdvancedSettings.tsx b/src/components/Settings/sections/AdvancedSettings.tsx index 5100ab8..256e9e8 100644 --- a/src/components/Settings/sections/AdvancedSettings.tsx +++ b/src/components/Settings/sections/AdvancedSettings.tsx @@ -1,32 +1,18 @@ -// React import React from 'react' -// React Native import { Pressable, View } from 'react-native' -// Components -import SettingsItemLabel from '../shared/SettingsItemLabel' -// Redux import { useDispatch } from 'react-redux' -import { setAppsList } from '../../../slices/appsList' +import { getAppsListAction } from '../../../slices/appsList' import { resetPreferences } from '../../../slices/preferences' -// Native modules -import AppsModule from '../../../native-modules/AppsModule' -// Utils import { displayToast } from '../../../utils/toast' -// Models -import { AppDetails } from '../../../models/app-details' -// Constants +import SettingsItemLabel from '../shared/SettingsItemLabel' import { settingItemButtonRippleConfig, settingItemWrapperStyle, settingsPressableItemStyle } from '../shared/values' const AdvancedSettings = () => { const dispatch = useDispatch() const reloadAllApps = () => { - AppsModule.getApplications((applications: string) => { - const apps = JSON.parse(applications) as AppDetails[] - - dispatch(setAppsList(apps)) - displayToast('All apps reloaded successfully!') - }) + dispatch(getAppsListAction()) + displayToast('All apps reloaded successfully!') } const onResetPreferences = () => { @@ -38,7 +24,7 @@ const AdvancedSettings = () => { <> { ({ + ...jest.requireActual('react-redux'), + useDispatch: () => useDispatchMock, +})) describe(' Tests', () => { beforeEach(() => { jest.spyOn(ToastAndroid, 'show') }) - afterEach(() => { - jest.resetAllMocks() - }) - it('should render correctly and match snapshot', () => { renderWithProvider() expect(screen.toJSON()).toMatchSnapshot() - expect(screen.getByTestId('settings-bottom-display-favorite-apps-switch')).toBeOnTheScreen() - expect(screen.getByTestId('settings-bottom-sort-favorite-apps-button')).toBeOnTheScreen() - expect(screen.getByTestId('advanced-settings-clear-favorite-apps-button')).toBeOnTheScreen() + expect(screen.getByTestId('display-favorite-apps-switch')).toBeOnTheScreen() + expect(screen.getByTestId('sort-favorite-apps-button')).toBeOnTheScreen() + expect(screen.getByTestId('clear-favorite-apps-button')).toBeOnTheScreen() }) - it('should clear favorite apps when button is pressed', () => { - const customInitialState = { + it('should dispatch action to clear favorite apps when button is pressed', () => { + const customInitialState: RootState = { ...initialStoreState, favoriteApps: { list: [ { - name: 'com.google.chrome', - label: 'Chrome', - icon: 'icon-base64-string-chrome', + packageName: 'com.google.chrome', + name: 'Chrome', + icon: 'ICON', }, { - name: 'com.google.maps', - label: 'Maps', - icon: 'icon-base64-string-maps', + packageName: 'com.google.maps', + name: 'Maps', + icon: 'ICON', }, - ] as FavoriteApp[], + ], }, } - const { store } = renderWithProvider(, { preloadedState: customInitialState }) + renderWithProvider(, { preloadedState: customInitialState }) - const clearFavoriteAppsButton = screen.getByTestId('advanced-settings-clear-favorite-apps-button') + const clearFavoriteAppsButton = screen.getByTestId('clear-favorite-apps-button') expect(clearFavoriteAppsButton).toBeOnTheScreen() fireEvent.press(clearFavoriteAppsButton) - expect(store.getState().favoriteApps.list).toEqual([]) + expect(useDispatchMock).toBeCalledWith(clearFavoriteApps()) expect(ToastAndroid.show).toBeCalledWith('Favorite apps cleared successfully!', ToastAndroid.LONG) }) describe('Sort favorite apps button tests', () => { it('should render sort favorite apps button as disabled if favorite apps are <= 1', () => { - const customInitialStoreState = { + const customInitialStoreState: RootState = { ...initialStoreState, favoriteApps: { list: [ { - name: 'com.google.chrome', - label: 'Chrome', - icon: 'icon-base64-string-chrome', + packageName: 'com.google.chrome', + name: 'Chrome', + icon: 'ICON', }, - ] as FavoriteApp[], + ], }, preferences: { - displayRecentApps: false, + ...initialStoreState.preferences, displayFavoriteApps: true, - displayPinnedApps: false, - displayTemporaryPinnedApps: true, - } as PreferencesState, + }, } renderWithProvider(, { preloadedState: customInitialStoreState }) - expect(screen.getByTestId('settings-bottom-sort-favorite-apps-button')).toBeDisabled() - expect(screen.getByText(/Sort/)).toBeOnTheScreen() + expect(screen.getByTestId('sort-favorite-apps-button')).toBeDisabled() expect(screen.getByText(/Add more apps to be able to sort/)).toBeOnTheScreen() }) it('should render sort favorite apps button as disabled if display favorite apps value is false', () => { - const customInitialStoreState = { + const customInitialStoreState: RootState = { ...initialStoreState, favoriteApps: { list: [ { - name: 'com.google.chrome', - label: 'Chrome', - icon: 'icon-base64-string-chrome', + packageName: 'com.google.chrome', + name: 'Chrome', + icon: 'ICON', }, { - name: 'com.google.maps', - label: 'Maps', - icon: 'icon-base64-string-maps', + packageName: 'com.google.maps', + name: 'Maps', + icon: 'ICON', }, - ] as FavoriteApp[], + ], }, preferences: { + ...initialStoreState.preferences, displayFavoriteApps: false, - displayRecentApps: false, - displayPinnedApps: false, - displayTemporaryPinnedApps: true, - } as PreferencesState, + }, } renderWithProvider(, { preloadedState: customInitialStoreState }) - expect(screen.getByTestId('settings-bottom-sort-favorite-apps-button')).toBeDisabled() - expect(screen.getByText(/Sort/)).toBeOnTheScreen() + expect(screen.getByTestId('sort-favorite-apps-button')).toBeDisabled() expect(screen.getByText(/Display favorite apps to be able to sort/)).toBeOnTheScreen() }) - it('should not render sort favorite apps button as disabled if favorite apps are > 1 and display favorite apps value is true', () => { - const customInitialStoreState = { + it('should render sort favorite apps button as enabled if favorite apps are > 1 and display favorite apps value is true', () => { + const customInitialStoreState: RootState = { ...initialStoreState, favoriteApps: { list: [ { - name: 'com.google.chrome', - label: 'Chrome', - icon: 'icon-base64-string-chrome', + packageName: 'com.google.chrome', + name: 'Chrome', + icon: 'ICON', }, { - name: 'com.google.maps', - label: 'Maps', - icon: 'icon-base64-string-maps', + packageName: 'com.google.maps', + name: 'Maps', + icon: 'ICON', }, - ] as FavoriteApp[], + ], }, preferences: { + ...initialStoreState.preferences, displayFavoriteApps: true, - displayRecentApps: false, - displayPinnedApps: false, - displayTemporaryPinnedApps: true, - } as PreferencesState, + }, } renderWithProvider(, { preloadedState: customInitialStoreState }) - expect(screen.getByTestId('settings-bottom-sort-favorite-apps-button')).not.toBeDisabled() - expect(screen.getByText(/Sort/)).toBeOnTheScreen() + expect(screen.getByTestId('sort-favorite-apps-button')).not.toBeDisabled() expect(screen.getByText(/Click to start sorting/)).toBeOnTheScreen() }) - it('should dismiss keyboard when pressed and toggle sortable favorite apps view', () => { - const customInitialStoreState = { + it('should dispatch action to sort favorite when button is pressed', () => { + const customInitialStoreState: RootState = { ...initialStoreState, favoriteApps: { list: [ { - name: 'com.google.chrome', - label: 'Chrome', - icon: 'icon-base64-string-chrome', + packageName: 'com.google.chrome', + name: 'Chrome', + icon: 'ICON', }, { - name: 'com.google.maps', - label: 'Maps', - icon: 'icon-base64-string-maps', + packageName: 'com.google.maps', + name: 'Maps', + icon: 'ICON', }, - ] as FavoriteApp[], + ], }, preferences: { + ...initialStoreState.preferences, displayFavoriteApps: true, - displayRecentApps: false, - displayPinnedApps: false, - displayTemporaryPinnedApps: true, - } as PreferencesState, - } - - const dismissKeyboardFn = jest.fn() - const toggleSortableFavoriteAppsFn = jest.fn() - - const customGlobalContextValue: GlobalContextType = { - ...defaultGlobalContextValue, - dismissKeyboard: dismissKeyboardFn, - toggleSortableFavoriteApps: toggleSortableFavoriteAppsFn, + }, } - renderWithProviderAndContexts(, { - preloadedState: customInitialStoreState, - globalContextValue: customGlobalContextValue, - }) + renderWithProvider(, { preloadedState: customInitialStoreState }) - const sortFavoriteAppsButton = screen.getByTestId('settings-bottom-sort-favorite-apps-button') + const sortFavoriteAppsButton = screen.getByTestId('sort-favorite-apps-button') + expect(sortFavoriteAppsButton).toBeOnTheScreen() expect(sortFavoriteAppsButton).not.toBeDisabled() - expect(screen.getByText(/Sort/)).toBeOnTheScreen() - expect(screen.getByText(/Click to start sorting/)).toBeOnTheScreen() fireEvent.press(sortFavoriteAppsButton) - expect(dismissKeyboardFn).toBeCalled() - expect(toggleSortableFavoriteAppsFn).toBeCalled() + expect(useDispatchMock).toBeCalledWith(sortFavoriteApps()) }) }) - it('should toggle display favorite apps value in the store when pressed', () => { - const customInitialStoreState = { + it('should dispatch action to toggle display favorite apps when pressed', () => { + const customInitialStoreState: RootState = { ...initialStoreState, preferences: { ...initialStoreState.preferences, @@ -210,14 +189,15 @@ describe(' Tests', () => { }, } - const { store } = renderWithProvider(, { preloadedState: customInitialStoreState }) + renderWithProvider(, { preloadedState: customInitialStoreState }) - const toggleFavoriteAppsSwitch = screen.getByTestId('settings-bottom-display-favorite-apps-switch') + const toggleFavoriteAppsSwitch = screen.getByTestId('display-favorite-apps-switch') expect(toggleFavoriteAppsSwitch).toBeOnTheScreen() + expect(toggleFavoriteAppsSwitch).not.toBeDisabled() fireEvent(toggleFavoriteAppsSwitch, 'valueChange') - expect(store.getState().preferences.displayFavoriteApps).toEqual(true) + expect(useDispatchMock).toBeCalledWith(displayFavoriteApps(true)) }) }) diff --git a/src/components/Settings/sections/FavoriteAppsSettings.tsx b/src/components/Settings/sections/FavoriteAppsSettings.tsx index 7711f72..6377960 100644 --- a/src/components/Settings/sections/FavoriteAppsSettings.tsx +++ b/src/components/Settings/sections/FavoriteAppsSettings.tsx @@ -1,40 +1,31 @@ -// React -import React, { useContext, useMemo } from 'react' -// React Native +import React, { useMemo } from 'react' import { Pressable, Switch, View } from 'react-native' -// Components -import SettingsItemLabel from '../shared/SettingsItemLabel' -// Redux import { useDispatch, useSelector } from 'react-redux' +import { sortFavoriteApps } from '../../../slices/appState' import { clearFavoriteApps, selectFavoriteAppsCountMemoized } from '../../../slices/favoriteApps' -import { selectDisplayFavoriteAppsMemoized, displayFavoriteApps } from '../../../slices/preferences' -// Contexts -import GlobalContext from '../../../contexts/GlobalContext' -// Utils +import { displayFavoriteApps, selectDisplayFavoriteAppsMemoized } from '../../../slices/preferences' import { displayToast } from '../../../utils/toast' -// Constants +import SettingsItemLabel from '../shared/SettingsItemLabel' import { - switchTrackColor, activeSwitch, inActiveSwitch, settingItemButtonRippleConfig, - settingsPressableItemStyle, settingItemWrapperStyle, + settingsPressableItemStyle, + switchTrackColor, } from '../shared/values' const FavoriteAppsSettings = () => { const dispatch = useDispatch() const favoriteAppsCount = useSelector(selectFavoriteAppsCountMemoized) const displayFavoriteAppsValue = useSelector(selectDisplayFavoriteAppsMemoized) - const { dismissKeyboard, toggleSortableFavoriteApps } = useContext(GlobalContext) const toggleDisplayFavoriteApps = () => { dispatch(displayFavoriteApps(!displayFavoriteAppsValue)) } const onFavoriteAppsSortViewClick = () => { - dismissKeyboard() - toggleSortableFavoriteApps() + dispatch(sortFavoriteApps()) } const onClearFavoriteApps = () => { @@ -52,7 +43,7 @@ const FavoriteAppsSettings = () => { { { { const dispatch = useDispatch() const displayPinnedAppsValue = useSelector(selectDisplayPinnedAppsMemoized) + const pinnedAppsCount = useSelector(selectPinnedAppsCountMemoized) const displayTemporaryPinnedAppsValue = useSelector(selectDisplayTemporaryPinnedAppsMemoized) + const temporaryPinnedAppsCount = useSelector(selectTemporaryPinnedAppsCountMemoized) const temporaryPinnedAppsConfig = useSelector(selectTemporaryPinnedAppsConfigMemoized) const toggleDisplayPinnedApps = () => { @@ -47,8 +45,13 @@ const PinnedAppsSettings = () => { } const onClearPinnedApps = () => { - dispatch(clearPinnedApps()) - displayToast('All pinned apps cleared successfully!') + dispatch(clearPinnedApps({ temporarily: false })) + displayToast('Pinned apps cleared successfully!') + } + + const onClearTemporarilyPinnedApps = () => { + dispatch(clearPinnedApps({ temporarily: true })) + displayToast('Temporarily pinned apps cleared successfully!') } const showTimePicker = (forStartDate: boolean) => { @@ -67,7 +70,7 @@ const PinnedAppsSettings = () => { } const setTemporaryPinnedAppsStartDate = (event: DateTimePickerEvent, date?: Date) => { - if (event.type != 'set' || !date) return + if (event.type !== 'set' || !date) return dispatch( setTemporaryAppsConfig({ @@ -78,7 +81,7 @@ const PinnedAppsSettings = () => { } const setTemporaryPinnedAppsEndDate = (event: DateTimePickerEvent, date?: Date) => { - if (event.type != 'set' || !date) return + if (event.type !== 'set' || !date) return dispatch( setTemporaryAppsConfig({ @@ -88,6 +91,24 @@ const PinnedAppsSettings = () => { ) } + const onPinnedAppsSortViewClick = () => { + dispatch(sortPinnedApps()) + } + + const onTemporaryPinnedAppsSortViewClick = () => { + dispatch(sortTemporaryPinnedApps()) + } + + const pinnedAppsSortDisabled = useMemo( + () => pinnedAppsCount <= 1 || !displayPinnedAppsValue, + [pinnedAppsCount, displayPinnedAppsValue] + ) + + const temporaryPinnedAppsSortDisabled = useMemo( + () => temporaryPinnedAppsCount <= 1 || !displayTemporaryPinnedAppsValue, + [temporaryPinnedAppsCount, displayTemporaryPinnedAppsValue] + ) + return ( <> @@ -100,6 +121,27 @@ const PinnedAppsSettings = () => { /> + + + + + + { /> + + + + + + { android_disableSound={true} android_ripple={settingItemButtonRippleConfig} style={settingsPressableItemStyle}> - + + + + + + + diff --git a/src/components/Settings/sections/RecentAppsSettings.test.tsx b/src/components/Settings/sections/RecentAppsSettings.test.tsx index 802edd6..990b02a 100644 --- a/src/components/Settings/sections/RecentAppsSettings.test.tsx +++ b/src/components/Settings/sections/RecentAppsSettings.test.tsx @@ -1,61 +1,66 @@ -import React from 'react' import { fireEvent, screen } from '@testing-library/react-native' +import React from 'react' +import { ToastAndroid } from 'react-native' import { initialStoreState } from '../../../../utils/test/data' import { renderWithProvider } from '../../../../utils/test/utils' -import { ToastAndroid } from 'react-native' -import { RecentAppDetails } from '../../../models/recent-app' +import { displayRecentApps } from '../../../slices/preferences' +import { clearRecentApps } from '../../../slices/recentApps' +import { RootState } from '../../../store' import RecentAppsSettings from './RecentAppsSettings' +const useDispatchMock = jest.fn() + +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useDispatch: () => useDispatchMock, +})) + describe(' Tests', () => { beforeEach(() => { jest.spyOn(ToastAndroid, 'show') }) - afterEach(() => { - jest.resetAllMocks() - }) - it('should render correctly and match snapshot', () => { renderWithProvider() expect(screen.toJSON()).toMatchSnapshot() - expect(screen.getByTestId('settings-bottom-display-recent-apps-switch')).toBeOnTheScreen() - expect(screen.getByTestId('advanced-settings-clear-recent-apps-button')).toBeOnTheScreen() + expect(screen.getByTestId('display-recent-apps-switch')).toBeOnTheScreen() + expect(screen.getByTestId('clear-recent-apps-button')).toBeOnTheScreen() }) it('should clear recent apps when button is pressed', () => { - const customInitialState = { + const customInitialState: RootState = { ...initialStoreState, recentApps: { list: [ { - name: 'com.google.chrome', - label: 'Chrome', - icon: 'icon-base64-string-chrome', + packageName: 'com.google.chrome', + name: 'Chrome', + icon: 'ICON', }, { - name: 'com.google.maps', - label: 'Maps', - icon: 'icon-base64-string-maps', + packageName: 'com.google.maps', + name: 'Maps', + icon: 'ICON', }, - ] as RecentAppDetails[], + ], }, } - const { store } = renderWithProvider(, { preloadedState: customInitialState }) + renderWithProvider(, { preloadedState: customInitialState }) - const clearRecentAppsButton = screen.getByTestId('advanced-settings-clear-recent-apps-button') + const clearRecentAppsButton = screen.getByTestId('clear-recent-apps-button') expect(clearRecentAppsButton).toBeOnTheScreen() fireEvent.press(clearRecentAppsButton) - expect(store.getState().recentApps.list).toEqual([]) + expect(useDispatchMock).toBeCalledWith(clearRecentApps()) expect(ToastAndroid.show).toBeCalledWith('Recent apps cleared successfully!', ToastAndroid.LONG) }) - it('should toggle display recent apps value in the store when pressed', () => { - const customInitialStoreState = { + it('should dispatch action to toggle display recent apps when pressed', () => { + const customInitialStoreState: RootState = { ...initialStoreState, preferences: { ...initialStoreState.preferences, @@ -63,14 +68,15 @@ describe(' Tests', () => { }, } - const { store } = renderWithProvider(, { preloadedState: customInitialStoreState }) + renderWithProvider(, { preloadedState: customInitialStoreState }) - const toggleRecentAppsSwitch = screen.getByTestId('settings-bottom-display-recent-apps-switch') + const toggleRecentAppsSwitch = screen.getByTestId('display-recent-apps-switch') expect(toggleRecentAppsSwitch).toBeOnTheScreen() + expect(toggleRecentAppsSwitch).not.toBeDisabled() fireEvent(toggleRecentAppsSwitch, 'valueChange') - expect(store.getState().preferences.displayRecentApps).toEqual(true) + expect(useDispatchMock).toBeCalledWith(displayRecentApps(true)) }) }) diff --git a/src/components/Settings/sections/RecentAppsSettings.tsx b/src/components/Settings/sections/RecentAppsSettings.tsx index f74f648..1cad67a 100644 --- a/src/components/Settings/sections/RecentAppsSettings.tsx +++ b/src/components/Settings/sections/RecentAppsSettings.tsx @@ -1,23 +1,17 @@ -// React import React from 'react' -// React Native import { Pressable, Switch, View } from 'react-native' -// Components -import SettingsItemLabel from '../shared/SettingsItemLabel' -// Redux import { useDispatch, useSelector } from 'react-redux' +import { displayRecentApps, selectDisplayRecentAppsMemoized } from '../../../slices/preferences' import { clearRecentApps } from '../../../slices/recentApps' -import { selectDisplayRecentAppsMemoized, displayRecentApps } from '../../../slices/preferences' -// Utils import { displayToast } from '../../../utils/toast' -// Constants +import SettingsItemLabel from '../shared/SettingsItemLabel' import { - switchTrackColor, activeSwitch, inActiveSwitch, settingItemButtonRippleConfig, - settingsPressableItemStyle, settingItemWrapperStyle, + settingsPressableItemStyle, + switchTrackColor, } from '../shared/values' const RecentAppsSettings = () => { @@ -38,7 +32,7 @@ const RecentAppsSettings = () => { { Tests should render correctly and match snapshot 1`] = ` -[ + - - Reload all apps - + + Reload all apps + + - , - - - Reset settings - + + Reset settings + + - , -] + + `; diff --git a/src/components/Settings/sections/__snapshots__/FavoriteAppsSettings.test.tsx.snap b/src/components/Settings/sections/__snapshots__/FavoriteAppsSettings.test.tsx.snap index d327350..9be2731 100644 --- a/src/components/Settings/sections/__snapshots__/FavoriteAppsSettings.test.tsx.snap +++ b/src/components/Settings/sections/__snapshots__/FavoriteAppsSettings.test.tsx.snap @@ -1,215 +1,235 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` Tests should render correctly and match snapshot 1`] = ` -[ + - - Display - - - + Display + + + - , - + testID="display-favorite-apps-switch" + thumbTintColor="#42855B" + tintColor="#e5e5e5" + value={true} + /> + - - Sort - - - Add more apps to be able to sort - + > + Sort + + + Add more apps to be able to sort + + - , - - - Clear - + + Clear + + - , -] + + `; diff --git a/src/components/Settings/sections/__snapshots__/RecentAppsSettings.test.tsx.snap b/src/components/Settings/sections/__snapshots__/RecentAppsSettings.test.tsx.snap index 2c25afe..b714861 100644 --- a/src/components/Settings/sections/__snapshots__/RecentAppsSettings.test.tsx.snap +++ b/src/components/Settings/sections/__snapshots__/RecentAppsSettings.test.tsx.snap @@ -1,128 +1,148 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` Tests should render correctly and match snapshot 1`] = ` -[ + - - Display - - - + Display + + + - , - + testID="display-recent-apps-switch" + thumbTintColor="#42855B" + tintColor="#e5e5e5" + value={true} + /> + - - Clear - + + Clear + + - , -] + + `; diff --git a/src/components/Settings/shared/SettingsItemLabel.test.tsx b/src/components/Settings/shared/SettingsItemLabel.test.tsx index 50fe03b..7ef544e 100644 --- a/src/components/Settings/shared/SettingsItemLabel.test.tsx +++ b/src/components/Settings/shared/SettingsItemLabel.test.tsx @@ -1,8 +1,7 @@ -import React from 'react' import { screen } from '@testing-library/react-native' +import React from 'react' import { renderWithProvider } from '../../../../utils/test/utils' -import { SettingsItemLabelProps } from '../../../models/props' -import SettingsItemLabel from '../shared/SettingsItemLabel' +import SettingsItemLabel, { SettingsItemLabelProps } from '../shared/SettingsItemLabel' describe(' Tests', () => { const requiredProps: SettingsItemLabelProps = { diff --git a/src/components/Settings/shared/SettingsItemLabel.tsx b/src/components/Settings/shared/SettingsItemLabel.tsx index e40b264..37f7e2d 100644 --- a/src/components/Settings/shared/SettingsItemLabel.tsx +++ b/src/components/Settings/shared/SettingsItemLabel.tsx @@ -1,11 +1,14 @@ -// React import React from 'react' -// React Native -import { StyleSheet, Text, View } from 'react-native' -// Models -import { SettingsItemLabelProps as Props } from '../../../models/props' +import { StyleProp, StyleSheet, Text, TextStyle, View, ViewStyle } from 'react-native' -const SettingsItemLabel = ({ title, description, titleStyle, wrapperStyle }: Props) => { +export type SettingsItemLabelProps = { + title: string + description?: string + titleStyle?: StyleProp + wrapperStyle?: StyleProp +} + +const SettingsItemLabel = ({ title, description, wrapperStyle, titleStyle }: SettingsItemLabelProps) => { return ( {title} diff --git a/src/components/Settings/shared/ToggleSettings.tsx b/src/components/Settings/shared/ToggleSettings.tsx index 266afb3..f7adee6 100644 --- a/src/components/Settings/shared/ToggleSettings.tsx +++ b/src/components/Settings/shared/ToggleSettings.tsx @@ -1,14 +1,12 @@ -// React -import React, { useState } from 'react' -// React Native +import React, { ReactNode, useState } from 'react' import { Pressable, StyleSheet, View } from 'react-native' -// Components -import SettingsItemLabel from './SettingsItemLabel' import CustomIcon from '../../shared/CustomIcon' -// Constants +import SettingsItemLabel, { SettingsItemLabelProps } from './SettingsItemLabel' import { settingItemButtonRippleConfig } from './values' -// Models -import { ToggleSettingsProps as Props } from '../../../models/props' + +type Props = SettingsItemLabelProps & { + children: ReactNode +} const ToggleSettings = ({ title, description, children }: Props) => { const [renderSettings, setRenderSettings] = useState(false) diff --git a/src/components/Settings/shared/__snapshots__/SettingsItemLabel.test.tsx.snap b/src/components/Settings/shared/__snapshots__/SettingsItemLabel.test.tsx.snap index 88d51d7..2991889 100644 --- a/src/components/Settings/shared/__snapshots__/SettingsItemLabel.test.tsx.snap +++ b/src/components/Settings/shared/__snapshots__/SettingsItemLabel.test.tsx.snap @@ -1,62 +1,106 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` Tests should render correctly and match snapshot - required and optional props 1`] = ` - - - An item label - - - An item description - - + + + An item label + + + An item description + + + + `; exports[` Tests should render correctly and match snapshot - required props 1`] = ` - - - An item label - - + + + An item label + + + + `; diff --git a/src/components/Settings/shared/values.ts b/src/components/Settings/shared/values.ts index b76a4f3..544b4f0 100644 --- a/src/components/Settings/shared/values.ts +++ b/src/components/Settings/shared/values.ts @@ -1,6 +1,4 @@ -// React Native import { PressableAndroidRippleConfig, StyleProp, ViewStyle } from 'react-native' -// Constants import { PRIMARY_COLOR } from '../../../constants' export const activeSwitch = PRIMARY_COLOR diff --git a/src/components/SortableFavoriteApps.tsx b/src/components/SortableFavoriteApps.tsx index 2741b1e..3ee80bf 100644 --- a/src/components/SortableFavoriteApps.tsx +++ b/src/components/SortableFavoriteApps.tsx @@ -1,20 +1,21 @@ -// React -import React, { useContext, useState } from 'react' -// React Native -import { Pressable, Image, StyleSheet, View, Animated, Easing, Text, PressableAndroidRippleConfig } from 'react-native' -// Components -import CustomIcon from './shared/CustomIcon' -// Redux -import { useDispatch, useSelector } from 'react-redux' -import { selectFavoriteAppsMemoized, setFavoriteApps } from '../slices/favoriteApps' -// Contexts -import GlobalContext from '../contexts/GlobalContext' -// DraggableFlatList +import React, { useEffect, useState } from 'react' +import { Pressable, PressableAndroidRippleConfig, StyleSheet, Text, View } from 'react-native' import DraggableFlatList, { RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlist' -// Constants -import { BACKGROUND_COLOR } from '../constants' -// Models +import Animated, { + cancelAnimation, + useAnimatedStyle, + useSharedValue, + withRepeat, + withTiming, +} from 'react-native-reanimated' +import { useDispatch, useSelector } from 'react-redux' +import { BACKGROUND_COLOR, WHITE_COLOR } from '../constants' import { FavoriteApp } from '../models/favorite-app' +import { setDisplaySortableFavoriteApps } from '../slices/appState' +import { selectFavoriteAppsMemoized, setFavoriteApps } from '../slices/favoriteApps' +import { getListKey } from '../utils/apps' +import AppIcon from './shared/AppIcon' +import CustomIcon from './shared/CustomIcon' const doneButtonRippleConfig: PressableAndroidRippleConfig = { borderless: true, @@ -23,35 +24,23 @@ const doneButtonRippleConfig: PressableAndroidRippleConfig = { radius: 10, } -const keyExtractor = ({ name }: FavoriteApp) => name - const SortableFavoriteApps = () => { const dispatch = useDispatch() const apps = useSelector(selectFavoriteAppsMemoized) - const { toggleSortableFavoriteApps } = useContext(GlobalContext) const [sorted, setSorted] = useState(false) - const animatedValueX = new Animated.Value(-1) + const rotateValue = useSharedValue(-5) + + useEffect(() => { + rotateValue.value = withRepeat(withTiming(5), -1, true) + + return () => cancelAnimation(rotateValue) + }, []) - Animated.loop( - Animated.sequence([ - Animated.timing(animatedValueX, { - toValue: 1, - duration: 200, - easing: Easing.linear, - useNativeDriver: true, - }), - Animated.timing(animatedValueX, { - toValue: -1, - duration: 200, - easing: Easing.linear, - useNativeDriver: true, - }), - ]) - ).start() + const animatedStyles = useAnimatedStyle(() => ({ transform: [{ rotate: `${rotateValue.value}deg` }] })) const doneSorting = () => { - toggleSortableFavoriteApps() + dispatch(setDisplaySortableFavoriteApps(false)) } const onDragEnd = async ({ data }: { data: FavoriteApp[] }) => { @@ -62,9 +51,9 @@ const SortableFavoriteApps = () => { const renderItem = ({ item, drag, isActive }: RenderItemParams) => { return ( - - - + + + @@ -80,7 +69,7 @@ const SortableFavoriteApps = () => { onPress={doneSorting} android_disableSound={true} android_ripple={doneButtonRippleConfig}> - + @@ -89,7 +78,8 @@ const SortableFavoriteApps = () => { data={apps} onDragEnd={onDragEnd} renderItem={renderItem} - keyExtractor={keyExtractor} + keyExtractor={getListKey} + keyboardShouldPersistTaps={'handled'} /> @@ -102,7 +92,7 @@ const styles = StyleSheet.create({ justifyContent: 'center', alignItems: 'center', height: 100, - borderRadius: 10, + borderRadius: 5, paddingVertical: 2.5, backgroundColor: BACKGROUND_COLOR, }, @@ -126,11 +116,11 @@ const styles = StyleSheet.create({ margin: 2.5, padding: 2.5, position: 'relative', - borderBottomColor: '#fff', + borderBottomColor: WHITE_COLOR, borderBottomWidth: 1, }, infoText: { - color: '#fff', + color: WHITE_COLOR, textShadowColor: 'rgba(0, 0, 0, 0.75)', textShadowRadius: 2.5, }, diff --git a/src/components/SortablePinnedApps.tsx b/src/components/SortablePinnedApps.tsx new file mode 100644 index 0000000..7984f49 --- /dev/null +++ b/src/components/SortablePinnedApps.tsx @@ -0,0 +1,136 @@ +import React, { useEffect, useState } from 'react' +import { Pressable, PressableAndroidRippleConfig, StyleSheet, Text, View } from 'react-native' +import DraggableFlatList, { RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlist' +import Animated, { + cancelAnimation, + useAnimatedStyle, + useSharedValue, + withRepeat, + withTiming, +} from 'react-native-reanimated' +import { useDispatch, useSelector } from 'react-redux' +import { BACKGROUND_COLOR, WHITE_COLOR } from '../constants' +import { PinnedApp } from '../models/pinned-app' +import { setDisplaySortablePinnedApps } from '../slices/appState' +import { selectPinnedAppsMemoized, setPinnedApps } from '../slices/pinnedApps' +import { getListKey } from '../utils/apps' +import AppIcon from './shared/AppIcon' +import CustomIcon from './shared/CustomIcon' + +const doneButtonRippleConfig: PressableAndroidRippleConfig = { + borderless: true, + foreground: false, + color: '#ccc', + radius: 10, +} + +const SortablePinnedApps = () => { + const dispatch = useDispatch() + const apps = useSelector(selectPinnedAppsMemoized) + const [sorted, setSorted] = useState(false) + + const rotateValue = useSharedValue(-5) + + useEffect(() => { + rotateValue.value = withRepeat(withTiming(5), -1, true) + + return () => cancelAnimation(rotateValue) + }, []) + + const animatedStyles = useAnimatedStyle(() => ({ transform: [{ rotate: `${rotateValue.value}deg` }] })) + + const doneSorting = () => { + dispatch(setDisplaySortablePinnedApps(false)) + } + + const onDragEnd = async ({ data }: { data: PinnedApp[] }) => { + dispatch(setPinnedApps({ apps: data, isPermanent: true })) + if (!sorted) setSorted(true) + } + + const renderItem = ({ item, drag, isActive }: RenderItemParams) => { + return ( + + + + + + + + ) + } + + return ( + + + Press and hold an app to start dragging + + + + + + + + + ) +} + +const styles = StyleSheet.create({ + wrapper: { + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + height: 100, + borderRadius: 5, + paddingVertical: 2.5, + backgroundColor: BACKGROUND_COLOR, + }, + draggableListWrapper: { + flex: 1, + }, + pressable: { + backgroundColor: 'transparent', + marginHorizontal: 10, + marginVertical: 0, + justifyContent: 'center', + alignItems: 'center', + height: '100%', + }, + image: { + width: 50, + height: 50, + }, + infoTextWrapper: { + width: '80%', + margin: 2.5, + padding: 2.5, + position: 'relative', + borderBottomColor: WHITE_COLOR, + borderBottomWidth: 1, + }, + infoText: { + color: WHITE_COLOR, + textShadowColor: 'rgba(0, 0, 0, 0.75)', + textShadowRadius: 2.5, + }, + doneButtonWrapper: { + top: 2.5, + right: 0, + padding: 2, + borderRadius: 50, + position: 'absolute', + }, +}) + +export default SortablePinnedApps diff --git a/src/components/SortableTemporaryPinnedApps.tsx b/src/components/SortableTemporaryPinnedApps.tsx new file mode 100644 index 0000000..e54c488 --- /dev/null +++ b/src/components/SortableTemporaryPinnedApps.tsx @@ -0,0 +1,136 @@ +import React, { useEffect, useState } from 'react' +import { Pressable, PressableAndroidRippleConfig, StyleSheet, Text, View } from 'react-native' +import DraggableFlatList, { RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlist' +import Animated, { + cancelAnimation, + useAnimatedStyle, + useSharedValue, + withRepeat, + withTiming, +} from 'react-native-reanimated' +import { useDispatch, useSelector } from 'react-redux' +import { BACKGROUND_COLOR, WHITE_COLOR } from '../constants' +import { PinnedApp } from '../models/pinned-app' +import { setDisplaySortableTemporaryPinnedApps } from '../slices/appState' +import { selectTemporaryPinnedAppsMemoized, setPinnedApps } from '../slices/pinnedApps' +import { getListKey } from '../utils/apps' +import AppIcon from './shared/AppIcon' +import CustomIcon from './shared/CustomIcon' + +const doneButtonRippleConfig: PressableAndroidRippleConfig = { + borderless: true, + foreground: false, + color: '#ccc', + radius: 10, +} + +const SortableTemporaryPinnedApps = () => { + const dispatch = useDispatch() + const apps = useSelector(selectTemporaryPinnedAppsMemoized) + const [sorted, setSorted] = useState(false) + + const rotateValue = useSharedValue(-5) + + useEffect(() => { + rotateValue.value = withRepeat(withTiming(5), -1, true) + + return () => cancelAnimation(rotateValue) + }, []) + + const animatedStyles = useAnimatedStyle(() => ({ transform: [{ rotate: `${rotateValue.value}deg` }] })) + + const doneSorting = () => { + dispatch(setDisplaySortableTemporaryPinnedApps(false)) + } + + const onDragEnd = async ({ data }: { data: PinnedApp[] }) => { + dispatch(setPinnedApps({ apps: data, isPermanent: false })) + if (!sorted) setSorted(true) + } + + const renderItem = ({ item, drag, isActive }: RenderItemParams) => { + return ( + + + + + + + + ) + } + + return ( + + + Press and hold an app to start dragging + + + + + + + + + ) +} + +const styles = StyleSheet.create({ + wrapper: { + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + height: 100, + borderRadius: 5, + paddingVertical: 2.5, + backgroundColor: BACKGROUND_COLOR, + }, + draggableListWrapper: { + flex: 1, + }, + pressable: { + backgroundColor: 'transparent', + marginHorizontal: 10, + marginVertical: 0, + justifyContent: 'center', + alignItems: 'center', + height: '100%', + }, + image: { + width: 50, + height: 50, + }, + infoTextWrapper: { + width: '80%', + margin: 2.5, + padding: 2.5, + position: 'relative', + borderBottomColor: WHITE_COLOR, + borderBottomWidth: 1, + }, + infoText: { + color: WHITE_COLOR, + textShadowColor: 'rgba(0, 0, 0, 0.75)', + textShadowRadius: 2.5, + }, + doneButtonWrapper: { + top: 2.5, + right: 0, + padding: 2, + borderRadius: 50, + position: 'absolute', + }, +}) + +export default SortableTemporaryPinnedApps diff --git a/src/components/TemporaryPinnedApps.tsx b/src/components/TemporaryPinnedApps.tsx index 37578e1..e384063 100644 --- a/src/components/TemporaryPinnedApps.tsx +++ b/src/components/TemporaryPinnedApps.tsx @@ -1,76 +1,45 @@ -// React Native +import React from 'react' import { FlatList, ListRenderItem, ListRenderItemInfo, StyleSheet, Text, View } from 'react-native' -// Components -import AppItem from './AppItem' -// Redux import { useSelector } from 'react-redux' -import { selectTemporaryPinnedAppsMemoized } from '../slices/pinnedApps' -// Constants -import { APP_ITEM_HEIGHT_ICON_DISPLAYED, BACKGROUND_COLOR } from '../constants' -import { singleRowAppsViewStyle, whiteTextColorStyle } from '../shared/styles' -// Models -import { RenderedIn } from '../models/rendered-in' import { PinnedApp } from '../models/pinned-app' -import { AppDetails } from '../models/app-details' - -const keyExtractor = ({ name }: AppDetails) => name -const getItemLayout = (_data: unknown, index: number) => ({ - length: APP_ITEM_HEIGHT_ICON_DISPLAYED, - offset: APP_ITEM_HEIGHT_ICON_DISPLAYED * index, - index, -}) +import { RenderedIn } from '../models/rendered-in' +import { sectionHeaderLabelStyle, sectionHeaderWrapperStyle, sectionWrapper } from '../shared/styles' +import { selectTemporaryPinnedAppsMemoized } from '../slices/pinnedApps' +import { getListItemLayout, getListKey } from '../utils/apps' +import AppItem from './AppItem' +import EmptyListComponent from './shared/EmptyListComponent' const TemporaryPinnedApps = () => { const apps = useSelector(selectTemporaryPinnedAppsMemoized) const renderItem: ListRenderItem = ({ item }: ListRenderItemInfo) => ( - + ) return ( - - - Temporarliy Pinned + + + Temporarliy Pinned + + + } + /> - {apps.length > 0 ? ( - - - - ) : ( - - No temporarliy pinned apps yet - - )} ) } const styles = StyleSheet.create({ - wrapper: { - borderRadius: 5, - backgroundColor: BACKGROUND_COLOR, - }, - headerLabel: { - color: 'rgba(255,255,255,0.75)', - fontSize: 12, - }, - headerWrapper: { - paddingVertical: 2.5, - paddingHorizontal: 10, - borderBottomWidth: 1, - borderBottomColor: 'rgba(255,255,255,0.5)', - }, - verticalAppsWrapper: { - flexDirection: 'row', - justifyContent: 'flex-start', + appsWrapper: { padding: 5, }, }) diff --git a/src/components/__snapshots__/AllAppsIcon.test.tsx.snap b/src/components/__snapshots__/AllAppsIcon.test.tsx.snap index c97cd2c..cc8415a 100644 --- a/src/components/__snapshots__/AllAppsIcon.test.tsx.snap +++ b/src/components/__snapshots__/AllAppsIcon.test.tsx.snap @@ -1,69 +1,180 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` Tests should render correctly and match snapshot 1`] = ` - - - ó°‹™ - + + + + ó°‹™ + + + + - + `; diff --git a/src/components/__snapshots__/AllAppsLetterIndex.test.tsx.snap b/src/components/__snapshots__/AllAppsLetterIndex.test.tsx.snap index ff484a7..c0b2356 100644 --- a/src/components/__snapshots__/AllAppsLetterIndex.test.tsx.snap +++ b/src/components/__snapshots__/AllAppsLetterIndex.test.tsx.snap @@ -1,148 +1,192 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` Tests should render correctly and match snapshot - empty list 1`] = ` - - - - - + + + + + + + `; exports[` Tests should render correctly and match snapshot - populated list 1`] = ` - - - - + - - + - C - + + + C + + + - + - - + + `; diff --git a/src/components/__snapshots__/HighlightText.test.tsx.snap b/src/components/__snapshots__/HighlightText.test.tsx.snap index ee5208c..a2ba89e 100644 --- a/src/components/__snapshots__/HighlightText.test.tsx.snap +++ b/src/components/__snapshots__/HighlightText.test.tsx.snap @@ -1,47 +1,91 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` Tests should render correctly and match snapshot - with search query 1`] = ` - - - - Chro - - - me - - + + + + Chro + + + me + + + + `; exports[` Tests should render correctly and match snapshot - without search query 1`] = ` - - Chrome - + + + Chrome + + + `; diff --git a/src/components/__snapshots__/Search.test.tsx.snap b/src/components/__snapshots__/Search.test.tsx.snap index 3c5120d..2c12ef7 100644 --- a/src/components/__snapshots__/Search.test.tsx.snap +++ b/src/components/__snapshots__/Search.test.tsx.snap @@ -1,109 +1,255 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` Tests should render correctly and match snapshot 1`] = ` - - - + > + + + + + `; exports[` Tests should render correctly and match snapshot with clear button 1`] = ` - - - - - ó°…– - + + + + + + ó°…– + + + + + - + `; diff --git a/src/components/shared/AppIcon.tsx b/src/components/shared/AppIcon.tsx new file mode 100644 index 0000000..24c49c9 --- /dev/null +++ b/src/components/shared/AppIcon.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { Image, ImageStyle, StyleProp } from 'react-native' + +type Props = { + icon: string + style: StyleProp +} + +const AppIcon = ({ icon, style }: Props) => { + return +} + +export default AppIcon diff --git a/src/components/shared/CustomIcon.tsx b/src/components/shared/CustomIcon.tsx index 02f9ebe..9ce5827 100644 --- a/src/components/shared/CustomIcon.tsx +++ b/src/components/shared/CustomIcon.tsx @@ -1,9 +1,12 @@ -// React import React from 'react' -// Icon import Icon from 'react-native-vector-icons/MaterialCommunityIcons' -// Models -import { CustomIconProps as Props } from '../../models/props' + +type Props = { + name: string + size: number + color: string + style?: Record +} const CustomIcon = ({ name, size, color, style }: Props) => { return diff --git a/src/components/shared/EmptyListComponent.tsx b/src/components/shared/EmptyListComponent.tsx new file mode 100644 index 0000000..51c31d1 --- /dev/null +++ b/src/components/shared/EmptyListComponent.tsx @@ -0,0 +1,17 @@ +import React from 'react' +import { Text, View } from 'react-native' +import { noAppsViewStyle, whiteTextColorStyle } from '../../shared/styles' + +type Props = { + text: string +} + +const EmptyListComponent = ({ text }: Props) => { + return ( + + {text} + + ) +} + +export default EmptyListComponent diff --git a/src/constants/app.ts b/src/constants/app.ts index 82580ae..162d271 100644 --- a/src/constants/app.ts +++ b/src/constants/app.ts @@ -1,20 +1,17 @@ -// Modules import AppsModule from '../native-modules/AppsModule' // Constant values -export const { - packageName: CONTEXT_LAUNCHER_APP_ID, - appVersion: CONTEXT_LAUNCHER_APP_VERSION, - buildNumber: CONTEXT_LAUNCHER_APP_BUILD_NUMBER, -} = AppsModule.getConstants() -export const APP_ITEM_HEIGHT_ICON_DISPLAYED = 60 +export const { packageName: APP_ID, appVersion: APP_VERSION, buildNumber: APP_BUILD_NUMBER } = AppsModule.getConstants() +export const APP_ITEM_HEIGHT = 60 +export const BOTTOM_CONTAINER_HEIGHT = 48.8 +export const BOTTOM_CONTAINER_HEIGHT_WITH_PADDINGS = BOTTOM_CONTAINER_HEIGHT + 10 // Colors export const WHITE_COLOR = '#FFFFFF' export const PRIMARY_COLOR = '#42855B' export const SECONDARY_COLOR = '#A0C2AD' -export const BACKGROUND_COLOR = 'rgba(255, 255, 255, 0.2)' +export const NEUTRAL_COLOR = '#424242' +export const DISABLED_COLOR = '#D3D3D3' +export const BACKGROUND_COLOR = 'rgba(255, 255, 255, .2)' +export const PRESSABLE_RIPPLE_COLOR = 'rgba(255, 255, 255, .3)' // Native export const HARDWARE_BACK_PRESS_EVENT_NAME = 'hardwareBackPress' -// React Native -export const KEYBOARD_DID_SHOW_EVENT_NAME = 'keyboardDidShow' -export const KEYBOARD_DID_HIDE_EVENT_NAME = 'keyboardDidHide' diff --git a/src/containers/BottomContainer.tsx b/src/containers/BottomContainer.tsx index ee55d8a..0c7615c 100644 --- a/src/containers/BottomContainer.tsx +++ b/src/containers/BottomContainer.tsx @@ -1,18 +1,14 @@ -// React -import React, { useContext, useMemo } from 'react' -// React Native +import React, { useMemo } from 'react' import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native' -// Components -import Search from '../components/Search' +import { useSelector } from 'react-redux' import AllAppsIcon from '../components/AllAppsIcon' +import Search from '../components/Search' import SettingsIcon from '../components/Settings/SettingsIcon' -// Contexts -import GlobalContext from '../contexts/GlobalContext' -// Constants import { SECONDARY_COLOR, WHITE_COLOR } from '../constants' +import { selectDisplayAllApps } from '../slices/appState' const BottomContainer = () => { - const { displayAllApps } = useContext(GlobalContext) + const displayAllApps = useSelector(selectDisplayAllApps) const adaptiveStyle: StyleProp = useMemo(() => { return { backgroundColor: displayAllApps ? SECONDARY_COLOR : WHITE_COLOR } @@ -31,6 +27,7 @@ const styles = StyleSheet.create({ wrapper: { borderRadius: 5, flexDirection: 'row', + alignItems: 'center', }, }) diff --git a/src/containers/TopContainer.tsx b/src/containers/TopContainer.tsx index 1936cf6..e89ee80 100644 --- a/src/containers/TopContainer.tsx +++ b/src/containers/TopContainer.tsx @@ -1,28 +1,29 @@ -// React -import { useContext } from 'react' -// React Native +import React from 'react' import { StyleSheet, View } from 'react-native' -// Redux import { useSelector } from 'react-redux' -import { selectAppsSearchQuery } from '../slices/appsSearch' -import { - selectDisplayFavoriteAppsMemoized, - selectDisplayPinnedAppsMemoized, - selectDisplayRecentAppsMemoized, - selectDisplayTemporaryPinnedAppsMemoized, -} from '../slices/preferences' -// Components import AllApps from '../components/AllApps' -import RecentApps from '../components/RecentApps' import FavoriteApps from '../components/FavoriteApps' import FilteredApps from '../components/FilteredApps' -import SortableFavoriteApps from '../components/SortableFavoriteApps' import PinnedApps from '../components/PinnedApps' +import RecentApps from '../components/RecentApps' +import SortableFavoriteApps from '../components/SortableFavoriteApps' +import SortablePinnedApps from '../components/SortablePinnedApps' +import SortableTemporaryPinnedApps from '../components/SortableTemporaryPinnedApps' import TemporarliyPinnedApps from '../components/TemporaryPinnedApps' -// Hooks import { useTimeBasedRendering } from '../hooks/useTimeBasedRendering' -// Contexts -import GlobalContext from '../contexts/GlobalContext' +import { + selectAppsSearchQuery, + selectDisplayAllApps, + selectDisplaySortableFavoriteApps, + selectDisplaySortablePinnedApps, + selectDisplaySortableTemporaryPinnedApps, +} from '../slices/appState' +import { + selectDisplayFavoriteAppsMemoized, + selectDisplayPinnedAppsMemoized, + selectDisplayRecentAppsMemoized, + selectDisplayTemporaryPinnedAppsMemoized, +} from '../slices/preferences' const TopContainer = () => { const searchQuery = useSelector(selectAppsSearchQuery) @@ -30,7 +31,10 @@ const TopContainer = () => { const displayPinnedApps = useSelector(selectDisplayPinnedAppsMemoized) const displayTemporaryPinnedApps = useSelector(selectDisplayTemporaryPinnedAppsMemoized) const displayFavoriteApps = useSelector(selectDisplayFavoriteAppsMemoized) - const { displayAllApps, sortableFavoriteApps } = useContext(GlobalContext) + const sortableFavoriteApps = useSelector(selectDisplaySortableFavoriteApps) + const sortablePinnedApps = useSelector(selectDisplaySortablePinnedApps) + const sortableTemporaryPinnedApps = useSelector(selectDisplaySortableTemporaryPinnedApps) + const displayAllApps = useSelector(selectDisplayAllApps) const canRender = useTimeBasedRendering() return ( @@ -54,16 +58,26 @@ const TopContainer = () => { )} {/* Pinned apps */} - {displayTemporaryPinnedApps && canRender && ( + {displayTemporaryPinnedApps && canRender && !sortableTemporaryPinnedApps && ( )} - {displayPinnedApps && ( + {displayTemporaryPinnedApps && canRender && sortableTemporaryPinnedApps && ( + + + + )} + {displayPinnedApps && !sortablePinnedApps && ( )} + {displayPinnedApps && sortablePinnedApps && ( + + + + )} {/* Favorite apps */} {displayFavoriteApps && !sortableFavoriteApps && ( diff --git a/src/contexts/GlobalContext.ts b/src/contexts/GlobalContext.ts deleted file mode 100644 index 84f73dd..0000000 --- a/src/contexts/GlobalContext.ts +++ /dev/null @@ -1,8 +0,0 @@ -// React -import { createContext } from 'react' -// Models -import { GlobalContextType } from '../models/context' - -const GlobalContext = createContext({} as GlobalContextType) - -export default GlobalContext diff --git a/src/contexts/GlobalContextWrapper.tsx b/src/contexts/GlobalContextWrapper.tsx deleted file mode 100644 index 28e531b..0000000 --- a/src/contexts/GlobalContextWrapper.tsx +++ /dev/null @@ -1,77 +0,0 @@ -// React -import React, { RefObject, useRef, useState } from 'react' -// Context -import GlobalContext from './GlobalContext' -// Hooks -import { useKeyboard } from '../hooks/useKeyboard' -// Utils -import { dismissKeyboard } from '../utils/keyboard' -// BottomSheet -import { BottomSheetModal } from '@gorhom/bottom-sheet' -// Models -import { AppDetailsWithOptionalIcon } from '../models/app-details' -import { GlobalContextWrapperProps as Props } from '../models/props' - -const GlobalContextWrapper = ({ children }: Props) => { - const keyboard = useKeyboard() - const [displayAllApps, setDisplayAllApps] = useState(false) - const [sortableFavoriteApps, setSortableFavoriteApps] = useState(false) - const settingsBottomSheetRef: RefObject | null = useRef(null) - const appItemMenuBottomSheetRef: RefObject | null = useRef(null) - const [appItemMenuDetails, setAppItemMenuDetails] = useState(null) - - const hideAllApps = () => { - if (displayAllApps) setDisplayAllApps(false) - } - - const appLaunchProcedure = () => { - // Hide all apps view - hideAllApps() - // Hide favorite apps sort view - if (sortableFavoriteApps) setSortableFavoriteApps(false) - // Close settings bottom sheet - settingsBottomSheetRef.current?.dismiss() - // Close app item menu sheet - appItemMenuBottomSheetRef.current?.dismiss() - // Reset app menu data - if (!appItemMenuDetails) setAppItemMenuDetails(null) - } - - const closeKeyboard = () => { - if (keyboard.isShown) dismissKeyboard() - } - - const toggleSortableFavoriteApps = () => { - if (!sortableFavoriteApps) { - // Hide all apps view - hideAllApps() - // Close settings bottom sheet - settingsBottomSheetRef.current?.dismiss() - } - - setSortableFavoriteApps(!sortableFavoriteApps) - } - - return ( - setDisplayAllApps(!displayAllApps), - settingsBottomSheetRef, - displaySettingsBottomSheet: () => settingsBottomSheetRef.current?.present(), - appItemMenuDetails, - setAppItemMenuDetails: (appDetails: AppDetailsWithOptionalIcon) => setAppItemMenuDetails(appDetails), - appItemMenuBottomSheetRef, - displayAppItemMenuBottomSheet: () => appItemMenuBottomSheetRef.current?.present(), - sortableFavoriteApps, - toggleSortableFavoriteApps, - dismissKeyboard: closeKeyboard, - }}> - {children} - - ) -} - -export default GlobalContextWrapper diff --git a/src/contexts/SearchContext.ts b/src/contexts/SearchContext.ts index 4af97b1..ceba28d 100644 --- a/src/contexts/SearchContext.ts +++ b/src/contexts/SearchContext.ts @@ -1,7 +1,11 @@ -// React -import { createContext } from 'react' -// Models -import { SearchContextType } from '../models/context' +import { createContext, RefObject } from 'react' +import { TextInput } from 'react-native-gesture-handler' + +export type SearchContextType = { + searchInputRef: RefObject | null + clearSearchInput: () => void + searchAppLaunchProcedure: () => void +} const SearchContext = createContext({} as SearchContextType) diff --git a/src/contexts/SearchContextWrapper.tsx b/src/contexts/SearchContextWrapper.tsx index e62f765..5148744 100644 --- a/src/contexts/SearchContextWrapper.tsx +++ b/src/contexts/SearchContextWrapper.tsx @@ -1,39 +1,29 @@ -// React -import React, { RefObject, useRef, useState } from 'react' -// React Native +import React, { ReactNode, RefObject, useMemo, useRef } from 'react' import { TextInput } from 'react-native' -// Context -import SearchContext from './SearchContext' -// Utils -import { dismissKeyboard } from '../utils/keyboard' -// Custom Hooks -import { useKeyboard } from '../hooks/useKeyboard' -// Models -import { SearchContextWrapperProps as Props } from '../models/props' +import SearchContext, { SearchContextType } from './SearchContext' + +type Props = { + children: ReactNode +} const SearchContextWrapper = ({ children }: Props) => { - const keyboard = useKeyboard() const searchInputRef: RefObject | null = useRef(null) - const [invalidCharacters, setInvalidCharacters] = useState(false) - const dismissKeyboardAndBlurSearchInput = () => { - // Dismiss keyboard - if (keyboard.isShown) dismissKeyboard() - // Remove search input focus - if (searchInputRef.current?.isFocused()) searchInputRef.current?.blur() + const clearSearchInput = () => { + searchInputRef.current?.clear() } - return ( - setInvalidCharacters(isInvalid), - }}> - {children} - + const searchAppLaunchProcedure = () => { + clearSearchInput() + searchInputRef.current?.blur() + } + + const value: SearchContextType = useMemo( + () => ({ searchInputRef, clearSearchInput, searchAppLaunchProcedure } as SearchContextType), + [searchInputRef] ) + + return {children} } export default SearchContextWrapper diff --git a/src/hooks/useBackHandler.ts b/src/hooks/useBackHandler.ts index 062e369..4512b1c 100644 --- a/src/hooks/useBackHandler.ts +++ b/src/hooks/useBackHandler.ts @@ -1,14 +1,13 @@ -// React import { useEffect } from 'react' -// React Native import { BackHandler } from 'react-native' -// Constants import { HARDWARE_BACK_PRESS_EVENT_NAME } from '../constants' export const useBackHandler = (handler: () => boolean) => { useEffect(() => { BackHandler.addEventListener(HARDWARE_BACK_PRESS_EVENT_NAME, handler) - return () => BackHandler.removeEventListener(HARDWARE_BACK_PRESS_EVENT_NAME, handler) + return () => { + BackHandler.removeEventListener(HARDWARE_BACK_PRESS_EVENT_NAME, handler) + } }, [handler]) } diff --git a/src/hooks/useKeyboard.ts b/src/hooks/useKeyboard.ts deleted file mode 100644 index 9bedae6..0000000 --- a/src/hooks/useKeyboard.ts +++ /dev/null @@ -1,24 +0,0 @@ -// React -import { useEffect, useState } from 'react' -// React Native -import { EmitterSubscription, Keyboard, KeyboardEventListener } from 'react-native' -// Constants -import { KEYBOARD_DID_HIDE_EVENT_NAME, KEYBOARD_DID_SHOW_EVENT_NAME } from '../constants' - -export const useKeyboard = () => { - const [shown, setShown] = useState(false) - - const handleKeyboardDidShow: KeyboardEventListener = () => setShown(true) - const handleKeyboardDidHide: KeyboardEventListener = () => setShown(false) - - useEffect(() => { - const subscriptions: EmitterSubscription[] = [ - Keyboard.addListener(KEYBOARD_DID_SHOW_EVENT_NAME, handleKeyboardDidShow), - Keyboard.addListener(KEYBOARD_DID_HIDE_EVENT_NAME, handleKeyboardDidHide), - ] - - return () => subscriptions.forEach(subscription => subscription.remove()) - }, []) - - return { isShown: shown } -} diff --git a/src/hooks/usePackageChange.ts b/src/hooks/usePackageChange.ts index 9c00658..a501b2d 100644 --- a/src/hooks/usePackageChange.ts +++ b/src/hooks/usePackageChange.ts @@ -1,10 +1,6 @@ -// React import { useEffect } from 'react' -// React Native import { NativeEventEmitter, NativeModules } from 'react-native' -// Constants import { PACKAGE_CHANGE_EVENT_NAME } from '../constants' -// Models import { PackageChange } from '../models/event' export const usePackageChange = (callback: (packageChange: PackageChange) => void) => { diff --git a/src/hooks/useTimeBasedRendering.ts b/src/hooks/useTimeBasedRendering.ts index 77a0fab..7168f67 100644 --- a/src/hooks/useTimeBasedRendering.ts +++ b/src/hooks/useTimeBasedRendering.ts @@ -1,6 +1,4 @@ -// React import { useEffect, useState } from 'react' -// Redux import { useSelector } from 'react-redux' import { selectTemporaryPinnedAppsConfigMemoized } from '../slices/pinnedApps' @@ -22,12 +20,15 @@ export const useTimeBasedRendering = () => { }, [temporaryPinnedAppsConfig.startDate, temporaryPinnedAppsConfig.endDate]) useEffect(() => { - let startRenderingTimeOut: NodeJS.Timeout | undefined = undefined - let stopRenderingTimeOut: NodeJS.Timeout | undefined = undefined + let startRenderingTimeOut: NodeJS.Timeout | undefined + let stopRenderingTimeOut: NodeJS.Timeout | undefined - const { startRenderingTimeOutId, stopRenderingTimeOutId } = check() - startRenderingTimeOut = startRenderingTimeOutId - stopRenderingTimeOut = stopRenderingTimeOutId + const { + startRenderingTimeOutId: initialStartRenderingTimeOutId, + stopRenderingTimeOutId: initialStopRenderingTimeOutId, + } = check() + startRenderingTimeOut = initialStartRenderingTimeOutId + stopRenderingTimeOut = initialStopRenderingTimeOutId const intervalId = setInterval(() => { const { startRenderingTimeOutId, stopRenderingTimeOutId } = check() @@ -43,8 +44,8 @@ export const useTimeBasedRendering = () => { }, [startDate, endDate]) const check = (): CheckResult => { - let startRenderingTimeOut: NodeJS.Timeout | undefined = undefined - let stopRenderingTimeOut: NodeJS.Timeout | undefined = undefined + let startRenderingTimeOut: NodeJS.Timeout | undefined + let stopRenderingTimeOut: NodeJS.Timeout | undefined if (!startDate || !endDate) { setCanRender(false) @@ -87,8 +88,8 @@ export const useTimeBasedRendering = () => { startRenderingTimeOut = setTimeout(() => setCanRender(true), timeToStartRendering) stopRenderingTimeOut = setTimeout(() => setCanRender(false), timeToStopRendering) } else if (timeToStartRendering < 0 && timeToStopRendering > 0) { - const timeToStopRendering = endDate.getTime() - currentDate.getTime() - stopRenderingTimeOut = setTimeout(() => setCanRender(false), timeToStopRendering) + const innerTimeToStopRendering = endDate.getTime() - currentDate.getTime() + stopRenderingTimeOut = setTimeout(() => setCanRender(false), innerTimeToStopRendering) setCanRender(true) } else { startRenderingTimeOut = undefined diff --git a/src/models/app-details.ts b/src/models/app-details.ts index fce7740..66d41fb 100644 --- a/src/models/app-details.ts +++ b/src/models/app-details.ts @@ -1,8 +1,5 @@ export type AppDetails = { - label: string + packageName: string name: string + icon: string } - -export type AppDetailsWithOptionalIcon = AppDetails & { icon?: string } - -export type AppDetailsWithIcon = AppDetails & { icon: string } diff --git a/src/models/context.ts b/src/models/context.ts deleted file mode 100644 index feb1535..0000000 --- a/src/models/context.ts +++ /dev/null @@ -1,39 +0,0 @@ -// React -import { RefObject } from 'react' -// React Native -import { TextInput } from 'react-native' -// BottomSheet -import { BottomSheetModal } from '@gorhom/bottom-sheet' -// Models -import { AppDetailsWithOptionalIcon } from '../models/app-details' - -export type GlobalContextType = { - // All apps - hideAllApps: () => void - displayAllApps: boolean - toggleDisplayAllApps: () => void - // Settings bottom sheet - settingsBottomSheetRef: RefObject | null - displaySettingsBottomSheet: () => void - // App item menu - appItemMenuDetails: AppDetailsWithOptionalIcon | null - setAppItemMenuDetails: (appDetails: AppDetailsWithOptionalIcon) => void - appItemMenuBottomSheetRef: RefObject | null - displayAppItemMenuBottomSheet: () => void - // Favorite apps sort - sortableFavoriteApps: boolean - toggleSortableFavoriteApps: () => void - // Global app launch procedure - globalAppLaunchProcedure: () => void - // Misc - dismissKeyboard: () => void -} - -export type SearchContextType = { - // Search input - searchInputRef: RefObject | null - searchAppLaunchProcedure: () => void - // Search query validity - invalidCharacters: boolean - setInvalidCharacters: (isInvalid: boolean) => void -} diff --git a/src/models/favorite-app.ts b/src/models/favorite-app.ts index c58017f..5c0d320 100644 --- a/src/models/favorite-app.ts +++ b/src/models/favorite-app.ts @@ -1,4 +1,3 @@ -// Models -import { AppDetailsWithIcon } from './app-details' +import { AppDetails } from './app-details' -export type FavoriteApp = AppDetailsWithIcon +export type FavoriteApp = AppDetails diff --git a/src/models/global-state.ts b/src/models/global-state.ts deleted file mode 100644 index bc48b32..0000000 --- a/src/models/global-state.ts +++ /dev/null @@ -1,14 +0,0 @@ -export type GlobalProperties = { - os: string - osVersion: string - reactNativeVersion: string - isTesting: string - uiMode: string - manufacturer: string - brand: string - model: string - serial: string - release: string - fingerprint: string - version: string -} diff --git a/src/models/native-module.ts b/src/models/native-module.ts deleted file mode 100644 index e562135..0000000 --- a/src/models/native-module.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface AppsModuleInterface { - getConstants(): AppsModuleConstants - launchApplication(packageName: string): void - showApplicationDetails(packageName: string): void - requestApplicationUninstall(packageName: string): void - getApplications(callback: (applications: string) => void): void - getApplicationIcon(packageName: string, callback: (nativeAppIcon: string) => void): void -} - -type AppsModuleConstants = { - appVersion: string - buildNumber: string - packageName: string -} diff --git a/src/models/pinned-app.ts b/src/models/pinned-app.ts index 02ddba5..ec3cda7 100644 --- a/src/models/pinned-app.ts +++ b/src/models/pinned-app.ts @@ -1,10 +1,6 @@ -// Models -import { AppDetailsWithIcon } from './app-details' +import { AppDetails } from './app-details' -export type PinnedApp = AppDetailsWithIcon & { - isPermanent: boolean - isTemporary: boolean -} +export type PinnedApp = AppDetails export type TemporaryPinnedAppsConfig = { startDate?: string @@ -13,5 +9,6 @@ export type TemporaryPinnedAppsConfig = { export type PinnedAppsState = { list: PinnedApp[] + temporarily: PinnedApp[] temporaryAppsConfig: TemporaryPinnedAppsConfig } diff --git a/src/models/props.ts b/src/models/props.ts deleted file mode 100644 index 12498e4..0000000 --- a/src/models/props.ts +++ /dev/null @@ -1,57 +0,0 @@ -// React -import { ReactNode } from 'react' -// React Native -import { StyleProp, TextStyle, ViewStyle } from 'react-native' -import { BaseAnimationBuilder } from 'react-native-reanimated' -// Models -import { AppDetails } from './app-details' -import { RenderedIn } from './rendered-in' - -export type AppItemProps = { - appDetails: AppDetails - renderedIn: RenderedIn - appIcon?: string - wrapperStyle?: StyleProp - pressableStyle?: StyleProp -} - -export type HighlightTextProps = { - text: string -} - -export type SearchContextWrapperProps = { - children: ReactNode -} - -export type GlobalContextWrapperProps = { - children: ReactNode -} - -export type AllAppsLetterIndexProps = { - onPress: (letterIndex: number) => void -} - -export type SettingsItemLabelProps = { - title: string - description?: string - titleStyle?: StyleProp - wrapperStyle?: StyleProp -} - -export type ToggleSettingsProps = SettingsItemLabelProps & { - children: ReactNode -} - -export type CustomViewProps = { - children: ReactNode - style?: StyleProp - exitAnimation?: BaseAnimationBuilder | typeof BaseAnimationBuilder - entryAnimation?: BaseAnimationBuilder | typeof BaseAnimationBuilder -} - -export type CustomIconProps = { - name: string - size: number - color: string - style?: Record -} diff --git a/src/models/recent-app.ts b/src/models/recent-app.ts index 20b2c42..358628e 100644 --- a/src/models/recent-app.ts +++ b/src/models/recent-app.ts @@ -1,6 +1,3 @@ -// Models -import { AppDetailsWithIcon, AppDetailsWithOptionalIcon } from './app-details' +import { AppDetails } from './app-details' -export type RecentAppDetails = AppDetailsWithIcon - -export type RecentAppDetailsWithOptionalIcon = AppDetailsWithOptionalIcon +export type RecentApp = AppDetails diff --git a/src/native-modules/AppsModule.ts b/src/native-modules/AppsModule.ts index e30ee6e..22f0db8 100644 --- a/src/native-modules/AppsModule.ts +++ b/src/native-modules/AppsModule.ts @@ -1,7 +1,18 @@ -// React Native import { NativeModules } from 'react-native' -// Models -import { AppsModuleInterface } from '../models/native-module' + +export interface AppsModuleInterface { + getConstants(): AppsModuleConstants + launchApplication(packageName: string): void + showApplicationDetails(packageName: string): void + requestApplicationUninstall(packageName: string): void + getApplications(): Promise +} + +type AppsModuleConstants = { + appVersion: string + buildNumber: string + packageName: string +} const { AppsModule } = NativeModules diff --git a/src/rootSaga.ts b/src/rootSaga.ts new file mode 100644 index 0000000..a31b79e --- /dev/null +++ b/src/rootSaga.ts @@ -0,0 +1,7 @@ +import { all } from 'redux-saga/effects' +import { appsListSagas } from './slices/appsListHandler' +import { appStateSaga } from './slices/appStateHandler' + +export default function* rootSaga() { + yield all([appStateSaga(), appsListSagas()]) +} diff --git a/src/shared/bottom-container/index.ts b/src/shared/bottom-container/index.ts index 6aec8a8..65f53a8 100644 --- a/src/shared/bottom-container/index.ts +++ b/src/shared/bottom-container/index.ts @@ -1,24 +1,6 @@ -// React Native -import { StyleSheet, PressableProps } from 'react-native' -// Constants -import { SECONDARY_COLOR } from '../../constants' +import { ViewStyle } from 'react-native' -export const iconsStyle = StyleSheet.create({ - wrapper: { - alignItems: 'center', - flexDirection: 'row', - }, - icon: { - margin: 5, - }, -}) - -export const iconsPressableConfig: PressableProps = { - android_disableSound: true, - android_ripple: { - color: SECONDARY_COLOR, - borderless: false, - foreground: true, - radius: 20, - }, +export const iconButtonStyle: ViewStyle = { + margin: 0, + borderRadius: 0, } diff --git a/src/shared/styles.ts b/src/shared/styles.ts index 150eccb..f718870 100644 --- a/src/shared/styles.ts +++ b/src/shared/styles.ts @@ -1,12 +1,29 @@ -// React Native import { StyleProp, TextStyle, ViewStyle } from 'react-native' +import { BACKGROUND_COLOR, WHITE_COLOR } from '../constants' export const whiteTextColorStyle: StyleProp = { - color: '#fff', + color: WHITE_COLOR, } -export const singleRowAppsViewStyle: StyleProp = { - minHeight: 70, +export const noAppsViewStyle: StyleProp = { + minHeight: 62, alignItems: 'center', justifyContent: 'center', } + +export const sectionWrapper: StyleProp = { + borderRadius: 5, + backgroundColor: BACKGROUND_COLOR, +} + +export const sectionHeaderWrapperStyle: StyleProp = { + paddingLeft: 10, + paddingVertical: 2.5, + borderBottomWidth: 1, + borderBottomColor: 'rgba(255, 255, 255, .5)', +} + +export const sectionHeaderLabelStyle: StyleProp = { + fontSize: 12, + color: 'rgba(255, 255, 255, .75)', +} diff --git a/src/slices/appState.ts b/src/slices/appState.ts new file mode 100644 index 0000000..3601286 --- /dev/null +++ b/src/slices/appState.ts @@ -0,0 +1,107 @@ +import { createAction, createSlice, PayloadAction } from '@reduxjs/toolkit' +import { AppDetails } from '../models/app-details' +import { RenderedIn } from '../models/rendered-in' +import { RootState } from '../store' + +interface AppsSearchState { + query?: string + result: AppDetails[] +} + +export interface AppState { + displayAllApps: boolean + displaySettings: boolean + menuAppDetails?: AppDetails + displayAppMenu: boolean + displaySortableFavoriteApps: boolean + displaySortablePinnedApps: boolean + displaySortableTemporaryPinnedApps: boolean + search: AppsSearchState +} + +const initialState: AppState = { + displayAllApps: false, + displaySettings: false, + menuAppDetails: undefined, + displayAppMenu: false, + displaySortableFavoriteApps: false, + displaySortablePinnedApps: false, + displaySortableTemporaryPinnedApps: false, + search: { + query: undefined, + result: [], + }, +} + +export const appStateSlice = createSlice({ + name: 'appState', + initialState, + reducers: { + // GlobalAppState + setDisplayAllApps: (state: AppState, { payload }: PayloadAction) => { + state.displayAllApps = payload + }, + setDisplaySettings: (state: AppState, { payload }: PayloadAction) => { + state.displaySettings = payload + }, + setMenuAppDetails: (state: AppState, { payload }: PayloadAction) => { + state.menuAppDetails = payload + }, + setDisplayAppMenu: (state: AppState, { payload }: PayloadAction) => { + state.displayAppMenu = payload + }, + setDisplaySortableFavoriteApps: (state: AppState, { payload }: PayloadAction) => { + state.displaySortableFavoriteApps = payload + }, + setDisplaySortablePinnedApps: (state: AppState, { payload }: PayloadAction) => { + state.displaySortablePinnedApps = payload + }, + setDisplaySortableTemporaryPinnedApps: (state: AppState, { payload }: PayloadAction) => { + state.displaySortableTemporaryPinnedApps = payload + }, + // AppsSearchState + setAppsSearchQuery: (state: AppState, { payload }: PayloadAction) => { + state.search.query = payload + }, + setAppsSearchResult: (state: AppState, { payload }: PayloadAction) => { + state.search.result = payload + }, + resetAppsSearchState: (state: AppState) => { + state.search.query = undefined + state.search.result = [] + }, + }, +}) + +export const { + setDisplayAllApps, + setDisplaySettings, + setMenuAppDetails, + setDisplayAppMenu, + setDisplaySortableFavoriteApps, + setDisplaySortablePinnedApps, + setDisplaySortableTemporaryPinnedApps, + setAppsSearchQuery, + setAppsSearchResult, + resetAppsSearchState, +} = appStateSlice.actions + +export const toogleAllApps = createAction('appState/toogleAllApps') +export const appLaunch = createAction<{ renderedIn: RenderedIn; appDetails: AppDetails }>('appState/appLaunch') +export const appLaunchFromSearch = createAction('appState/appLaunchFromSearch') +export const sortFavoriteApps = createAction('appState/sortFavoriteApps') +export const sortPinnedApps = createAction('appState/sortPinnedApps') +export const sortTemporaryPinnedApps = createAction('appState/sortTemporaryPinnedApps') + +export const selectAppState = (state: RootState) => state.appState +export const selectDisplayAllApps = (state: RootState) => state.appState.displayAllApps +export const selectDisplaySettings = (state: RootState) => state.appState.displaySettings +export const selectMenuAppDetails = (state: RootState) => state.appState.menuAppDetails +export const selectDisplayAppMenu = (state: RootState) => state.appState.displayAppMenu +export const selectDisplaySortableFavoriteApps = (state: RootState) => state.appState.displaySortableFavoriteApps +export const selectDisplaySortablePinnedApps = (state: RootState) => state.appState.displaySortablePinnedApps +export const selectDisplaySortableTemporaryPinnedApps = (state: RootState) => state.appState.displaySortableTemporaryPinnedApps +export const selectAppsSearchQuery = (state: RootState) => state.appState.search.query +export const selectAppsSearchResult = (state: RootState) => state.appState.search.result + +export default appStateSlice.reducer diff --git a/src/slices/appStateHandler.ts b/src/slices/appStateHandler.ts new file mode 100644 index 0000000..a362c07 --- /dev/null +++ b/src/slices/appStateHandler.ts @@ -0,0 +1,80 @@ +import { PayloadAction } from '@reduxjs/toolkit' +import { call, put, select, takeLatest } from 'redux-saga/effects' +import { AppDetails } from '../models/app-details' +import { RenderedIn } from '../models/rendered-in' +import { launchApp } from '../utils/apps-module' +import { dismissKeyboard } from '../utils/keyboard' +import { + appLaunch, + appLaunchFromSearch, + resetAppsSearchState, + selectAppsSearchResult, + selectDisplayAllApps, + setDisplayAllApps, + setDisplaySettings, + setDisplaySortableFavoriteApps, + setDisplaySortablePinnedApps, + setDisplaySortableTemporaryPinnedApps, + setMenuAppDetails, + sortFavoriteApps, + sortPinnedApps, + sortTemporaryPinnedApps, + toogleAllApps, +} from './appState' +import { addRecentApp } from './recentApps' + +const ADD_TO_RECENT_APPS_RENDERED_IN_VALUES = [RenderedIn.ALL_APPS, RenderedIn.FILTERED_APPS] + +function* toggleAllAppsHandler() { + const allAppsDisplayed: boolean = yield select(selectDisplayAllApps) + yield put(setDisplayAllApps(!allAppsDisplayed)) +} + +function* appLaunchHandler(action: PayloadAction<{ renderedIn: RenderedIn; appDetails: AppDetails }>) { + yield call(dismissKeyboard) + yield put(resetAppsSearchState()) + yield put(setDisplayAllApps(false)) + yield put(setMenuAppDetails(undefined)) + yield put(setDisplaySettings(false)) + yield put(setDisplaySortableFavoriteApps(false)) + + if (ADD_TO_RECENT_APPS_RENDERED_IN_VALUES.includes(action.payload.renderedIn)) { + yield put(addRecentApp(action.payload.appDetails)) + } +} + +function* sortFavoriteAppsHandler() { + yield put(setDisplaySettings(false)) + yield put(setDisplaySortableFavoriteApps(true)) +} + +function* sortPinnedAppsHandler() { + yield put(setDisplaySettings(false)) + yield put(setDisplaySortablePinnedApps(true)) +} + +function* sortTemporaryPinnedAppsHandler() { + yield put(setDisplaySettings(false)) + yield put(setDisplaySortableTemporaryPinnedApps(true)) +} + +function* appLaunchFromSearchHandler() { + const searchResult: AppDetails[] = yield select(selectAppsSearchResult) + if (searchResult.length === 0) { + return + } + + const appDetails = searchResult[0] + + yield call(launchApp, appDetails.packageName) + yield put(appLaunch({ renderedIn: RenderedIn.FILTERED_APPS, appDetails })) +} + +export function* appStateSaga() { + yield takeLatest(toogleAllApps.type, toggleAllAppsHandler) + yield takeLatest(appLaunch.type, appLaunchHandler) + yield takeLatest(sortFavoriteApps.type, sortFavoriteAppsHandler) + yield takeLatest(sortPinnedApps.type, sortPinnedAppsHandler) + yield takeLatest(sortTemporaryPinnedApps.type, sortTemporaryPinnedAppsHandler) + yield takeLatest(appLaunchFromSearch.type, appLaunchFromSearchHandler) +} diff --git a/src/slices/appsList.ts b/src/slices/appsList.ts index 774eb2c..7e391ba 100644 --- a/src/slices/appsList.ts +++ b/src/slices/appsList.ts @@ -1,12 +1,8 @@ -// Redux -import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit' -// Constants -import { CONTEXT_LAUNCHER_APP_ID } from '../constants' -// Utils -import { getAppsLetterIndex } from '../utils/alphabet-list' -// Models -import { RootState } from '../store' +import { createAction, createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit' +import { APP_ID } from '../constants' import { AppDetails } from '../models/app-details' +import { RootState } from '../store' +import { getAppsLetterIndex } from '../utils/alphabet-list' export interface AppsListState { list: AppDetails[] @@ -23,18 +19,20 @@ export const appsListSlice = createSlice({ setAppsList: (state: AppsListState, { payload }: PayloadAction) => { // Filter out Context Launcher from the app's list and sort it state.list = payload - .filter(({ name }: AppDetails) => name !== CONTEXT_LAUNCHER_APP_ID) - .sort((appOne: AppDetails, appTwo: AppDetails) => appOne.label.localeCompare(appTwo.label)) + .filter(({ packageName }: AppDetails) => packageName !== APP_ID) + .sort((appOne: AppDetails, appTwo: AppDetails) => appOne.name.localeCompare(appTwo.name)) }, }, }) export const { setAppsList } = appsListSlice.actions +export const getAppsListAction = createAction('appsList/getAppsListAction') +export const appRemovedAction = createAction('appsList/appRemovedAction') + const selectAppsList = (state: RootState) => state.appsList.list export const selectAppsListMemoized = createSelector(selectAppsList, (list: AppDetails[]) => list) - export const selectAppsLetterListMemoized = createSelector(selectAppsList, (list: AppDetails[]) => getAppsLetterIndex(list) ) diff --git a/src/slices/appsListHandler.ts b/src/slices/appsListHandler.ts new file mode 100644 index 0000000..a72e92a --- /dev/null +++ b/src/slices/appsListHandler.ts @@ -0,0 +1,35 @@ +import { PayloadAction } from '@reduxjs/toolkit' +import { call, put, takeLatest } from 'redux-saga/effects' +import { AppDetails } from '../models/app-details' +import AppsModule from '../native-modules/AppsModule' +import { appRemovedAction, getAppsListAction, setAppsList } from './appsList' +import { removeFavoriteApp } from './favoriteApps' +import { removePinnedApp } from './pinnedApps' +import { removeRecentApp } from './recentApps' + +function* getAppsListActionHandler() { + try { + const applications: string = yield call(AppsModule.getApplications) + const apps = JSON.parse(applications) as AppDetails[] + yield put(setAppsList(apps)) + } catch (error: unknown) { + yield put(setAppsList([])) + console.error('Error getting apps list:', error) + } +} + +function* appRemovedActionHandler(action: PayloadAction) { + yield put(removeRecentApp(action.payload)) + yield put( + removePinnedApp({ app: { packageName: action.payload, icon: 'NO_ICON', name: 'NO_NAME' }, isPermanent: true }) + ) + yield put( + removePinnedApp({ app: { packageName: action.payload, icon: 'NO_ICON', name: 'NO_NAME' }, isPermanent: false }) + ) + yield put(removeFavoriteApp(action.payload)) +} + +export function* appsListSagas() { + yield takeLatest(getAppsListAction.type, getAppsListActionHandler) + yield takeLatest(appRemovedAction.type, appRemovedActionHandler) +} diff --git a/src/slices/appsSearch.ts b/src/slices/appsSearch.ts deleted file mode 100644 index ca2a9db..0000000 --- a/src/slices/appsSearch.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Redux -import { createSlice, PayloadAction } from '@reduxjs/toolkit' -// Models -import { RootState } from '../store' -import { AppDetails } from '../models/app-details' - -export interface AppsSearchState { - query: string | undefined - result: AppDetails[] -} - -const initialState: AppsSearchState = { - query: undefined, - result: [], -} - -export const appsSearchSlice = createSlice({ - name: 'appsSearch', - initialState, - reducers: { - setAppsSearchQuery: (state: AppsSearchState, { payload }: PayloadAction) => { - state.query = payload - }, - setAppsSearchResult: (state: AppsSearchState, { payload }: PayloadAction) => { - state.result = payload - }, - resetAppsSearchState: (state: AppsSearchState) => { - if (!state.query && state.result.length == 0) return - - state.query = undefined - state.result = [] - }, - }, -}) - -export const { setAppsSearchQuery, setAppsSearchResult, resetAppsSearchState } = appsSearchSlice.actions - -export const selectAppsSearchQuery = (state: RootState) => state.appsSearch.query -export const selectAppsSearchResult = (state: RootState) => state.appsSearch.result - -export default appsSearchSlice.reducer diff --git a/src/slices/favoriteApps.ts b/src/slices/favoriteApps.ts index 47d0b9d..4257aad 100644 --- a/src/slices/favoriteApps.ts +++ b/src/slices/favoriteApps.ts @@ -1,8 +1,7 @@ -// Redux import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit' -// Models -import { RootState } from '../store' import { FavoriteApp } from '../models/favorite-app' +import { RootState } from '../store' +import { getApp, getAppIndex } from '../utils/apps' export interface FavoriteAppsState { list: FavoriteApp[] @@ -17,15 +16,13 @@ export const favoriteAppsSlice = createSlice({ initialState, reducers: { addFavoriteApp: (state: FavoriteAppsState, { payload }: PayloadAction) => { - if (state.list.length == 5) return - - const existingApp = state.list.find(({ name }: FavoriteApp) => name === payload.name) + if (state.list.length === 5) return // Add to list only if it doesn't exists - if (!existingApp) state.list.push({ ...payload }) + if (!getApp(state.list, payload.packageName)) state.list.push({ ...payload }) }, removeFavoriteApp: (state: FavoriteAppsState, { payload }: PayloadAction) => { - const foundAppIndex = state.list.findIndex(({ name }: FavoriteApp) => name === payload) + const foundAppIndex = getAppIndex(state.list, payload) if (foundAppIndex === -1) return diff --git a/src/slices/pinnedApps.ts b/src/slices/pinnedApps.ts index 59e2109..fe3a65d 100644 --- a/src/slices/pinnedApps.ts +++ b/src/slices/pinnedApps.ts @@ -1,11 +1,11 @@ -// Redux import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit' -// Models -import { RootState } from '../store' import { PinnedApp, PinnedAppsState, TemporaryPinnedAppsConfig } from '../models/pinned-app' +import { RootState } from '../store' +import { getAppIndex } from '../utils/apps' const initialState: PinnedAppsState = { list: [], + temporarily: [], // Temporarily pinned apps temporaryAppsConfig: { startDate: undefined, endDate: undefined, @@ -16,47 +16,66 @@ export const pinnedAppsSlice = createSlice({ name: 'pinnedApps', initialState, reducers: { - updateOrRemovePinnedApp: (state: PinnedAppsState, { payload }: PayloadAction) => { - const foundAppIndex = state.list.findIndex(({ name }: PinnedApp) => name === payload.name) + addPinnedApp: (state: PinnedAppsState, { payload }: PayloadAction<{ app: PinnedApp; isPermanent: boolean }>) => { + if (payload.isPermanent) { + const foundAppIndex = getAppIndex(state.list, payload.app.packageName) + + if (foundAppIndex !== -1) return - // Add if no entry is found - if (foundAppIndex === -1) { - state.list.push({ ...payload }) - return + state.list.push({ ...payload.app }) + } else { + const foundAppIndex = getAppIndex(state.temporarily, payload.app.packageName) + + if (foundAppIndex !== -1) return + + state.temporarily.push({ ...payload.app }) } + }, + removePinnedApp: (state: PinnedAppsState, { payload }: PayloadAction<{ app: PinnedApp; isPermanent: boolean }>) => { + if (payload.isPermanent) { + const foundAppIndex = getAppIndex(state.list, payload.app.packageName) - // Get pinned app object - const foundApp = state.list.find(({ name }: PinnedApp) => name === payload.name) + if (foundAppIndex === -1) return - // Remove if entry is not pinned permanently and temporarily - if (!foundApp?.isPermanent && !foundApp?.isTemporary) { state.list.splice(foundAppIndex, 1) - return - } + } else { + const foundAppIndex = getAppIndex(state.temporarily, payload.app.packageName) + + if (foundAppIndex === -1) return - // Update changed values - state.list.splice(foundAppIndex, 1, { ...foundApp, ...payload }) + state.temporarily.splice(foundAppIndex, 1) + } }, - clearPinnedApps: (state: PinnedAppsState) => { - state.list = [] + clearPinnedApps: (state: PinnedAppsState, { payload }: PayloadAction<{ temporarily: boolean }>) => { + if (payload.temporarily) state.temporarily = [] + else state.list = [] }, setTemporaryAppsConfig: (state: PinnedAppsState, { payload }: PayloadAction) => { state.temporaryAppsConfig = { ...payload } }, + setPinnedApps: ( + state: PinnedAppsState, + { payload }: PayloadAction<{ apps: PinnedApp[]; isPermanent: boolean }> + ) => { + if (payload.isPermanent) state.list = payload.apps + else state.temporarily = payload.apps + }, }, }) -export const { updateOrRemovePinnedApp, setTemporaryAppsConfig, clearPinnedApps } = pinnedAppsSlice.actions +export const { addPinnedApp, removePinnedApp, setTemporaryAppsConfig, clearPinnedApps, setPinnedApps } = + pinnedAppsSlice.actions const selectPinnedApps = (state: RootState) => state.pinnedApps.list +const selecttTemporaryPinnedApps = (state: RootState) => state.pinnedApps.temporarily const selectTemporaryPinnedAppsConfig = (state: RootState) => state.pinnedApps.temporaryAppsConfig -export const selectAllPinnedAppsMemoized = createSelector(selectPinnedApps, (list: PinnedApp[]) => list) -export const selectPinnedAppsMemoized = createSelector(selectPinnedApps, (list: PinnedApp[]) => - list.filter((pinnedApp: PinnedApp) => pinnedApp.isPermanent) -) -export const selectTemporaryPinnedAppsMemoized = createSelector(selectPinnedApps, (list: PinnedApp[]) => - list.filter((pinnedApp: PinnedApp) => pinnedApp.isTemporary) +export const selectPinnedAppsMemoized = createSelector(selectPinnedApps, (list: PinnedApp[]) => list) +export const selectPinnedAppsCountMemoized = createSelector(selectPinnedApps, (list: PinnedApp[]) => list.length) +export const selectTemporaryPinnedAppsMemoized = createSelector(selecttTemporaryPinnedApps, (list: PinnedApp[]) => list) +export const selectTemporaryPinnedAppsCountMemoized = createSelector( + selecttTemporaryPinnedApps, + (list: PinnedApp[]) => list.length ) export const selectTemporaryPinnedAppsConfigMemoized = createSelector( selectTemporaryPinnedAppsConfig, diff --git a/src/slices/preferences.ts b/src/slices/preferences.ts index 062ebdb..66f5b58 100644 --- a/src/slices/preferences.ts +++ b/src/slices/preferences.ts @@ -1,6 +1,4 @@ -// Redux import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit' -// Models import { RootState } from '../store' export interface PreferencesState { @@ -34,7 +32,10 @@ export const preferencesSlice = createSlice({ state.displayTemporaryPinnedApps = payload }, resetPreferences: (state: PreferencesState) => { - state = { ...initialState } + state.displayPinnedApps = initialState.displayPinnedApps + state.displayRecentApps = initialState.displayRecentApps + state.displayFavoriteApps = initialState.displayFavoriteApps + state.displayTemporaryPinnedApps = initialState.displayTemporaryPinnedApps }, }, }) diff --git a/src/slices/recentApps.ts b/src/slices/recentApps.ts index 4e8895d..f61b746 100644 --- a/src/slices/recentApps.ts +++ b/src/slices/recentApps.ts @@ -1,11 +1,10 @@ -// Redux import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit' -// Models +import { RecentApp } from '../models/recent-app' import { RootState } from '../store' -import { RecentAppDetails, RecentAppDetailsWithOptionalIcon } from '../models/recent-app' +import { getAppIndex } from '../utils/apps' export interface RecentAppsState { - list: RecentAppDetails[] + list: RecentApp[] } const initialState: RecentAppsState = { @@ -16,11 +15,8 @@ export const recentAppsSlice = createSlice({ name: 'recentApps', initialState, reducers: { - addRecentApp: (state: RecentAppsState, { payload }: PayloadAction) => { - // Don't add app without icon - if (!payload.icon) return - - const list = state.list.filter(({ name }: RecentAppDetails) => name !== payload.name) + addRecentApp: (state: RecentAppsState, { payload }: PayloadAction) => { + const list = state.list.filter(({ packageName }: RecentApp) => packageName !== payload.packageName) // Put most recent app first list.unshift({ ...payload, icon: payload.icon }) @@ -31,7 +27,7 @@ export const recentAppsSlice = createSlice({ state.list = list }, removeRecentApp: (state: RecentAppsState, { payload }: PayloadAction) => { - const foundAppIndex = state.list.findIndex(({ name }: RecentAppDetails) => name === payload) + const foundAppIndex = getAppIndex(state.list, payload) if (foundAppIndex === -1) return @@ -47,6 +43,6 @@ export const { addRecentApp, removeRecentApp, clearRecentApps } = recentAppsSlic const selectRecentApps = (state: RootState) => state.recentApps.list -export const selectRecentAppsMemoized = createSelector(selectRecentApps, (list: RecentAppDetails[]) => list) +export const selectRecentAppsMemoized = createSelector(selectRecentApps, (list: RecentApp[]) => list) export default recentAppsSlice.reducer diff --git a/src/store.ts b/src/store.ts index 7f7e7a7..4aebbc0 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,36 +1,34 @@ -// Redux -import { configureStore, combineReducers } from '@reduxjs/toolkit' -// Reducers -import appsListReducer from './slices/appsList' -import appsSearchReducer from './slices/appsSearch' -import recentAppsReducer from './slices/recentApps' -import FavoriteAppsReducer from './slices/favoriteApps' -import preferencesReducer from './slices/preferences' -import pinnedAppsReducer from './slices/pinnedApps' -// Storage import AsyncStorage from '@react-native-async-storage/async-storage' -// Redux Persist +import { combineReducers, configureStore } from '@reduxjs/toolkit' import { - persistStore, - persistReducer, + createMigrate, FLUSH, - REHYDRATE, + MigrationManifest, PAUSE, PERSIST, + PersistConfig, + persistReducer, + persistStore, PURGE, REGISTER, - createMigrate, - MigrationManifest, - PersistConfig, + REHYDRATE, } from 'redux-persist' +import createSagaMiddleware from 'redux-saga' +import rootSaga from './rootSaga' +import appsListReducer from './slices/appsList' +import appStateReducer from './slices/appState' +import FavoriteAppsReducer from './slices/favoriteApps' +import pinnedAppsReducer from './slices/pinnedApps' +import preferencesReducer from './slices/preferences' +import recentAppsReducer from './slices/recentApps' export const rootReducer = combineReducers({ appsList: appsListReducer, - appsSearch: appsSearchReducer, recentApps: recentAppsReducer, favoriteApps: FavoriteAppsReducer, preferences: preferencesReducer, pinnedApps: pinnedAppsReducer, + appState: appStateReducer, }) const migrations: MigrationManifest = { @@ -60,18 +58,52 @@ const migrations: MigrationManifest = { }, } }, + 4: (state: any) => { + const localState = { ...state } + delete localState.appsSearch + + return { + ...localState, + appsList: { + list: [], + }, + pinnedApps: { + list: [], + temporarily: [], + temporaryAppsConfig: { + startDate: undefined, + endDate: undefined, + }, + }, + appState: { + displayAllApps: false, + displaySettings: false, + menuAppDetails: undefined, + displayAppMenu: false, + displaySortableFavoriteApps: false, + displaySortablePinnedApps: false, + search: { + query: undefined, + result: [], + }, + }, + } + }, } const persistConfig: PersistConfig = { key: 'root', debug: false, - version: 3, + version: 4, storage: AsyncStorage, + blacklist: ['appState'], migrate: createMigrate(migrations, { debug: false }), } const persistedReducer = persistReducer(persistConfig, rootReducer) +const sagaMiddleware = createSagaMiddleware() + export const store = configureStore({ reducer: persistedReducer, middleware: getDefaultMiddleware => @@ -79,10 +111,12 @@ export const store = configureStore({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, - }), + }).concat(sagaMiddleware), }) +sagaMiddleware.run(rootSaga) + export const persistor = persistStore(store) -export type RootState = ReturnType +export type RootState = ReturnType export type AppDispatch = typeof store.dispatch diff --git a/src/utils/alphabet-list.test.ts b/src/utils/alphabet-list.test.ts index 00eb09f..2b59f02 100644 --- a/src/utils/alphabet-list.test.ts +++ b/src/utils/alphabet-list.test.ts @@ -1,14 +1,14 @@ -import { getAppsLetterIndex, getFirstLetter, getLettersMap } from './alphabet-list' -import { AppLetterIndex } from '../models/list-letter-index' import { AppDetails } from '../models/app-details' +import { AppLetterIndex } from '../models/list-letter-index' +import { getAppsLetterIndex, getFirstLetter, getLettersMap } from './alphabet-list' describe('AlphabetList Tests', () => { describe('getLettersMap Tests', () => { it('should map letters to indexes correctly', () => { const result = getLettersMap() - expect(result['A']).toEqual(1) - expect(result['Z']).toEqual(26) + expect(result.A).toEqual(1) + expect(result.Z).toEqual(26) }) }) @@ -25,36 +25,44 @@ describe('AlphabetList Tests', () => { describe('getAppsLetterIndex Tests', () => { const apps: AppDetails[] = [ { - label: 'App 1', - name: 'com.app_1', + name: 'App 1', + packageName: 'com.app_1', + icon: 'ICON', }, { - label: 'App 2', - name: 'com.app_2', + name: 'App 2', + packageName: 'com.app_2', + icon: 'ICON', }, { - label: 'Brave', - name: 'com.brave', + name: 'Brave', + packageName: 'com.brave', + icon: 'ICON', }, { - label: 'Chrome', - name: 'com.chrome', + name: 'Chrome', + packageName: 'com.chrome', + icon: 'ICON', }, { - label: 'Clock', - name: 'com.clock', + name: 'Clock', + packageName: 'com.clock', + icon: 'ICON', }, { - label: 'Google', - name: 'com.google', + name: 'Google', + packageName: 'com.google', + icon: 'ICON', }, { - label: 'Youtube', - name: 'com.youtube', + name: 'Youtube', + packageName: 'com.youtube', + icon: 'ICON', }, { - label: '_App', - name: 'com._app', + name: '_App', + packageName: 'com._app', + icon: 'ICON', }, ] const appLetterIndex: AppLetterIndex[] = [ diff --git a/src/utils/alphabet-list.ts b/src/utils/alphabet-list.ts index 3447a31..bcc717d 100644 --- a/src/utils/alphabet-list.ts +++ b/src/utils/alphabet-list.ts @@ -1,4 +1,3 @@ -// Models import { AppDetails } from '../models/app-details' import { AppLetterIndex } from '../models/list-letter-index' @@ -63,8 +62,8 @@ export const getAppsLetterIndex = (apps: AppDetails[]): AppLetterIndex[] => { const appsLetterIndex: AppLetterIndex[] = [] const treatedLetters: string[] = [] - apps.forEach(({ label }: AppDetails, index: number) => { - const letter = getFirstLetter(label) + apps.forEach(({ name }: AppDetails, index: number) => { + const letter = getFirstLetter(name) if (treatedLetters.includes(letter)) return diff --git a/src/utils/apps-module.test.ts b/src/utils/apps-module.test.ts index 2e2981e..790b6fe 100644 --- a/src/utils/apps-module.test.ts +++ b/src/utils/apps-module.test.ts @@ -1,5 +1,5 @@ -import { launchApp, requestAppUninstall, showAppDetails } from './apps-module' import AppsModule from '../native-modules/AppsModule' +import { launchApp, requestAppUninstall, showAppDetails } from './apps-module' describe('AppsModule Tests', () => { const aPackageName = 'com.a_package_name' diff --git a/src/utils/apps-module.ts b/src/utils/apps-module.ts index 1b019ea..3e26cba 100644 --- a/src/utils/apps-module.ts +++ b/src/utils/apps-module.ts @@ -1,4 +1,3 @@ -// Native modules import AppsModule from '../native-modules/AppsModule' export const launchApp = (packageName: string) => AppsModule.launchApplication(packageName) diff --git a/src/utils/apps.ts b/src/utils/apps.ts index 2d23c3f..74dd8d4 100644 --- a/src/utils/apps.ts +++ b/src/utils/apps.ts @@ -1,15 +1,23 @@ -// Models -import { AppDetails, AppDetailsWithIcon } from '../models/app-details' +import { APP_ITEM_HEIGHT } from '../constants' +import { AppDetails } from '../models/app-details' import { PinnedApp } from '../models/pinned-app' export const getAppsByLabel = (apps: AppDetails[], query: string): AppDetails[] => { - return apps.filter((app: AppDetails) => app.label.match(new RegExp(query, 'gi'))) + return apps.filter((app: AppDetails) => app.name.match(new RegExp(query, 'gi'))) } -export const getPinnedAppByName = (pinnedApps: PinnedApp[], appDetails: AppDetailsWithIcon): PinnedApp => { - const pinnedApp = pinnedApps.find(({ name }: PinnedApp) => name === appDetails.name) - - if (pinnedApp) return pinnedApp +export const getAppIndex = (apps: AppDetails[], appPackageName: string): number => { + return apps.findIndex(({ packageName }: AppDetails) => packageName === appPackageName) +} - return { ...appDetails, isTemporary: false, isPermanent: false } +export const getApp = (apps: AppDetails[], appPackageName: string): AppDetails | PinnedApp | undefined => { + return apps.find(({ packageName }: AppDetails) => packageName === appPackageName) } + +export const getListKey = ({ packageName }: AppDetails) => packageName + +export const getListItemLayout = (_data: unknown, index: number) => ({ + length: APP_ITEM_HEIGHT, + offset: APP_ITEM_HEIGHT * index, + index, +}) diff --git a/src/utils/date.ts b/src/utils/date.ts index 717ce39..9cea930 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -3,8 +3,8 @@ export const getTimeFromDate = (stringDate?: string): string => { const date = new Date(stringDate) - const singleDigitHours = `${date.getHours()}`.length == 1 - const singleDigitMinutes = `${date.getMinutes()}`.length == 1 + const singleDigitHours = `${date.getHours()}`.length === 1 + const singleDigitMinutes = `${date.getMinutes()}`.length === 1 const hours = singleDigitHours ? `0${date.getHours()}` : date.getHours() const minutes = singleDigitMinutes ? `0${date.getMinutes()}` : date.getMinutes() diff --git a/src/utils/keyboard.ts b/src/utils/keyboard.ts index c553dc9..60fd9b8 100644 --- a/src/utils/keyboard.ts +++ b/src/utils/keyboard.ts @@ -1,4 +1,3 @@ -// React Native import { Keyboard } from 'react-native' export const dismissKeyboard = () => Keyboard.dismiss() diff --git a/src/utils/string.test.ts b/src/utils/string.test.ts index 45bdfeb..578d810 100644 --- a/src/utils/string.test.ts +++ b/src/utils/string.test.ts @@ -1,4 +1,4 @@ -import { truncateString, createKeyForHighlightTextElement } from './string' +import { createKeyForHighlightTextElement, truncateString } from './string' describe('String utils tests', () => { describe('truncateString Tests', () => { diff --git a/src/utils/toast.ts b/src/utils/toast.ts index f5183db..8087302 100644 --- a/src/utils/toast.ts +++ b/src/utils/toast.ts @@ -1,4 +1,3 @@ -// React Native import { ToastAndroid } from 'react-native' export const displayToast = (message: string, duration: number = ToastAndroid.SHORT) => { diff --git a/utils/test/data.ts b/utils/test/data.ts index 84ede91..a649eb0 100644 --- a/utils/test/data.ts +++ b/utils/test/data.ts @@ -1,16 +1,12 @@ -// Models -import { GlobalContextType, SearchContextType } from '../../src/models/context' +import { SearchContextType } from '../../src/contexts/SearchContext' +import { RootState } from '../../src/store' -export const CONTEXT_LAUNCHER_APP_ID = 'com.razinj.context_launcher' +export const APP_ID = 'com.razinj.context_launcher' -export const initialStoreState = { +export const initialStoreState: RootState = { appsList: { list: [], }, - appsSearch: { - query: undefined, - result: [], - }, favoriteApps: { list: [], }, @@ -25,32 +21,29 @@ export const initialStoreState = { }, pinnedApps: { list: [], + temporarily: [], temporaryAppsConfig: { startDate: undefined, endDate: undefined, }, }, -} - -export const defaultGlobalContextValue: GlobalContextType = { - dismissKeyboard: jest.fn(), - globalAppLaunchProcedure: jest.fn(), - displayAllApps: false, - hideAllApps: jest.fn(), - toggleDisplayAllApps: jest.fn(), - sortableFavoriteApps: false, - toggleSortableFavoriteApps: jest.fn(), - appItemMenuBottomSheetRef: null, - displayAppItemMenuBottomSheet: jest.fn(), - appItemMenuDetails: null, - setAppItemMenuDetails: jest.fn(), - settingsBottomSheetRef: null, - displaySettingsBottomSheet: jest.fn(), + appState: { + displayAllApps: false, + displaySettings: false, + menuAppDetails: undefined, + displayAppMenu: false, + displaySortableFavoriteApps: false, + displaySortablePinnedApps: false, + displaySortableTemporaryPinnedApps: false, + search: { + query: undefined, + result: [], + }, + }, } export const defaultSearchContextValue: SearchContextType = { searchInputRef: null, - invalidCharacters: false, - setInvalidCharacters: jest.fn(), + clearSearchInput: jest.fn(), searchAppLaunchProcedure: jest.fn(), } diff --git a/utils/test/utils.tsx b/utils/test/utils.tsx index 20747fc..78008af 100644 --- a/utils/test/utils.tsx +++ b/utils/test/utils.tsx @@ -1,25 +1,16 @@ -// React -import React from 'react' -import { ReactElement, ReactNode } from 'react' -// Redux -import { Provider } from 'react-redux' import { configureStore, PreloadedState, Store } from '@reduxjs/toolkit' -// Testing Library import { render, RenderOptions } from '@testing-library/react-native' -// Store +import React, { ReactElement, ReactNode } from 'react' +import { Provider as PaperProvider } from 'react-native-paper' +import { Provider as StoreProvider } from 'react-redux' +import SearchContext, { SearchContextType } from '../../src/contexts/SearchContext' + import { rootReducer, RootState } from '../../src/store' -// Models -import { GlobalContextType, SearchContextType } from '../../src/models/context' -// Contexts -import GlobalContext from '../../src/contexts/GlobalContext' -import SearchContext from '../../src/contexts/SearchContext' -// Constants -import { defaultGlobalContextValue, defaultSearchContextValue, initialStoreState } from './data' +import { defaultSearchContextValue, initialStoreState } from './data' interface ExtendedRenderOptions extends RenderOptions { preloadedState?: PreloadedState store?: Store - globalContextValue?: GlobalContextType searchContextValue?: SearchContextType } @@ -32,7 +23,11 @@ export const renderWithProvider = ( }: ExtendedRenderOptions = {} ) => { const wrapper = ({ children }: { children: ReactNode }): JSX.Element => { - return {children} + return ( + + {children} + + ) } // Return an object with the store and all of RTL's query functions @@ -43,7 +38,6 @@ export const renderWithProviderAndContexts = ( component: ReactElement, { preloadedState = initialStoreState, - globalContextValue = defaultGlobalContextValue, searchContextValue = defaultSearchContextValue, store = configureStore({ reducer: rootReducer, preloadedState }), ...renderOptions @@ -51,11 +45,11 @@ export const renderWithProviderAndContexts = ( ) => { const wrapper = ({ children }: { children: ReactNode }): JSX.Element => { return ( - - - {children} - - + + + {children} + + ) }