about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2023-03-19 18:53:57 -0500
committerGitHub <noreply@github.com>2023-03-19 18:53:57 -0500
commit1de724b24b9607d4ee83dc0dbb92c13b2b77dcaf (patch)
treede1b244a976e55818f1181e6bf2b727237aff7c2
parentc31ffdac1b970d8d51c538f931cc64a942670740 (diff)
downloadvoidsky-1de724b24b9607d4ee83dc0dbb92c13b2b77dcaf.tar.zst
Add custom feeds selector, rework search, simplify onboarding (#325)
* Get home screen's swipable pager working with the drawer

* Add tab bar to pager

* Implement popular & following views on home screen

* Visual tune-up

* Move the feed selector to the footer

* Fix to 'new posts' poll

* Add the view header as a feed item

* Use the native driver on the tabbar indicator to improve perf

* Reduce home polling to the currently active page; also reuse some code

* Add soft reset on tap selected in tab bar

* Remove explicit 'onboarding' flow

* Choose good stuff based on service

* Add foaf-based follow discovery

* Fall back to who to follow

* Fix backgrounds

* Switch to the off-spec goodstuff route

* 1.8

* Fix for dev & staging

* Swap the tab bar items and rename suggested to what's hot

* Go to whats-hot by default if you have no follows

* Implement pager and tabbar for desktop web

* Pin deps to make expo happy

* Add language filtering to goodstuff
-rw-r--r--app.json2
-rw-r--r--ios/Podfile.lock50
-rw-r--r--ios/bluesky/Info.plist2
-rw-r--r--package.json14
-rw-r--r--patches/react-native-pager-view+6.1.4.patch54
-rw-r--r--src/App.native.tsx10
-rw-r--r--src/lib/api/feed-manip.ts32
-rw-r--r--src/lib/hooks/usePalette.ts4
-rw-r--r--src/lib/styles.ts1
-rw-r--r--src/state/models/discovery/foafs.ts110
-rw-r--r--src/state/models/feed-view.ts85
-rw-r--r--src/state/models/me.ts1
-rw-r--r--src/state/models/my-follows.ts6
-rw-r--r--src/state/models/session.ts11
-rw-r--r--src/state/models/ui/shell.ts15
-rw-r--r--src/view/com/discover/SuggestedFollows.tsx160
-rw-r--r--src/view/com/posts/Feed.tsx74
-rw-r--r--src/view/com/posts/FollowingEmptyState.tsx81
-rw-r--r--src/view/com/profile/FollowButton.tsx6
-rw-r--r--src/view/com/profile/ProfileCard.tsx62
-rw-r--r--src/view/com/util/LoadingPlaceholder.tsx49
-rw-r--r--src/view/com/util/PostMeta.tsx11
-rw-r--r--src/view/com/util/TabBar.tsx162
-rw-r--r--src/view/com/util/WelcomeBanner.tsx101
-rw-r--r--src/view/com/util/pager/Pager.tsx87
-rw-r--r--src/view/com/util/pager/Pager.web.tsx69
-rw-r--r--src/view/screens/Home.tsx144
-rw-r--r--src/view/screens/Search.tsx93
-rw-r--r--src/view/screens/home/FeedsTabBar.tsx72
-rw-r--r--src/view/screens/home/FeedsTabBar.web.tsx22
-rw-r--r--src/view/shell/BottomBar.tsx22
-rw-r--r--src/view/shell/index.tsx6
-rw-r--r--yarn.lock712
33 files changed, 1636 insertions, 694 deletions
diff --git a/app.json b/app.json
index ebf4224bd..8d9bc0280 100644
--- a/app.json
+++ b/app.json
@@ -2,7 +2,7 @@
   "expo": {
     "name": "bluesky",
     "slug": "bluesky",
-    "version": "1.7.0",
+    "version": "1.8.0",
     "orientation": "portrait",
     "icon": "./assets/icon.png",
     "userInterfaceStyle": "light",
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index d6d28c5e2..12f262ddd 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -19,10 +19,10 @@ PODS:
   - EXJSONUtils (0.5.1)
   - EXManifests (0.5.2):
     - EXJSONUtils
-  - EXMediaLibrary (15.2.2):
+  - EXMediaLibrary (15.2.3):
     - ExpoModulesCore
     - React-Core
-  - Expo (48.0.6):
+  - Expo (48.0.7):
     - ExpoModulesCore
   - expo-dev-client (2.1.5):
     - EXManifests
@@ -100,7 +100,7 @@ PODS:
     - ExpoModulesCore
   - ExpoKeepAwake (12.0.1):
     - ExpoModulesCore
-  - ExpoModulesCore (1.2.4):
+  - ExpoModulesCore (1.2.5):
     - React-Core
     - React-RCTAppDelegate
     - ReactCommon/turbomodule/core
@@ -384,10 +384,12 @@ PODS:
     - glog
   - react-native-blur (4.3.0):
     - React-Core
-  - react-native-cameraroll (5.2.4):
+  - react-native-cameraroll (5.3.1):
     - React-Core
   - react-native-image-resizer (3.0.5):
     - React-Core
+  - react-native-pager-view (6.1.2):
+    - React-Core
   - react-native-paste-input (0.6.2):
     - React-Core
     - Swime (= 3.0.6)
@@ -401,7 +403,7 @@ PODS:
     - React-Core
   - react-native-version-number (0.3.6):
     - React
-  - react-native-webview (11.26.1):
+  - react-native-webview (11.26.0):
     - React-Core
   - React-perflogger (0.71.3)
   - React-RCTActionSheet (0.71.3):
@@ -489,9 +491,9 @@ PODS:
     - React-perflogger (= 0.71.3)
   - rn-fetch-blob (0.12.0):
     - React-Core
-  - RNBackgroundFetch (4.1.8):
+  - RNBackgroundFetch (4.1.9):
     - React-Core
-  - RNCAsyncStorage (1.17.11):
+  - RNCAsyncStorage (1.17.12):
     - React-Core
   - RNCClipboard (1.11.2):
     - React-Core
@@ -514,10 +516,10 @@ PODS:
     - TOCropViewController
   - RNInAppBrowser (3.7.0):
     - React-Core
-  - RNNotifee (7.5.0):
+  - RNNotifee (7.6.1):
     - React-Core
-    - RNNotifee/NotifeeCore (= 7.5.0)
-  - RNNotifee/NotifeeCore (7.5.0):
+    - RNNotifee/NotifeeCore (= 7.6.1)
+  - RNNotifee/NotifeeCore (7.6.1):
     - React-Core
   - RNReactNativeHapticFeedback (1.14.0):
     - React-Core
@@ -551,7 +553,7 @@ PODS:
   - RNScreens (3.20.0):
     - React-Core
     - React-RCTImage
-  - RNSVG (13.8.0):
+  - RNSVG (13.4.0):
     - React-Core
   - SDWebImage (5.11.1):
     - SDWebImage/Core (= 5.11.1)
@@ -559,7 +561,7 @@ PODS:
   - SDWebImageWebPCoder (0.8.5):
     - libwebp (~> 1.0)
     - SDWebImage/Core (~> 5.10)
-  - segment-analytics-react-native (2.13.1):
+  - segment-analytics-react-native (2.13.4):
     - React-Core
     - sovran-react-native
   - sovran-react-native (0.4.5):
@@ -614,6 +616,7 @@ DEPENDENCIES:
   - "react-native-blur (from `../node_modules/@react-native-community/blur`)"
   - "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)"
   - "react-native-image-resizer (from `../node_modules/@bam.tech/react-native-image-resizer`)"
+  - react-native-pager-view (from `../node_modules/react-native-pager-view`)
   - "react-native-paste-input (from `../node_modules/@mattermost/react-native-paste-input`)"
   - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
   - react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
@@ -747,6 +750,8 @@ EXTERNAL SOURCES:
     :path: "../node_modules/@react-native-camera-roll/camera-roll"
   react-native-image-resizer:
     :path: "../node_modules/@bam.tech/react-native-image-resizer"
+  react-native-pager-view:
+    :path: "../node_modules/react-native-pager-view"
   react-native-paste-input:
     :path: "../node_modules/@mattermost/react-native-paste-input"
   react-native-safe-area-context:
@@ -830,15 +835,15 @@ SPEC CHECKSUMS:
   EXImageLoader: fd053169a8ee932dd83bf1fe5487a50c26d27c2b
   EXJSONUtils: 48b1e764ac35160e6f54d21ab60d7d9501f3e473
   EXManifests: 500666d48e8dd7ca5a482c9e729e4a7a6c34081b
-  EXMediaLibrary: 792fe9b828b5bfa2c5a8b629730f175af2938285
-  Expo: 04ba1ddde0be07aff4306ae636a1804810679145
+  EXMediaLibrary: 587cd8aad27a6fc8d7c38b950bc75bc1845a7480
+  Expo: 707f9b0039eacc6a1dce90c08c9e37b9c417bba2
   expo-dev-client: 7c1ef51516853465f4d448c14ddf365167d20361
   expo-dev-launcher: 90de99d9e5d1a883d81355ca10e87c2f3c81d46e
   expo-dev-menu: d4369e74d8d21a0ccdee35f7c732e7118b0fee16
   expo-dev-menu-interface: 6c82ae323c4b8724dead4763ce3ff24a2108bdb1
   ExpoImagePicker: 270dea232b3a072d981dd564e2cafc63a864edb1
   ExpoKeepAwake: 69f5f627670d62318410392d03e0b5db0f85759a
-  ExpoModulesCore: 1667335d4f4c9b7801990930e6f0eea42c916a21
+  ExpoModulesCore: 397fc99e9d6c9dcc010f36d5802097c17b90424c
   EXSplashScreen: cd7fb052dff5ba8311d5c2455ecbebffe1b7a8ca
   EXUpdatesInterface: dd699d1930e28639dcbd70a402caea98e86364ca
   FBLazyVector: 60195509584153283780abdac5569feffb8f08cc
@@ -863,13 +868,14 @@ SPEC CHECKSUMS:
   React-jsinspector: 9f7c9137605e72ca0343db4cea88006cb94856dd
   React-logger: 957e5dc96d9dbffc6e0f15e0ee4d2b42829ff207
   react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3
-  react-native-cameraroll: cb752fda6d5268f1646b4390bd5be1f27706b9a0
+  react-native-cameraroll: f3050460fe1708378698c16686bfaa5f34099be2
   react-native-image-resizer: 00ceb0e05586c7aadf061eea676957a6c2ec60fa
+  react-native-pager-view: 54bed894cecebe28cede54c01038d9d1e122de43
   react-native-paste-input: 3392800944a47c00dddbff23c31c281482209679
   react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
   react-native-splash-screen: 4312f786b13a81b5169ef346d76d33bc0c6dc457
   react-native-version-number: b415bbec6a13f2df62bf978e85bc0d699462f37f
-  react-native-webview: 9f111dfbcfc826084d6c507f569e5e03342ee1c1
+  react-native-webview: 994b9f8fbb504d6314dc40d83f94f27c6831b3bf
   React-perflogger: af8a3d31546077f42d729b949925cc4549f14def
   React-RCTActionSheet: 57cc5adfefbaaf0aae2cf7e10bccd746f2903673
   React-RCTAnimation: 11c61e94da700c4dc915cf134513764d87fc5e2b
@@ -884,22 +890,22 @@ SPEC CHECKSUMS:
   React-runtimeexecutor: 7bf0dafc7b727d93c8cb94eb00a9d3753c446c3e
   ReactCommon: 6f65ea5b7d84deb9e386f670dd11ce499ded7b40
   rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
-  RNBackgroundFetch: 8e16176ff415daac743a6eb57afc8e9e14dbe623
-  RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60
+  RNBackgroundFetch: 642777e4e76435773c149d565a043d66f1781237
+  RNCAsyncStorage: 09fc8595e6d6f6d5abf16b23a56b257d9c6b7c5b
   RNCClipboard: 3f0451a8100393908bea5c5c5b16f96d45f30bfc
   RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
   RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
   RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
   RNImageCropPicker: 648356d68fbf9911a1016b3e3723885d28373eda
   RNInAppBrowser: e36d6935517101ccba0e875bac8ad7b0cb655364
-  RNNotifee: 053c0ace9c73634709a0214fd9c436a5777a562f
+  RNNotifee: bdc064c29f4d558046f51f0c3ae02bab4fd3cd85
   RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c
   RNReanimated: cc5e3aa479cb9170bcccf8204291a6950a3be128
   RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
-  RNSVG: c1e76b81c76cdcd34b4e1188852892dc280eb902
+  RNSVG: 07dbd870b0dcdecc99b3a202fa37c8ca163caec2
   SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
   SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
-  segment-analytics-react-native: f962dff3a084655a29f9403b8c139c75a3362524
+  segment-analytics-react-native: cc12d9422f7ce863ee57c1b650ab48eec4b6d5bd
   sovran-react-native: fd3dc8f1a4b14acdc4ad25fc6b4ac4f52a2a2a15
   Swime: d7b2c277503b6cea317774aedc2dce05613f8b0b
   TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
diff --git a/ios/bluesky/Info.plist b/ios/bluesky/Info.plist
index dced58ddf..e578aa25d 100644
--- a/ios/bluesky/Info.plist
+++ b/ios/bluesky/Info.plist
@@ -21,7 +21,7 @@
 	<key>CFBundlePackageType</key>
 	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
 	<key>CFBundleShortVersionString</key>
-	<string>1.7</string>
+	<string>1.8</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleURLTypes</key>
diff --git a/package.json b/package.json
index 30f2c3542..33c72458e 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,9 @@
 {
   "name": "bsky.app",
-  "version": "1.7.0",
+  "version": "1.8.0",
   "private": true,
   "scripts": {
+    "postinstall": "patch-package",
     "android": "expo run:android",
     "ios": "expo run:ios",
     "web": "expo start --web",
@@ -58,12 +59,13 @@
     "expo-camera": "~13.2.1",
     "expo-dev-client": "~2.1.1",
     "expo-image-picker": "~14.1.1",
-    "expo-media-library": "~15.2.1",
+    "expo-media-library": "~15.2.3",
     "expo-splash-screen": "~0.18.1",
     "expo-status-bar": "~1.4.4",
     "he": "^1.2.0",
     "history": "^5.3.0",
     "js-sha256": "^0.9.0",
+    "lande": "^1.0.10",
     "lodash.chunk": "^4.2.0",
     "lodash.clonedeep": "^4.5.0",
     "lodash.debounce": "^4.0.8",
@@ -75,6 +77,8 @@
     "mobx": "^6.6.1",
     "mobx-react-lite": "^3.4.0",
     "normalize-url": "^8.0.0",
+    "patch-package": "^6.5.1",
+    "postinstall-postinstall": "^2.1.0",
     "react": "18.2.0",
     "react-avatar-editor": "^13.0.0",
     "react-circular-progressbar": "^2.1.0",
@@ -90,21 +94,21 @@
     "react-native-image-crop-picker": "^0.38.1",
     "react-native-inappbrowser-reborn": "^3.6.3",
     "react-native-linear-gradient": "^2.6.2",
+    "react-native-pager-view": "6.1.2",
     "react-native-progress": "bluesky-social/react-native-progress",
     "react-native-reanimated": "~2.14.4",
     "react-native-root-siblings": "^4.1.1",
     "react-native-safe-area-context": "^4.4.1",
     "react-native-screens": "^3.13.1",
     "react-native-splash-screen": "^3.3.0",
-    "react-native-svg": "^13.4.0",
-    "react-native-tab-view": "^3.3.0",
+    "react-native-svg": "13.4.0",
     "react-native-url-polyfill": "^1.3.0",
     "react-native-uuid": "^2.0.1",
     "react-native-version-number": "^0.3.6",
     "react-native-web": "^0.18.11",
     "react-native-web-linear-gradient": "^1.1.2",
     "react-native-web-webview": "^1.0.2",
-    "react-native-webview": "^11.26.1",
+    "react-native-webview": "11.26.0",
     "react-native-youtube-iframe": "^2.2.2",
     "rn-fetch-blob": "^0.12.0",
     "tippy.js": "^6.3.7",
diff --git a/patches/react-native-pager-view+6.1.4.patch b/patches/react-native-pager-view+6.1.4.patch
new file mode 100644
index 000000000..adee2533f
--- /dev/null
+++ b/patches/react-native-pager-view+6.1.4.patch
@@ -0,0 +1,54 @@
+diff --git a/node_modules/react-native-pager-view/ios/ReactNativePageView.m b/node_modules/react-native-pager-view/ios/ReactNativePageView.m
+index ab0fc7f..fbbf19f 100644
+--- a/node_modules/react-native-pager-view/ios/ReactNativePageView.m
++++ b/node_modules/react-native-pager-view/ios/ReactNativePageView.m
+@@ -1,6 +1,6 @@
+ 
+ #import "ReactNativePageView.h"
+-#import "React/RCTLog.h"
++#import <React/RCTLog.h>
+ #import <React/RCTViewManager.h>
+ 
+ #import "UIViewController+CreateExtension.h"
+@@ -9,7 +9,7 @@
+ #import "RCTOnPageSelected.h"
+ #import <math.h>
+ 
+-@interface ReactNativePageView () <UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIScrollViewDelegate>
++@interface ReactNativePageView () <UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIScrollViewDelegate, UIGestureRecognizerDelegate>
+ 
+ @property(nonatomic, strong) UIPageViewController *reactPageViewController;
+ @property(nonatomic, strong) RCTEventDispatcher *eventDispatcher;
+@@ -80,6 +80,10 @@
+         [self setupInitialController];
+     }
+ 
++    UIPanGestureRecognizer* panGestureRecognizer = [UIPanGestureRecognizer new];
++    panGestureRecognizer.delegate = self;
++    [self addGestureRecognizer: panGestureRecognizer];
++
+     if (self.reactViewController.navigationController != nil && self.reactViewController.navigationController.interactivePopGestureRecognizer != nil) {
+         [self.scrollView.panGestureRecognizer requireGestureRecognizerToFail:self.reactViewController.navigationController.interactivePopGestureRecognizer];
+     }
+@@ -463,4 +467,21 @@
+ - (BOOL)isLtrLayout {
+     return [_layoutDirection isEqualToString:@"ltr"];
+ }
++
++- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
++    if (otherGestureRecognizer == self.scrollView.panGestureRecognizer) {
++        UIPanGestureRecognizer* p = (UIPanGestureRecognizer*) gestureRecognizer;
++        CGPoint velocity = [p velocityInView:self];
++        if (self.currentIndex == 0 && velocity.x > 0) {
++            self.scrollView.panGestureRecognizer.enabled = false;
++            return NO;
++        } else {
++            self.scrollView.panGestureRecognizer.enabled = self.scrollEnabled;
++        }
++    } else {
++        self.scrollView.panGestureRecognizer.enabled = self.scrollEnabled;
++    }
++    
++    return YES;
++}
+ @end
diff --git a/src/App.native.tsx b/src/App.native.tsx
index fcd6e787b..ebe6a7cd6 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -4,8 +4,10 @@ import {Linking} from 'react-native'
 import {RootSiblingParent} from 'react-native-root-siblings'
 import SplashScreen from 'react-native-splash-screen'
 import {SafeAreaProvider} from 'react-native-safe-area-context'
+import {GestureHandlerRootView} from 'react-native-gesture-handler'
 import {observer} from 'mobx-react-lite'
 import {ThemeProvider} from 'lib/ThemeContext'
+import {s} from 'lib/styles'
 import * as view from './view/index'
 import {RootStoreModel, setupState, RootStoreProvider} from './state'
 import {Shell} from './view/shell'
@@ -51,9 +53,11 @@ const App = observer(() => {
       <RootSiblingParent>
         <analytics.Provider>
           <RootStoreProvider value={rootStore}>
-            <SafeAreaProvider>
-              <Shell />
-            </SafeAreaProvider>
+            <GestureHandlerRootView style={s.h100pct}>
+              <SafeAreaProvider>
+                <Shell />
+              </SafeAreaProvider>
+            </GestureHandlerRootView>
           </RootStoreProvider>
         </analytics.Provider>
       </RootSiblingParent>
diff --git a/src/lib/api/feed-manip.ts b/src/lib/api/feed-manip.ts
index 00938be93..67ca8f952 100644
--- a/src/lib/api/feed-manip.ts
+++ b/src/lib/api/feed-manip.ts
@@ -1,5 +1,7 @@
 import {AppBskyFeedFeedViewPost} from '@atproto/api'
+import lande from 'lande'
 type FeedViewPost = AppBskyFeedFeedViewPost.Main
+import {hasProp} from '@atproto/lexicon'
 
 export type FeedTunerFn = (
   tuner: FeedTuner,
@@ -140,7 +142,8 @@ export class FeedTuner {
       for (const item of slice.items) {
         this.seenUris.add(item.post.uri)
       }
-      slice.logSelf()
+      // DEBUG uncomment to get a quick view of the data
+      // slice.logSelf()
     }
 
     return slices
@@ -177,6 +180,33 @@ export class FeedTuner {
       }
     }
   }
+
+  static englishOnly(tuner: FeedTuner, slices: FeedViewPostsSlice[]) {
+    // TEMP
+    // remove slices with no english in them
+    // we very soon need to get the local user's language and filter
+    // according to their preferences, but for the moment
+    // we're just rolling with english
+    // -prf
+    for (let i = slices.length - 1; i >= 0; i--) {
+      let hasEnglish = false
+      for (const item of slices[i].items) {
+        if (
+          hasProp(item.post.record, 'text') &&
+          typeof item.post.record.text === 'string'
+        ) {
+          const res = lande(item.post.record.text)
+          if (res[0][0] === 'eng') {
+            hasEnglish = true
+            break
+          }
+        }
+      }
+      if (!hasEnglish) {
+        slices.splice(i, 1)
+      }
+    }
+  }
 }
 
 function getSelfReplyUri(item: FeedViewPost): string | undefined {
diff --git a/src/lib/hooks/usePalette.ts b/src/lib/hooks/usePalette.ts
index 5b9929c7d..7eeb74228 100644
--- a/src/lib/hooks/usePalette.ts
+++ b/src/lib/hooks/usePalette.ts
@@ -4,6 +4,7 @@ import {useTheme, PaletteColorName, PaletteColor} from '../ThemeContext'
 export interface UsePaletteValue {
   colors: PaletteColor
   view: ViewStyle
+  viewLight: ViewStyle
   btn: ViewStyle
   border: ViewStyle
   borderDark: ViewStyle
@@ -20,6 +21,9 @@ export function usePalette(color: PaletteColorName): UsePaletteValue {
     view: {
       backgroundColor: palette.background,
     },
+    viewLight: {
+      backgroundColor: palette.backgroundLight,
+    },
     btn: {
       backgroundColor: palette.backgroundLight,
     },
diff --git a/src/lib/styles.ts b/src/lib/styles.ts
index 5d7f7f82d..aa255b21f 100644
--- a/src/lib/styles.ts
+++ b/src/lib/styles.ts
@@ -70,6 +70,7 @@ export const s = StyleSheet.create({
   borderRight1: {borderRightWidth: 1},
   borderBottom1: {borderBottomWidth: 1},
   borderLeft1: {borderLeftWidth: 1},
+  hidden: {display: 'none'},
 
   // font weights
   fw600: {fontWeight: '600'},
diff --git a/src/state/models/discovery/foafs.ts b/src/state/models/discovery/foafs.ts
new file mode 100644
index 000000000..241338a16
--- /dev/null
+++ b/src/state/models/discovery/foafs.ts
@@ -0,0 +1,110 @@
+import {AppBskyActorProfile, AppBskyActorRef} from '@atproto/api'
+import {makeAutoObservable, runInAction} from 'mobx'
+import sampleSize from 'lodash.samplesize'
+import {bundleAsync} from 'lib/async/bundle'
+import {RootStoreModel} from '../root-store'
+
+export type RefWithInfoAndFollowers = AppBskyActorRef.WithInfo & {
+  followers: AppBskyActorProfile.View[]
+}
+
+export type ProfileViewFollows = AppBskyActorProfile.View & {
+  follows: AppBskyActorRef.WithInfo[]
+}
+
+export class FoafsModel {
+  isLoading = false
+  hasData = false
+  sources: string[] = []
+  foafs: Map<string, ProfileViewFollows> = new Map()
+  popular: RefWithInfoAndFollowers[] = []
+
+  constructor(public rootStore: RootStoreModel) {
+    makeAutoObservable(this)
+  }
+
+  get hasContent() {
+    if (this.popular.length > 0) {
+      return true
+    }
+    for (const foaf of this.foafs.values()) {
+      if (foaf.follows.length) {
+        return true
+      }
+    }
+    return false
+  }
+
+  fetch = bundleAsync(async () => {
+    try {
+      this.isLoading = true
+      await this.rootStore.me.follows.fetchIfNeeded()
+      // grab 10 of the users followed by the user
+      this.sources = sampleSize(
+        Object.keys(this.rootStore.me.follows.followDidToRecordMap),
+        10,
+      )
+      if (this.sources.length === 0) {
+        return
+      }
+      this.foafs.clear()
+      this.popular.length = 0
+
+      // fetch their profiles
+      const profiles = await this.rootStore.api.app.bsky.actor.getProfiles({
+        actors: this.sources,
+      })
+
+      // fetch their follows
+      const results = await Promise.allSettled(
+        this.sources.map(source =>
+          this.rootStore.api.app.bsky.graph.getFollows({user: source}),
+        ),
+      )
+
+      // store the follows and construct a "most followed" set
+      const popular: RefWithInfoAndFollowers[] = []
+      for (let i = 0; i < results.length; i++) {
+        const res = results[i]
+        const profile = profiles.data.profiles[i]
+        const source = this.sources[i]
+        if (res.status === 'fulfilled' && profile) {
+          // filter out users already followed by the user or that *is* the user
+          res.value.data.follows = res.value.data.follows.filter(follow => {
+            return (
+              follow.did !== this.rootStore.me.did &&
+              !this.rootStore.me.follows.isFollowing(follow.did)
+            )
+          })
+
+          runInAction(() => {
+            this.foafs.set(source, {
+              ...profile,
+              follows: res.value.data.follows,
+            })
+          })
+          for (const follow of res.value.data.follows) {
+            let item = popular.find(p => p.did === follow.did)
+            if (!item) {
+              item = {...follow, followers: []}
+              popular.push(item)
+            }
+            item.followers.push(profile)
+          }
+        }
+      }
+
+      popular.sort((a, b) => b.followers.length - a.followers.length)
+      runInAction(() => {
+        this.popular = popular.filter(p => p.followers.length > 1).slice(0, 20)
+      })
+      this.hasData = true
+    } catch (e) {
+      console.error('Failed to fetch FOAFs', e)
+    } finally {
+      runInAction(() => {
+        this.isLoading = false
+      })
+    }
+  })
+}
diff --git a/src/state/models/feed-view.ts b/src/state/models/feed-view.ts
index 42b753b24..c412065dd 100644
--- a/src/state/models/feed-view.ts
+++ b/src/state/models/feed-view.ts
@@ -257,7 +257,7 @@ export class FeedModel {
 
   constructor(
     public rootStore: RootStoreModel,
-    public feedType: 'home' | 'author' | 'suggested',
+    public feedType: 'home' | 'author' | 'suggested' | 'goodstuff',
     params: GetTimeline.QueryParams | GetAuthorFeed.QueryParams,
   ) {
     makeAutoObservable(
@@ -336,6 +336,20 @@ export class FeedModel {
     return this.setup()
   }
 
+  private get feedTuners() {
+    if (this.feedType === 'goodstuff') {
+      return [
+        FeedTuner.dedupReposts,
+        FeedTuner.likedRepliesOnly,
+        FeedTuner.englishOnly,
+      ]
+    }
+    if (this.feedType === 'home') {
+      return [FeedTuner.dedupReposts, FeedTuner.likedRepliesOnly]
+    }
+    return []
+  }
+
   /**
    * Load for first render
    */
@@ -399,6 +413,7 @@ export class FeedModel {
           params: this.params,
           e,
         })
+        this.hasMore = false
       }
     } finally {
       this.lock.release()
@@ -476,7 +491,8 @@ export class FeedModel {
     }
     const res = await this._getFeed({limit: 1})
     const currentLatestUri = this.pollCursor
-    const item = res.data.feed[0]
+    const slices = this.tuner.tune(res.data.feed, this.feedTuners)
+    const item = slices[0]?.rootItem
     if (!item) {
       return
     }
@@ -541,12 +557,7 @@ export class FeedModel {
     this.loadMoreCursor = res.data.cursor
     this.hasMore = !!this.loadMoreCursor
 
-    const slices = this.tuner.tune(
-      res.data.feed,
-      this.feedType === 'home'
-        ? [FeedTuner.dedupReposts, FeedTuner.likedRepliesOnly]
-        : [],
-    )
+    const slices = this.tuner.tune(res.data.feed, this.feedTuners)
 
     const toAppend: FeedSliceModel[] = []
     for (const slice of slices) {
@@ -571,12 +582,7 @@ export class FeedModel {
   ) {
     this.pollCursor = res.data.feed[0]?.post.uri
 
-    const slices = this.tuner.tune(
-      res.data.feed,
-      this.feedType === 'home'
-        ? [FeedTuner.dedupReposts, FeedTuner.likedRepliesOnly]
-        : [],
-    )
+    const slices = this.tuner.tune(res.data.feed, this.feedTuners)
 
     const toPrepend: FeedSliceModel[] = []
     for (const slice of slices) {
@@ -634,6 +640,15 @@ export class FeedModel {
       return this.rootStore.api.app.bsky.feed.getTimeline(
         params as GetTimeline.QueryParams,
       )
+    } else if (this.feedType === 'goodstuff') {
+      const res = await getGoodStuff(
+        this.rootStore.session.currentSession?.accessJwt || '',
+        params as GetTimeline.QueryParams,
+      )
+      res.data.feed = (res.data.feed || []).filter(
+        item => !item.post.author.viewer?.muted,
+      )
+      return res
     } else {
       return this.rootStore.api.app.bsky.feed.getAuthorFeed(
         params as GetAuthorFeed.QueryParams,
@@ -641,3 +656,45 @@ export class FeedModel {
     }
   }
 }
+
+// HACK
+// temporary off-spec route to get the good stuff
+// -prf
+async function getGoodStuff(
+  accessJwt: string,
+  params: GetTimeline.QueryParams,
+): Promise<GetTimeline.Response> {
+  const controller = new AbortController()
+  const to = setTimeout(() => controller.abort(), 15e3)
+
+  const uri = new URL('https://bsky.social/xrpc/app.bsky.unspecced.getPopular')
+  let k: keyof GetTimeline.QueryParams
+  for (k in params) {
+    if (typeof params[k] !== 'undefined') {
+      uri.searchParams.set(k, String(params[k]))
+    }
+  }
+
+  const res = await fetch(String(uri), {
+    method: 'get',
+    headers: {
+      accept: 'application/json',
+      authorization: `Bearer ${accessJwt}`,
+    },
+    signal: controller.signal,
+  })
+
+  const resHeaders: Record<string, string> = {}
+  res.headers.forEach((value: string, key: string) => {
+    resHeaders[key] = value
+  })
+  let resBody = await res.json()
+
+  clearTimeout(to)
+
+  return {
+    success: res.status === 200,
+    headers: resHeaders,
+    data: resBody,
+  }
+}
diff --git a/src/state/models/me.ts b/src/state/models/me.ts
index 077c65595..192e8f19f 100644
--- a/src/state/models/me.ts
+++ b/src/state/models/me.ts
@@ -33,6 +33,7 @@ export class MeModel {
   clear() {
     this.mainFeed.clear()
     this.notifications.clear()
+    this.follows.clear()
     this.did = ''
     this.handle = ''
     this.displayName = ''
diff --git a/src/state/models/my-follows.ts b/src/state/models/my-follows.ts
index 732c2fe73..bf1bf9600 100644
--- a/src/state/models/my-follows.ts
+++ b/src/state/models/my-follows.ts
@@ -35,6 +35,12 @@ export class MyFollowsModel {
   // public api
   // =
 
+  clear() {
+    this.followDidToRecordMap = {}
+    this.lastSync = 0
+    this.myDid = undefined
+  }
+
   fetchIfNeeded = bundleAsync(async () => {
     if (
       this.myDid !== this.rootStore.me.did ||
diff --git a/src/state/models/session.ts b/src/state/models/session.ts
index 306c265d8..e131b2b2c 100644
--- a/src/state/models/session.ts
+++ b/src/state/models/session.ts
@@ -154,13 +154,13 @@ export class SessionModel {
   /**
    * Sets the active session
    */
-  setActiveSession(agent: AtpAgent, did: string) {
+  async setActiveSession(agent: AtpAgent, did: string) {
     this._log('SessionModel:setActiveSession')
     this.data = {
       service: agent.service.toString(),
       did,
     }
-    this.rootStore.handleSessionChange(agent)
+    await this.rootStore.handleSessionChange(agent)
   }
 
   /**
@@ -304,7 +304,7 @@ export class SessionModel {
       return false
     }
 
-    this.setActiveSession(agent, account.did)
+    await this.setActiveSession(agent, account.did)
     return true
   }
 
@@ -337,7 +337,7 @@ export class SessionModel {
       },
     )
 
-    this.setActiveSession(agent, did)
+    await this.setActiveSession(agent, did)
     this._log('SessionModel:login succeeded')
   }
 
@@ -376,8 +376,7 @@ export class SessionModel {
       },
     )
 
-    this.setActiveSession(agent, did)
-    this.rootStore.shell.setOnboarding(true)
+    await this.setActiveSession(agent, did)
     this._log('SessionModel:createAccount succeeded')
   }
 
diff --git a/src/state/models/ui/shell.ts b/src/state/models/ui/shell.ts
index d6fefb850..fec1e2899 100644
--- a/src/state/models/ui/shell.ts
+++ b/src/state/models/ui/shell.ts
@@ -122,13 +122,13 @@ export class ShellUiModel {
   darkMode = false
   minimalShellMode = false
   isDrawerOpen = false
+  isDrawerSwipeDisabled = false
   isModalActive = false
   activeModals: Modal[] = []
   isLightboxActive = false
   activeLightbox: ProfileImageLightbox | ImagesLightbox | undefined
   isComposerActive = false
   composerOpts: ComposerOpts | undefined
-  isOnboarding = false
 
   constructor(public rootStore: RootStoreModel) {
     makeAutoObservable(this, {
@@ -168,6 +168,10 @@ export class ShellUiModel {
     this.isDrawerOpen = false
   }
 
+  setIsDrawerSwipeDisabled(v: boolean) {
+    this.isDrawerSwipeDisabled = v
+  }
+
   openModal(modal: Modal) {
     this.rootStore.emitNavigation()
     this.isModalActive = true
@@ -200,13 +204,4 @@ export class ShellUiModel {
     this.isComposerActive = false
     this.composerOpts = undefined
   }
-
-  setOnboarding(v: boolean) {
-    this.isOnboarding = v
-    if (this.isOnboarding) {
-      this.rootStore.me.mainFeed.switchFeedType('suggested')
-    } else {
-      this.rootStore.me.mainFeed.switchFeedType('home')
-    }
-  }
 }
diff --git a/src/view/com/discover/SuggestedFollows.tsx b/src/view/com/discover/SuggestedFollows.tsx
index 1e40956ce..7a64a15f6 100644
--- a/src/view/com/discover/SuggestedFollows.tsx
+++ b/src/view/com/discover/SuggestedFollows.tsx
@@ -1,116 +1,68 @@
 import React from 'react'
-import {ActivityIndicator, StyleSheet, View} from 'react-native'
-import {CenteredView, FlatList} from '../util/Views'
-import {observer} from 'mobx-react-lite'
-import {ErrorScreen} from '../util/error/ErrorScreen'
+import {StyleSheet, View} from 'react-native'
+import {AppBskyActorRef, AppBskyActorProfile} from '@atproto/api'
+import {RefWithInfoAndFollowers} from 'state/models/discovery/foafs'
 import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
-import {useStores} from 'state/index'
-import {
-  SuggestedActorsViewModel,
-  SuggestedActor,
-} from 'state/models/suggested-actors-view'
-import {s} from 'lib/styles'
+import {Text} from '../util/text/Text'
 import {usePalette} from 'lib/hooks/usePalette'
 
-export const SuggestedFollows = observer(
-  ({onNoSuggestions}: {onNoSuggestions?: () => void}) => {
-    const pal = usePalette('default')
-    const store = useStores()
-
-    const view = React.useMemo<SuggestedActorsViewModel>(
-      () => new SuggestedActorsViewModel(store),
-      [store],
-    )
-
-    React.useEffect(() => {
-      view
-        .loadMore()
-        .catch((err: any) =>
-          store.log.error('Failed to fetch suggestions', err),
-        )
-    }, [view, store.log])
-
-    React.useEffect(() => {
-      if (!view.isLoading && !view.hasError && !view.hasContent) {
-        onNoSuggestions?.()
-      }
-    }, [view, view.isLoading, view.hasError, view.hasContent, onNoSuggestions])
-
-    const onRefresh = () => {
-      view
-        .refresh()
-        .catch((err: any) =>
-          store.log.error('Failed to fetch suggestions', err),
-        )
-    }
-    const onEndReached = () => {
-      view
-        .loadMore()
-        .catch(err =>
-          view?.rootStore.log.error('Failed to load more suggestions', err),
-        )
-    }
-
-    const renderItem = ({item}: {item: SuggestedActor}) => {
-      return (
-        <ProfileCardWithFollowBtn
-          key={item.did}
-          did={item.did}
-          declarationCid={item.declaration.cid}
-          handle={item.handle}
-          displayName={item.displayName}
-          avatar={item.avatar}
-          description={item.description}
-        />
-      )
-    }
-    return (
-      <View style={styles.container}>
-        {view.hasError ? (
-          <CenteredView>
-            <ErrorScreen
-              title="Failed to load suggestions"
-              message="There was an error while trying to load suggested follows."
-              details={view.error}
-              onPressTryAgain={onRefresh}
-            />
-          </CenteredView>
-        ) : view.isEmpty ? (
-          <View />
-        ) : (
-          <View style={[styles.suggestionsContainer, pal.view]}>
-            <FlatList
-              data={view.suggestions}
-              keyExtractor={item => item.did}
-              refreshing={view.isRefreshing}
-              onRefresh={onRefresh}
-              onEndReached={onEndReached}
-              renderItem={renderItem}
-              initialNumToRender={15}
-              ListFooterComponent={() => (
-                <View style={styles.footer}>
-                  {view.isLoading && <ActivityIndicator />}
-                </View>
-              )}
-              contentContainerStyle={s.contentContainer}
-            />
-          </View>
-        )}
-      </View>
-    )
-  },
-)
+export const SuggestedFollows = ({
+  title,
+  suggestions,
+}: {
+  title: string
+  suggestions: (AppBskyActorRef.WithInfo | RefWithInfoAndFollowers)[]
+}) => {
+  const pal = usePalette('default')
+  return (
+    <View style={[styles.container, pal.view]}>
+      <Text type="title" style={[styles.heading, pal.text]}>
+        {title}
+      </Text>
+      {suggestions.map(item => (
+        <View key={item.did} style={[styles.card, pal.view, pal.border]}>
+          <ProfileCardWithFollowBtn
+            key={item.did}
+            did={item.did}
+            declarationCid={item.declaration.cid}
+            handle={item.handle}
+            displayName={item.displayName}
+            avatar={item.avatar}
+            noBg
+            noBorder
+            description=""
+            followers={
+              item.followers
+                ? (item.followers as AppBskyActorProfile.View[])
+                : undefined
+            }
+          />
+        </View>
+      ))}
+    </View>
+  )
+}
 
 const styles = StyleSheet.create({
   container: {
-    height: '100%',
+    paddingVertical: 10,
+    paddingHorizontal: 4,
+  },
+
+  heading: {
+    fontWeight: 'bold',
+    paddingHorizontal: 4,
+    paddingBottom: 8,
   },
 
-  suggestionsContainer: {
-    height: '100%',
+  card: {
+    borderRadius: 12,
+    marginBottom: 2,
+    borderWidth: 1,
   },
-  footer: {
-    height: 200,
-    paddingTop: 20,
+
+  loadMore: {
+    paddingLeft: 16,
+    paddingVertical: 12,
   },
 })
diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx
index 1edcd55d9..c910b70e7 100644
--- a/src/view/com/posts/Feed.tsx
+++ b/src/view/com/posts/Feed.tsx
@@ -7,23 +7,17 @@ import {
   StyleSheet,
   ViewStyle,
 } from 'react-native'
-import {useNavigation} from '@react-navigation/native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
 import {CenteredView, FlatList} from '../util/Views'
 import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
-import {Text} from '../util/text/Text'
+import {ViewHeader} from '../util/ViewHeader'
 import {ErrorMessage} from '../util/error/ErrorMessage'
-import {Button} from '../util/forms/Button'
 import {FeedModel} from 'state/models/feed-view'
 import {FeedSlice} from './FeedSlice'
 import {OnScrollCb} from 'lib/hooks/useOnMainScroll'
 import {s} from 'lib/styles'
 import {useAnalytics} from 'lib/analytics'
-import {usePalette} from 'lib/hooks/usePalette'
-import {MagnifyingGlassIcon} from 'lib/icons'
-import {NavigationProp} from 'lib/routes/types'
 
+const HEADER_ITEM = {_reactKey: '__header__'}
 const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
 const ERROR_FEED_ITEM = {_reactKey: '__error__'}
 
@@ -34,6 +28,7 @@ export const Feed = observer(function Feed({
   scrollElRef,
   onPressTryAgain,
   onScroll,
+  renderEmptyState,
   testID,
   headerOffset = 0,
 }: {
@@ -43,17 +38,15 @@ export const Feed = observer(function Feed({
   scrollElRef?: MutableRefObject<FlatList<any> | null>
   onPressTryAgain?: () => void
   onScroll?: OnScrollCb
+  renderEmptyState?: () => JSX.Element
   testID?: string
   headerOffset?: number
 }) {
-  const pal = usePalette('default')
-  const palInverted = usePalette('inverted')
   const {track} = useAnalytics()
   const [isRefreshing, setIsRefreshing] = React.useState(false)
-  const navigation = useNavigation<NavigationProp>()
 
   const data = React.useMemo(() => {
-    let feedItems: any[] = []
+    let feedItems: any[] = [HEADER_ITEM]
     if (feed.hasLoaded) {
       if (feed.hasError) {
         feedItems = feedItems.concat([ERROR_FEED_ITEM])
@@ -80,6 +73,7 @@ export const Feed = observer(function Feed({
     }
     setIsRefreshing(false)
   }, [feed, track, setIsRefreshing])
+
   const onEndReached = React.useCallback(async () => {
     track('Feed:onEndReached')
     try {
@@ -95,37 +89,10 @@ export const Feed = observer(function Feed({
   const renderItem = React.useCallback(
     ({item}: {item: any}) => {
       if (item === EMPTY_FEED_ITEM) {
-        return (
-          <View style={styles.emptyContainer}>
-            <View style={styles.emptyIconContainer}>
-              <MagnifyingGlassIcon
-                style={[styles.emptyIcon, pal.text]}
-                size={62}
-              />
-            </View>
-            <Text type="xl-medium" style={[s.textCenter, pal.text]}>
-              Your feed is empty! You should follow some accounts to fix this.
-            </Text>
-            <Button
-              type="inverted"
-              style={styles.emptyBtn}
-              onPress={
-                () =>
-                  navigation.navigate(
-                    'SearchTab',
-                  ) /* TODO make sure it goes to root of the tab */
-              }>
-              <Text type="lg-medium" style={palInverted.text}>
-                Find accounts
-              </Text>
-              <FontAwesomeIcon
-                icon="angle-right"
-                style={palInverted.text as FontAwesomeIconStyle}
-                size={14}
-              />
-            </Button>
-          </View>
-        )
+        if (renderEmptyState) {
+          return renderEmptyState()
+        }
+        return <View />
       } else if (item === ERROR_FEED_ITEM) {
         return (
           <ErrorMessage
@@ -133,10 +100,12 @@ export const Feed = observer(function Feed({
             onPressTryAgain={onPressTryAgain}
           />
         )
+      } else if (item === HEADER_ITEM) {
+        return <ViewHeader title="Bluesky" canGoBack={false} />
       }
       return <FeedSlice slice={item} showFollowBtn={showPostFollowBtn} />
     },
-    [feed, onPressTryAgain, showPostFollowBtn, pal, palInverted, navigation],
+    [feed, onPressTryAgain, showPostFollowBtn, renderEmptyState],
   )
 
   const FeedFooter = React.useCallback(
@@ -183,21 +152,4 @@ export const Feed = observer(function Feed({
 
 const styles = StyleSheet.create({
   feedFooter: {paddingTop: 20},
-  emptyContainer: {
-    paddingVertical: 40,
-    paddingHorizontal: 30,
-  },
-  emptyIconContainer: {
-    marginBottom: 16,
-  },
-  emptyIcon: {
-    marginLeft: 'auto',
-    marginRight: 'auto',
-  },
-  emptyBtn: {
-    marginTop: 20,
-    flexDirection: 'row',
-    alignItems: 'center',
-    justifyContent: 'space-between',
-  },
 })
diff --git a/src/view/com/posts/FollowingEmptyState.tsx b/src/view/com/posts/FollowingEmptyState.tsx
new file mode 100644
index 000000000..acd035f21
--- /dev/null
+++ b/src/view/com/posts/FollowingEmptyState.tsx
@@ -0,0 +1,81 @@
+import React from 'react'
+import {StyleSheet, View} from 'react-native'
+import {useNavigation} from '@react-navigation/native'
+import {
+  FontAwesomeIcon,
+  FontAwesomeIconStyle,
+} from '@fortawesome/react-native-fontawesome'
+import {Text} from '../util/text/Text'
+import {Button} from '../util/forms/Button'
+import {MagnifyingGlassIcon} from 'lib/icons'
+import {NavigationProp} from 'lib/routes/types'
+import {usePalette} from 'lib/hooks/usePalette'
+import {s} from 'lib/styles'
+
+export function FollowingEmptyState() {
+  const pal = usePalette('default')
+  const palInverted = usePalette('inverted')
+  const navigation = useNavigation<NavigationProp>()
+
+  const onPressFindAccounts = React.useCallback(() => {
+    navigation.navigate('SearchTab')
+    navigation.popToTop()
+  }, [navigation])
+
+  return (
+    <View style={styles.emptyContainer}>
+      <View style={styles.emptyIconContainer}>
+        <MagnifyingGlassIcon style={[styles.emptyIcon, pal.text]} size={62} />
+      </View>
+      <Text type="xl-medium" style={[s.textCenter, pal.text]}>
+        Your following feed is empty! Find some accounts to follow to fix this.
+      </Text>
+      <Button
+        type="inverted"
+        style={styles.emptyBtn}
+        onPress={onPressFindAccounts}>
+        <Text type="lg-medium" style={palInverted.text}>
+          Find accounts to follow
+        </Text>
+        <FontAwesomeIcon
+          icon="angle-right"
+          style={palInverted.text as FontAwesomeIconStyle}
+          size={14}
+        />
+      </Button>
+    </View>
+  )
+}
+const styles = StyleSheet.create({
+  emptyContainer: {
+    // flex: 1,
+    height: '100%',
+    paddingVertical: 40,
+    paddingHorizontal: 30,
+  },
+  emptyIconContainer: {
+    marginBottom: 16,
+  },
+  emptyIcon: {
+    marginLeft: 'auto',
+    marginRight: 'auto',
+  },
+  emptyBtn: {
+    marginVertical: 20,
+    flexDirection: 'row',
+    alignItems: 'center',
+    justifyContent: 'space-between',
+    paddingVertical: 18,
+    paddingHorizontal: 24,
+    borderRadius: 30,
+  },
+
+  feedsTip: {
+    position: 'absolute',
+    left: 22,
+  },
+  feedsTipArrow: {
+    marginLeft: 32,
+    marginTop: 8,
+  },
+})
diff --git a/src/view/com/profile/FollowButton.tsx b/src/view/com/profile/FollowButton.tsx
index f24c3d0c9..5204f5a40 100644
--- a/src/view/com/profile/FollowButton.tsx
+++ b/src/view/com/profile/FollowButton.tsx
@@ -1,16 +1,18 @@
 import React from 'react'
 import {observer} from 'mobx-react-lite'
-import {Button} from '../util/forms/Button'
+import {Button, ButtonType} from '../util/forms/Button'
 import {useStores} from 'state/index'
 import * as apilib from 'lib/api/index'
 import * as Toast from '../util/Toast'
 
 const FollowButton = observer(
   ({
+    type = 'inverted',
     did,
     declarationCid,
     onToggleFollow,
   }: {
+    type?: ButtonType
     did: string
     declarationCid: string
     onToggleFollow?: (v: boolean) => void
@@ -42,7 +44,7 @@ const FollowButton = observer(
 
     return (
       <Button
-        type={isFollowing ? 'default' : 'primary'}
+        type={isFollowing ? 'default' : type}
         onPress={onToggleFollowInner}
         label={isFollowing ? 'Unfollow' : 'Follow'}
       />
diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx
index 087536c36..53f45fb11 100644
--- a/src/view/com/profile/ProfileCard.tsx
+++ b/src/view/com/profile/ProfileCard.tsx
@@ -1,6 +1,7 @@
 import React from 'react'
 import {StyleSheet, View} from 'react-native'
 import {observer} from 'mobx-react-lite'
+import {AppBskyActorProfile} from '@atproto/api'
 import {Link} from '../util/Link'
 import {Text} from '../util/text/Text'
 import {UserAvatar} from '../util/UserAvatar'
@@ -15,7 +16,9 @@ export function ProfileCard({
   avatar,
   description,
   isFollowedBy,
+  noBg,
   noBorder,
+  followers,
   renderButton,
 }: {
   handle: string
@@ -23,7 +26,9 @@ export function ProfileCard({
   avatar?: string
   description?: string
   isFollowedBy?: boolean
+  noBg?: boolean
   noBorder?: boolean
+  followers?: AppBskyActorProfile.View[] | undefined
   renderButton?: () => JSX.Element
 }) {
   const pal = usePalette('default')
@@ -31,9 +36,9 @@ export function ProfileCard({
     <Link
       style={[
         styles.outer,
-        pal.view,
         pal.border,
         noBorder && styles.outerNoBorder,
+        !noBg && pal.view,
       ]}
       href={`/profile/${handle}`}
       title={handle}
@@ -73,6 +78,25 @@ export function ProfileCard({
           </Text>
         </View>
       ) : undefined}
+      {followers?.length ? (
+        <View style={styles.followedBy}>
+          <Text
+            type="sm"
+            style={[styles.followsByDesc, pal.textLight]}
+            numberOfLines={2}
+            lineHeight={1.2}>
+            Followed by{' '}
+            {followers.map(f => f.displayName || f.handle).join(', ')}
+          </Text>
+          {followers.slice(0, 3).map(f => (
+            <View key={f.did} style={styles.followedByAviContainer}>
+              <View style={[styles.followedByAvi, pal.view]}>
+                <UserAvatar avatar={f.avatar} size={32} />
+              </View>
+            </View>
+          ))}
+        </View>
+      ) : undefined}
     </Link>
   )
 }
@@ -86,6 +110,9 @@ export const ProfileCardWithFollowBtn = observer(
     avatar,
     description,
     isFollowedBy,
+    noBg,
+    noBorder,
+    followers,
   }: {
     did: string
     declarationCid: string
@@ -94,6 +121,9 @@ export const ProfileCardWithFollowBtn = observer(
     avatar?: string
     description?: string
     isFollowedBy?: boolean
+    noBg?: boolean
+    noBorder?: boolean
+    followers?: AppBskyActorProfile.View[] | undefined
   }) => {
     const store = useStores()
     const isMe = store.me.handle === handle
@@ -105,6 +135,9 @@ export const ProfileCardWithFollowBtn = observer(
         avatar={avatar}
         description={description}
         isFollowedBy={isFollowedBy}
+        noBg={noBg}
+        noBorder={noBorder}
+        followers={followers}
         renderButton={
           isMe
             ? undefined
@@ -128,8 +161,8 @@ const styles = StyleSheet.create({
     alignItems: 'center',
   },
   layoutAvi: {
-    width: 60,
-    paddingLeft: 10,
+    width: 54,
+    paddingLeft: 4,
     paddingTop: 8,
     paddingBottom: 10,
   },
@@ -164,4 +197,27 @@ const styles = StyleSheet.create({
     marginLeft: 6,
     paddingHorizontal: 14,
   },
+
+  followedBy: {
+    flexDirection: 'row',
+    alignItems: 'flex-start',
+    paddingLeft: 54,
+    paddingRight: 20,
+    marginBottom: 10,
+    marginTop: -6,
+  },
+  followedByAviContainer: {
+    width: 24,
+    height: 36,
+  },
+  followedByAvi: {
+    width: 36,
+    height: 36,
+    borderRadius: 18,
+    padding: 2,
+  },
+  followsByDesc: {
+    flex: 1,
+    paddingRight: 10,
+  },
 })
diff --git a/src/view/com/util/LoadingPlaceholder.tsx b/src/view/com/util/LoadingPlaceholder.tsx
index 9e72640d2..2f653ee09 100644
--- a/src/view/com/util/LoadingPlaceholder.tsx
+++ b/src/view/com/util/LoadingPlaceholder.tsx
@@ -128,6 +128,46 @@ export function NotificationFeedLoadingPlaceholder() {
   )
 }
 
+export function ProfileCardLoadingPlaceholder({
+  style,
+}: {
+  style?: StyleProp<ViewStyle>
+}) {
+  const pal = usePalette('default')
+  return (
+    <View style={[styles.profileCard, pal.view, style]}>
+      <LoadingPlaceholder
+        width={40}
+        height={40}
+        style={styles.profileCardAvi}
+      />
+      <View>
+        <LoadingPlaceholder width={140} height={8} style={[s.mb5]} />
+        <LoadingPlaceholder width={120} height={8} style={[s.mb10]} />
+        <LoadingPlaceholder width={220} height={8} style={[s.mb5]} />
+      </View>
+    </View>
+  )
+}
+
+export function ProfileCardFeedLoadingPlaceholder() {
+  return (
+    <>
+      <ProfileCardLoadingPlaceholder />
+      <ProfileCardLoadingPlaceholder />
+      <ProfileCardLoadingPlaceholder />
+      <ProfileCardLoadingPlaceholder />
+      <ProfileCardLoadingPlaceholder />
+      <ProfileCardLoadingPlaceholder />
+      <ProfileCardLoadingPlaceholder />
+      <ProfileCardLoadingPlaceholder />
+      <ProfileCardLoadingPlaceholder />
+      <ProfileCardLoadingPlaceholder />
+      <ProfileCardLoadingPlaceholder />
+    </>
+  )
+}
+
 const styles = StyleSheet.create({
   loadingPlaceholder: {
     borderRadius: 6,
@@ -147,6 +187,15 @@ const styles = StyleSheet.create({
     paddingLeft: 46,
     margin: 1,
   },
+  profileCard: {
+    flexDirection: 'row',
+    padding: 10,
+    margin: 1,
+  },
+  profileCardAvi: {
+    borderRadius: 20,
+    marginRight: 10,
+  },
   smallAvatar: {
     borderRadius: 15,
     marginRight: 10,
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index 0bb402100..c53de5c1f 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -44,7 +44,7 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
     // two-liner with follow button
     return (
       <View style={styles.metaTwoLine}>
-        <View>
+        <View style={styles.metaTwoLineLeft}>
           <View style={styles.metaTwoLineTop}>
             <DesktopWebTextLink
               type="lg-bold"
@@ -69,6 +69,7 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
             type="md"
             style={[styles.metaItem, pal.textLight]}
             lineHeight={1.2}
+            numberOfLines={1}
             text={`@${handle}`}
             href={`/profile/${opts.authorHandle}`}
           />
@@ -76,6 +77,7 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
 
         <View>
           <FollowButton
+            type="default"
             did={opts.did}
             declarationCid={opts.declarationCid}
             onToggleFollow={onToggleFollow}
@@ -134,7 +136,12 @@ const styles = StyleSheet.create({
     flexDirection: 'row',
     alignItems: 'center',
     justifyContent: 'space-between',
-    paddingBottom: 2,
+    width: '100%',
+    paddingBottom: 4,
+  },
+  metaTwoLineLeft: {
+    flex: 1,
+    paddingRight: 40,
   },
   metaTwoLineTop: {
     flexDirection: 'row',
diff --git a/src/view/com/util/TabBar.tsx b/src/view/com/util/TabBar.tsx
new file mode 100644
index 000000000..4b67b8a80
--- /dev/null
+++ b/src/view/com/util/TabBar.tsx
@@ -0,0 +1,162 @@
+import React, {createRef, useState, useMemo} from 'react'
+import {
+  Animated,
+  StyleSheet,
+  TouchableWithoutFeedback,
+  View,
+} from 'react-native'
+import {Text} from './text/Text'
+import {usePalette} from 'lib/hooks/usePalette'
+import {isDesktopWeb} from 'platform/detection'
+
+interface Layout {
+  x: number
+  width: number
+}
+
+export interface TabBarProps {
+  selectedPage: number
+  items: string[]
+  position: Animated.Value
+  offset: Animated.Value
+  indicatorPosition?: 'top' | 'bottom'
+  indicatorColor?: string
+  onSelect?: (index: number) => void
+  onPressSelected?: () => void
+}
+
+export function TabBar({
+  selectedPage,
+  items,
+  position,
+  offset,
+  indicatorPosition = 'bottom',
+  indicatorColor,
+  onSelect,
+  onPressSelected,
+}: TabBarProps) {
+  const pal = usePalette('default')
+  const [itemLayouts, setItemLayouts] = useState<Layout[]>(
+    items.map(() => ({x: 0, width: 0})),
+  )
+  const itemRefs = useMemo(
+    () => Array.from({length: items.length}).map(() => createRef<View>()),
+    [items.length],
+  )
+  const panX = Animated.add(position, offset)
+
+  const indicatorStyle = {
+    backgroundColor: indicatorColor || pal.colors.link,
+    bottom:
+      indicatorPosition === 'bottom' ? (isDesktopWeb ? 0 : -1) : undefined,
+    top: indicatorPosition === 'top' ? (isDesktopWeb ? 0 : -1) : undefined,
+    transform: [
+      {
+        translateX: panX.interpolate({
+          inputRange: items.map((_item, i) => i),
+          outputRange: itemLayouts.map(l => l.x + l.width / 2),
+        }),
+      },
+      {
+        scaleX: panX.interpolate({
+          inputRange: items.map((_item, i) => i),
+          outputRange: itemLayouts.map(l => l.width),
+        }),
+      },
+    ],
+  }
+
+  const onLayout = () => {
+    const promises = []
+    for (let i = 0; i < items.length; i++) {
+      promises.push(
+        new Promise<Layout>(resolve => {
+          itemRefs[i].current?.measure(
+            (x: number, _y: number, width: number) => {
+              resolve({x, width})
+            },
+          )
+        }),
+      )
+    }
+    Promise.all(promises).then((layouts: Layout[]) => {
+      setItemLayouts(layouts)
+    })
+  }
+
+  const onPressItem = (index: number) => {
+    onSelect?.(index)
+    if (index === selectedPage) {
+      onPressSelected?.()
+    }
+  }
+
+  return (
+    <View style={[pal.view, styles.outer]} onLayout={onLayout}>
+      <Animated.View style={[styles.indicator, indicatorStyle]} />
+      {items.map((item, i) => {
+        const selected = i === selectedPage
+        return (
+          <TouchableWithoutFeedback key={i} onPress={() => onPressItem(i)}>
+            <View
+              style={
+                indicatorPosition === 'top' ? styles.itemTop : styles.itemBottom
+              }
+              ref={itemRefs[i]}>
+              <Text type="xl-bold" style={selected ? pal.text : pal.textLight}>
+                {item}
+              </Text>
+            </View>
+          </TouchableWithoutFeedback>
+        )
+      })}
+    </View>
+  )
+}
+
+const styles = isDesktopWeb
+  ? StyleSheet.create({
+      outer: {
+        flexDirection: 'row',
+        paddingHorizontal: 18,
+      },
+      itemTop: {
+        paddingTop: 16,
+        paddingBottom: 14,
+        marginRight: 24,
+      },
+      itemBottom: {
+        paddingTop: 14,
+        paddingBottom: 16,
+        marginRight: 24,
+      },
+      indicator: {
+        position: 'absolute',
+        left: 0,
+        width: 1,
+        height: 3,
+      },
+    })
+  : StyleSheet.create({
+      outer: {
+        flexDirection: 'row',
+        paddingHorizontal: 14,
+      },
+      itemTop: {
+        paddingTop: 10,
+        paddingBottom: 10,
+        marginRight: 24,
+      },
+      itemBottom: {
+        paddingTop: 8,
+        paddingBottom: 12,
+        marginRight: 24,
+      },
+      indicator: {
+        position: 'absolute',
+        left: 0,
+        width: 1,
+        height: 3,
+        borderRadius: 4,
+      },
+    })
diff --git a/src/view/com/util/WelcomeBanner.tsx b/src/view/com/util/WelcomeBanner.tsx
deleted file mode 100644
index 428a30764..000000000
--- a/src/view/com/util/WelcomeBanner.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-import React from 'react'
-import {StyleSheet, View} from 'react-native'
-import {observer} from 'mobx-react-lite'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {usePalette} from 'lib/hooks/usePalette'
-import {Text} from './text/Text'
-import {Button} from './forms/Button'
-import {s} from 'lib/styles'
-import {useStores} from 'state/index'
-import {SUGGESTED_FOLLOWS} from 'lib/constants'
-// @ts-ignore no type definition -prf
-import ProgressBar from 'react-native-progress/Bar'
-import {CenteredView} from './Views'
-
-export const WelcomeBanner = observer(() => {
-  const pal = usePalette('default')
-  const store = useStores()
-  const [isReady, setIsReady] = React.useState(false)
-
-  const numFollows = Math.min(
-    SUGGESTED_FOLLOWS(String(store.agent.service)).length,
-    5,
-  )
-  const remaining = numFollows - store.me.follows.numFollows
-
-  React.useEffect(() => {
-    if (remaining <= 0) {
-      // wait 500ms for the progress bar anim to finish
-      const ti = setTimeout(() => {
-        setIsReady(true)
-      }, 500)
-      return () => clearTimeout(ti)
-    } else {
-      setIsReady(false)
-    }
-  }, [remaining])
-
-  const onPressDone = React.useCallback(() => {
-    store.shell.setOnboarding(false)
-  }, [store])
-
-  return (
-    <CenteredView
-      testID="welcomeBanner"
-      style={[pal.view, styles.container, pal.border]}>
-      <Text
-        type="title-lg"
-        style={[pal.text, s.textCenter, s.bold, s.pb5]}
-        lineHeight={1.1}>
-        Welcome to Bluesky!
-      </Text>
-      {isReady ? (
-        <View style={styles.controls}>
-          <Button
-            type="primary"
-            style={[s.flexRow, s.alignCenter]}
-            onPress={onPressDone}>
-            <Text type="md-bold" style={s.white}>
-              See my feed!
-            </Text>
-            <FontAwesomeIcon icon="angle-right" size={14} style={s.white} />
-          </Button>
-        </View>
-      ) : (
-        <>
-          <Text type="lg" style={[pal.text, s.textCenter]}>
-            Follow at least {remaining} {remaining === 1 ? 'person' : 'people'}{' '}
-            to build your feed.
-          </Text>
-          <View style={[styles.controls, styles.progress]}>
-            <ProgressBar
-              progress={Math.max(
-                store.me.follows.numFollows / numFollows,
-                0.05,
-              )}
-            />
-          </View>
-        </>
-      )}
-    </CenteredView>
-  )
-})
-
-const styles = StyleSheet.create({
-  container: {
-    paddingTop: 16,
-    paddingBottom: 16,
-    paddingHorizontal: 20,
-    borderTopWidth: 1,
-    borderBottomWidth: 1,
-  },
-  controls: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    justifyContent: 'center',
-    marginTop: 10,
-  },
-  progress: {
-    marginTop: 12,
-  },
-})
diff --git a/src/view/com/util/pager/Pager.tsx b/src/view/com/util/pager/Pager.tsx
new file mode 100644
index 000000000..416828a27
--- /dev/null
+++ b/src/view/com/util/pager/Pager.tsx
@@ -0,0 +1,87 @@
+import React from 'react'
+import {Animated, View} from 'react-native'
+import PagerView, {PagerViewOnPageSelectedEvent} from 'react-native-pager-view'
+import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
+import {s} from 'lib/styles'
+
+export type PageSelectedEvent = PagerViewOnPageSelectedEvent
+const AnimatedPagerView = Animated.createAnimatedComponent(PagerView)
+
+export interface RenderTabBarFnProps {
+  selectedPage: number
+  position: Animated.Value
+  offset: Animated.Value
+  onSelect?: (index: number) => void
+}
+export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element
+
+interface Props {
+  tabBarPosition?: 'top' | 'bottom'
+  initialPage?: number
+  renderTabBar: RenderTabBarFn
+  onPageSelected?: (index: number) => void
+}
+export const Pager = ({
+  children,
+  tabBarPosition = 'top',
+  initialPage = 0,
+  renderTabBar,
+  onPageSelected,
+}: React.PropsWithChildren<Props>) => {
+  const [selectedPage, setSelectedPage] = React.useState(0)
+  const position = useAnimatedValue(0)
+  const offset = useAnimatedValue(0)
+  const pagerView = React.useRef<PagerView>()
+
+  const onPageSelectedInner = React.useCallback(
+    (e: PageSelectedEvent) => {
+      setSelectedPage(e.nativeEvent.position)
+      onPageSelected?.(e.nativeEvent.position)
+    },
+    [setSelectedPage, onPageSelected],
+  )
+
+  const onTabBarSelect = React.useCallback(
+    (index: number) => {
+      pagerView.current?.setPage(index)
+    },
+    [pagerView],
+  )
+
+  return (
+    <View>
+      {tabBarPosition === 'top' &&
+        renderTabBar({
+          selectedPage,
+          position,
+          offset,
+          onSelect: onTabBarSelect,
+        })}
+      <AnimatedPagerView
+        ref={pagerView}
+        style={s.h100pct}
+        initialPage={initialPage}
+        onPageSelected={onPageSelectedInner}
+        onPageScroll={Animated.event(
+          [
+            {
+              nativeEvent: {
+                position: position,
+                offset: offset,
+              },
+            },
+          ],
+          {useNativeDriver: true},
+        )}>
+        {children}
+      </AnimatedPagerView>
+      {tabBarPosition === 'bottom' &&
+        renderTabBar({
+          selectedPage,
+          position,
+          offset,
+          onSelect: onTabBarSelect,
+        })}
+    </View>
+  )
+}
diff --git a/src/view/com/util/pager/Pager.web.tsx b/src/view/com/util/pager/Pager.web.tsx
new file mode 100644
index 000000000..3c2805833
--- /dev/null
+++ b/src/view/com/util/pager/Pager.web.tsx
@@ -0,0 +1,69 @@
+import React from 'react'
+import {Animated, View} from 'react-native'
+import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
+import {s} from 'lib/styles'
+
+export interface RenderTabBarFnProps {
+  selectedPage: number
+  position: Animated.Value
+  offset: Animated.Value
+  onSelect?: (index: number) => void
+}
+export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element
+
+interface Props {
+  tabBarPosition?: 'top' | 'bottom'
+  initialPage?: number
+  renderTabBar: RenderTabBarFn
+  onPageSelected?: (index: number) => void
+}
+export const Pager = ({
+  children,
+  tabBarPosition = 'top',
+  initialPage = 0,
+  renderTabBar,
+  onPageSelected,
+}: React.PropsWithChildren<Props>) => {
+  const [selectedPage, setSelectedPage] = React.useState(initialPage)
+  const position = useAnimatedValue(0)
+  const offset = useAnimatedValue(0)
+
+  const onTabBarSelect = React.useCallback(
+    (index: number) => {
+      setSelectedPage(index)
+      onPageSelected?.(index)
+      Animated.timing(position, {
+        toValue: index,
+        duration: 200,
+        useNativeDriver: true,
+      }).start()
+    },
+    [setSelectedPage, onPageSelected, position],
+  )
+
+  return (
+    <View>
+      {tabBarPosition === 'top' &&
+        renderTabBar({
+          selectedPage,
+          position,
+          offset,
+          onSelect: onTabBarSelect,
+        })}
+      {children.map((child, i) => (
+        <View
+          style={selectedPage === i ? undefined : s.hidden}
+          key={`page-${i}`}>
+          {child}
+        </View>
+      ))}
+      {tabBarPosition === 'bottom' &&
+        renderTabBar({
+          selectedPage,
+          position,
+          offset,
+          onSelect: onTabBarSelect,
+        })}
+    </View>
+  )
+}
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index adc73315c..4950bc0fd 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -1,26 +1,97 @@
 import React from 'react'
-import {FlatList, View} from 'react-native'
+import {FlatList, View, useWindowDimensions} from 'react-native'
 import {useFocusEffect, useIsFocused} from '@react-navigation/native'
 import {observer} from 'mobx-react-lite'
 import useAppState from 'react-native-appstate-hook'
 import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types'
+import {FeedModel} from 'state/models/feed-view'
 import {withAuthRequired} from 'view/com/auth/withAuthRequired'
-import {ViewHeader} from '../com/util/ViewHeader'
 import {Feed} from '../com/posts/Feed'
+import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState'
 import {LoadLatestBtn} from '../com/util/LoadLatestBtn'
-import {WelcomeBanner} from '../com/util/WelcomeBanner'
+import {FeedsTabBar} from './home/FeedsTabBar'
+import {Pager, RenderTabBarFnProps} from 'view/com/util/pager/Pager'
 import {FAB} from '../com/util/FAB'
 import {useStores} from 'state/index'
 import {s} from 'lib/styles'
 import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
 import {useAnalytics} from 'lib/analytics'
 import {ComposeIcon2} from 'lib/icons'
+import {isDesktopWeb} from 'platform/detection'
 
-const HEADER_HEIGHT = 42
+const TAB_BAR_HEIGHT = 82
 
 type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
-export const HomeScreen = withAuthRequired(
-  observer(function Home(_opts: Props) {
+export const HomeScreen = withAuthRequired((_opts: Props) => {
+  const store = useStores()
+  const [selectedPage, setSelectedPage] = React.useState(0)
+
+  const algoFeed = React.useMemo(() => {
+    const feed = new FeedModel(store, 'goodstuff', {})
+    feed.setup()
+    return feed
+  }, [store])
+
+  useFocusEffect(
+    React.useCallback(() => {
+      store.shell.setIsDrawerSwipeDisabled(selectedPage > 0)
+      return () => {
+        store.shell.setIsDrawerSwipeDisabled(false)
+      }
+    }, [store, selectedPage]),
+  )
+
+  const onPageSelected = React.useCallback(
+    (index: number) => {
+      setSelectedPage(index)
+      store.shell.setIsDrawerSwipeDisabled(index > 0)
+    },
+    [store],
+  )
+
+  const onPressSelected = React.useCallback(() => {
+    store.emitScreenSoftReset()
+  }, [store])
+
+  const renderTabBar = React.useCallback(
+    (props: RenderTabBarFnProps) => {
+      return <FeedsTabBar {...props} onPressSelected={onPressSelected} />
+    },
+    [onPressSelected],
+  )
+
+  const renderFollowingEmptyState = React.useCallback(() => {
+    return <FollowingEmptyState />
+  }, [])
+
+  const initialPage = store.me.follows.isEmpty ? 1 : 0
+  return (
+    <Pager
+      onPageSelected={onPageSelected}
+      renderTabBar={renderTabBar}
+      tabBarPosition={isDesktopWeb ? 'top' : 'bottom'}
+      initialPage={initialPage}>
+      <FeedPage
+        key="1"
+        isPageFocused={selectedPage === 0}
+        feed={store.me.mainFeed}
+        renderEmptyState={renderFollowingEmptyState}
+      />
+      <FeedPage key="2" isPageFocused={selectedPage === 1} feed={algoFeed} />
+    </Pager>
+  )
+})
+
+const FeedPage = observer(
+  ({
+    isPageFocused,
+    feed,
+    renderEmptyState,
+  }: {
+    feed: FeedModel
+    isPageFocused: boolean
+    renderEmptyState?: () => JSX.Element
+  }) => {
     const store = useStores()
     const onMainScroll = useOnMainScroll(store)
     const {screen, track} = useAnalytics()
@@ -28,38 +99,51 @@ export const HomeScreen = withAuthRequired(
     const {appState} = useAppState({
       onForeground: () => doPoll(true),
     })
-    const isFocused = useIsFocused()
+    const isScreenFocused = useIsFocused()
+    const winDim = useWindowDimensions()
+    const containerStyle = React.useMemo(
+      () => ({height: winDim.height - (isDesktopWeb ? 0 : TAB_BAR_HEIGHT)}),
+      [winDim],
+    )
 
     const doPoll = React.useCallback(
       (knownActive = false) => {
-        if ((!knownActive && appState !== 'active') || !isFocused) {
+        if (
+          (!knownActive && appState !== 'active') ||
+          !isScreenFocused ||
+          !isPageFocused
+        ) {
           return
         }
-        if (store.me.mainFeed.isLoading) {
+        if (feed.isLoading) {
           return
         }
         store.log.debug('HomeScreen: Polling for new posts')
-        store.me.mainFeed.checkForLatest()
+        feed.checkForLatest()
       },
-      [appState, isFocused, store],
+      [appState, isScreenFocused, isPageFocused, store, feed],
     )
 
     const scrollToTop = React.useCallback(() => {
-      // NOTE: the feed is offset by the height of the collapsing header,
-      //       so we scroll to the negative of that height -prf
-      scrollElRef.current?.scrollToOffset({offset: -HEADER_HEIGHT})
+      scrollElRef.current?.scrollToOffset({offset: 0})
     }, [scrollElRef])
 
+    const onSoftReset = React.useCallback(() => {
+      if (isPageFocused) {
+        scrollToTop()
+      }
+    }, [isPageFocused, scrollToTop])
+
     useFocusEffect(
       React.useCallback(() => {
-        const softResetSub = store.onScreenSoftReset(scrollToTop)
-        const feedCleanup = store.me.mainFeed.registerListeners()
+        const softResetSub = store.onScreenSoftReset(onSoftReset)
+        const feedCleanup = feed.registerListeners()
         const pollInterval = setInterval(doPoll, 15e3)
 
         screen('Feed')
         store.log.debug('HomeScreen: Updating feed')
-        if (store.me.mainFeed.hasContent) {
-          store.me.mainFeed.update()
+        if (feed.hasContent) {
+          feed.update()
         }
 
         return () => {
@@ -67,7 +151,7 @@ export const HomeScreen = withAuthRequired(
           softResetSub.remove()
           feedCleanup()
         }
-      }, [store, doPoll, scrollToTop, screen]),
+      }, [store, doPoll, onSoftReset, screen, feed]),
     )
 
     const onPressCompose = React.useCallback(() => {
@@ -76,32 +160,28 @@ export const HomeScreen = withAuthRequired(
     }, [store, track])
 
     const onPressTryAgain = React.useCallback(() => {
-      store.me.mainFeed.refresh()
-    }, [store])
+      feed.refresh()
+    }, [feed])
 
     const onPressLoadLatest = React.useCallback(() => {
-      store.me.mainFeed.refresh()
+      feed.refresh()
       scrollToTop()
-    }, [store, scrollToTop])
+    }, [feed, scrollToTop])
 
     return (
-      <View style={s.hContentRegion}>
-        {store.shell.isOnboarding && <WelcomeBanner />}
+      <View style={containerStyle}>
         <Feed
           testID="homeFeed"
           key="default"
-          feed={store.me.mainFeed}
+          feed={feed}
           scrollElRef={scrollElRef}
           style={s.hContentRegion}
           showPostFollowBtn
           onPressTryAgain={onPressTryAgain}
           onScroll={onMainScroll}
-          headerOffset={store.shell.isOnboarding ? 0 : HEADER_HEIGHT}
+          renderEmptyState={renderEmptyState}
         />
-        {!store.shell.isOnboarding && (
-          <ViewHeader title="Bluesky" canGoBack={false} hideOnScroll />
-        )}
-        {store.me.mainFeed.hasNewLatest && !store.me.mainFeed.isRefreshing && (
+        {feed.hasNewLatest && !feed.isRefreshing && (
           <LoadLatestBtn onPress={onPressLoadLatest} />
         )}
         <FAB
@@ -111,5 +191,5 @@ export const HomeScreen = withAuthRequired(
         />
       </View>
     )
-  }),
+  },
 )
diff --git a/src/view/screens/Search.tsx b/src/view/screens/Search.tsx
index 19535a164..6ae5fba0d 100644
--- a/src/view/screens/Search.tsx
+++ b/src/view/screens/Search.tsx
@@ -1,6 +1,7 @@
 import React from 'react'
 import {
   Keyboard,
+  RefreshControl,
   StyleSheet,
   TextInput,
   TouchableOpacity,
@@ -13,21 +14,23 @@ import {
   FontAwesomeIconStyle,
 } from '@fortawesome/react-native-fontawesome'
 import {withAuthRequired} from 'view/com/auth/withAuthRequired'
-import {ScrollView} from '../com/util/Views'
+import {ScrollView} from 'view/com/util/Views'
 import {
   NativeStackScreenProps,
   SearchTabNavigatorParams,
 } from 'lib/routes/types'
 import {observer} from 'mobx-react-lite'
-import {UserAvatar} from '../com/util/UserAvatar'
-import {Text} from '../com/util/text/Text'
+import {UserAvatar} from 'view/com/util/UserAvatar'
+import {Text} from 'view/com/util/text/Text'
 import {useStores} from 'state/index'
 import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view'
+import {FoafsModel} from 'state/models/discovery/foafs'
 import {s} from 'lib/styles'
 import {MagnifyingGlassIcon} from 'lib/icons'
-import {WhoToFollow} from '../com/discover/WhoToFollow'
-import {SuggestedPosts} from '../com/discover/SuggestedPosts'
-import {ProfileCard} from '../com/profile/ProfileCard'
+import {WhoToFollow} from 'view/com/discover/WhoToFollow'
+import {SuggestedFollows} from 'view/com/discover/SuggestedFollows'
+import {ProfileCard} from 'view/com/profile/ProfileCard'
+import {ProfileCardFeedLoadingPlaceholder} from 'view/com/util/LoadingPlaceholder'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useTheme} from 'lib/ThemeContext'
 import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
@@ -53,6 +56,11 @@ export const SearchScreen = withAuthRequired(
       () => new UserAutocompleteViewModel(store),
       [store],
     )
+    const foafsView = React.useMemo<FoafsModel>(
+      () => new FoafsModel(store),
+      [store],
+    )
+    const [refreshing, setRefreshing] = React.useState(false)
 
     const onSoftReset = () => {
       scrollElRef.current?.scrollTo({x: 0, y: 0})
@@ -71,9 +79,12 @@ export const SearchScreen = withAuthRequired(
         }
         store.shell.setMinimalShellMode(false)
         autocompleteView.setup()
+        if (!foafsView.hasData) {
+          foafsView.fetch()
+        }
 
         return cleanup
-      }, [store, autocompleteView, lastRenderTime, setRenderTime]),
+      }, [store, autocompleteView, foafsView, lastRenderTime, setRenderTime]),
     )
 
     const onPressMenu = () => {
@@ -98,15 +109,18 @@ export const SearchScreen = withAuthRequired(
       autocompleteView.setActive(false)
       textInput.current?.blur()
     }
+    const onRefresh = React.useCallback(async () => {
+      setRefreshing(true)
+      try {
+        await foafsView.fetch()
+      } finally {
+        setRefreshing(false)
+      }
+    }, [foafsView, setRefreshing])
 
     return (
       <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
-        <ScrollView
-          ref={scrollElRef}
-          testID="searchScrollView"
-          style={[pal.view, styles.container]}
-          onScroll={onMainScroll}
-          scrollEventThrottle={100}>
+        <View style={[pal.view, styles.container]}>
           <View style={[pal.view, pal.border, styles.header]}>
             <TouchableOpacity
               testID="viewHeaderBackOrMenuBtn"
@@ -180,14 +194,53 @@ export const SearchScreen = withAuthRequired(
               </Text>
             </View>
           ) : (
-            <ScrollView onScroll={Keyboard.dismiss}>
-              <WhoToFollow key={`wtf-${lastRenderTime}`} />
-              <SuggestedPosts key={`sp-${lastRenderTime}`} />
+            <ScrollView
+              ref={scrollElRef}
+              testID="searchScrollView"
+              style={pal.view}
+              onScroll={onMainScroll}
+              scrollEventThrottle={100}
+              refreshControl={
+                <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
+              }>
+              {foafsView.isLoading ? (
+                <ProfileCardFeedLoadingPlaceholder />
+              ) : foafsView.hasContent ? (
+                <>
+                  {foafsView.popular.length > 0 && (
+                    <View style={styles.suggestions}>
+                      <SuggestedFollows
+                        title="In your network"
+                        suggestions={foafsView.popular}
+                      />
+                    </View>
+                  )}
+                  {foafsView.sources.map((source, i) => {
+                    const item = foafsView.foafs.get(source)
+                    if (!item || item.follows.length === 0) {
+                      return <View key={`sf-${item?.did || i}`} />
+                    }
+                    return (
+                      <View key={`sf-${item.did}`} style={styles.suggestions}>
+                        <SuggestedFollows
+                          title={`Followed by ${
+                            item.displayName || item.handle
+                          }`}
+                          suggestions={item.follows.slice(0, 10)}
+                        />
+                      </View>
+                    )
+                  })}
+                </>
+              ) : (
+                <View style={pal.view}>
+                  <WhoToFollow />
+                </View>
+              )}
               <View style={s.footerSpacer} />
             </ScrollView>
           )}
-          <View style={s.footerSpacer} />
-        </ScrollView>
+        </View>
       </TouchableWithoutFeedback>
     )
   }),
@@ -235,4 +288,8 @@ const styles = StyleSheet.create({
     textAlign: 'center',
     paddingTop: 10,
   },
+
+  suggestions: {
+    marginBottom: 8,
+  },
 })
diff --git a/src/view/screens/home/FeedsTabBar.tsx b/src/view/screens/home/FeedsTabBar.tsx
new file mode 100644
index 000000000..d34034103
--- /dev/null
+++ b/src/view/screens/home/FeedsTabBar.tsx
@@ -0,0 +1,72 @@
+import React from 'react'
+import {Animated, StyleSheet} from 'react-native'
+import {observer} from 'mobx-react-lite'
+import {TabBar} from 'view/com/util/TabBar'
+import {RenderTabBarFnProps} from 'view/com/util/pager/Pager'
+import {useStores} from 'state/index'
+import {usePalette} from 'lib/hooks/usePalette'
+import {useSafeAreaInsets} from 'react-native-safe-area-context'
+import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
+import {clamp} from 'lodash'
+
+const BOTTOM_BAR_HEIGHT = 48
+
+export const FeedsTabBar = observer(
+  (props: RenderTabBarFnProps & {onPressSelected: () => void}) => {
+    const store = useStores()
+    const safeAreaInsets = useSafeAreaInsets()
+    const pal = usePalette('default')
+    const interp = useAnimatedValue(0)
+
+    const pad = React.useMemo(
+      () => ({
+        paddingBottom: clamp(safeAreaInsets.bottom, 15, 20),
+      }),
+      [safeAreaInsets],
+    )
+
+    React.useEffect(() => {
+      Animated.timing(interp, {
+        toValue: store.shell.minimalShellMode ? 0 : 1,
+        duration: 100,
+        useNativeDriver: true,
+        isInteraction: false,
+      }).start()
+    }, [interp, store.shell.minimalShellMode])
+    const transform = {
+      transform: [
+        {translateY: Animated.multiply(interp, -1 * BOTTOM_BAR_HEIGHT)},
+      ],
+    }
+
+    return (
+      <Animated.View
+        style={[pal.view, pal.border, styles.tabBar, pad, transform]}>
+        <TabBar
+          {...props}
+          items={['Following', "What's hot"]}
+          indicatorPosition="top"
+          indicatorColor={pal.colors.link}
+        />
+      </Animated.View>
+    )
+  },
+)
+
+const styles = StyleSheet.create({
+  tabBar: {
+    position: 'absolute',
+    left: 0,
+    right: 0,
+    bottom: 0,
+    flexDirection: 'row',
+    alignItems: 'center',
+    paddingHorizontal: 10,
+    borderTopWidth: 1,
+    paddingTop: 0,
+    paddingBottom: 30,
+  },
+  tabBarAvi: {
+    marginRight: 4,
+  },
+})
diff --git a/src/view/screens/home/FeedsTabBar.web.tsx b/src/view/screens/home/FeedsTabBar.web.tsx
new file mode 100644
index 000000000..59ea42988
--- /dev/null
+++ b/src/view/screens/home/FeedsTabBar.web.tsx
@@ -0,0 +1,22 @@
+import React from 'react'
+import {observer} from 'mobx-react-lite'
+import {TabBar} from 'view/com/util/TabBar'
+import {CenteredView} from 'view/com/util/Views'
+import {RenderTabBarFnProps} from 'view/com/util/pager/Pager'
+import {usePalette} from 'lib/hooks/usePalette'
+
+export const FeedsTabBar = observer(
+  (props: RenderTabBarFnProps & {onPressSelected: () => void}) => {
+    const pal = usePalette('default')
+    return (
+      <CenteredView>
+        <TabBar
+          {...props}
+          items={['Following', "What's hot"]}
+          indicatorPosition="bottom"
+          indicatorColor={pal.colors.link}
+        />
+      </CenteredView>
+    )
+  },
+)
diff --git a/src/view/shell/BottomBar.tsx b/src/view/shell/BottomBar.tsx
index c59d8c675..1cbf2de86 100644
--- a/src/view/shell/BottomBar.tsx
+++ b/src/view/shell/BottomBar.tsx
@@ -34,16 +34,24 @@ export const BottomBar = observer(({navigation}: BottomTabBarProps) => {
   const minimalShellInterp = useAnimatedValue(0)
   const safeAreaInsets = useSafeAreaInsets()
   const {track} = useAnalytics()
-  const {isAtHome, isAtSearch, isAtNotifications} = useNavigationState(
-    state => {
-      return {
+  const {isAtHome, isAtSearch, isAtNotifications, noBorder} =
+    useNavigationState(state => {
+      const res = {
         isAtHome: getTabState(state, 'Home') !== TabState.Outside,
         isAtSearch: getTabState(state, 'Search') !== TabState.Outside,
         isAtNotifications:
           getTabState(state, 'Notifications') !== TabState.Outside,
+        noBorder: getTabState(state, 'Home') === TabState.InsideAtRoot,
       }
-    },
-  )
+      if (!res.isAtHome && !res.isAtNotifications && !res.isAtSearch) {
+        // HACK for some reason useNavigationState will give us pre-hydration results
+        //      and not update after, so we force isAtHome if all came back false
+        //      -prf
+        res.isAtHome = true
+        res.noBorder = true
+      }
+      return res
+    })
 
   React.useEffect(() => {
     if (store.shell.minimalShellMode) {
@@ -99,6 +107,7 @@ export const BottomBar = observer(({navigation}: BottomTabBarProps) => {
     <Animated.View
       style={[
         styles.bottomBar,
+        noBorder && styles.noBorder,
         pal.view,
         pal.border,
         {paddingBottom: clamp(safeAreaInsets.bottom, 15, 30)},
@@ -213,6 +222,9 @@ const styles = StyleSheet.create({
     paddingLeft: 5,
     paddingRight: 10,
   },
+  noBorder: {
+    borderTopWidth: 0,
+  },
   ctrl: {
     flex: 1,
     paddingTop: 13,
diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx
index d7877804b..eec0f8ed4 100644
--- a/src/view/shell/index.tsx
+++ b/src/view/shell/index.tsx
@@ -46,7 +46,11 @@ const ShellInner = observer(() => {
             onOpen={onOpenDrawer}
             onClose={onCloseDrawer}
             swipeEdgeWidth={winDim.width}
-            swipeEnabled={!canGoBack && store.session.hasSession}>
+            swipeEnabled={
+              !canGoBack &&
+              store.session.hasSession &&
+              !store.shell.isDrawerSwipeDisabled
+            }>
             <TabsNavigator />
           </Drawer>
         </ErrorBoundary>
diff --git a/yarn.lock b/yarn.lock
index 43600fa97..f3b747e8f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -208,20 +208,20 @@
   integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==
 
 "@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.14.0", "@babel/core@^7.16.0", "@babel/core@^7.20.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0":
-  version "7.21.0"
-  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.0.tgz#1341aefdcc14ccc7553fcc688dd8986a2daffc13"
-  integrity sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e"
+  integrity sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==
   dependencies:
     "@ampproject/remapping" "^2.2.0"
     "@babel/code-frame" "^7.18.6"
-    "@babel/generator" "^7.21.0"
+    "@babel/generator" "^7.21.3"
     "@babel/helper-compilation-targets" "^7.20.7"
-    "@babel/helper-module-transforms" "^7.21.0"
+    "@babel/helper-module-transforms" "^7.21.2"
     "@babel/helpers" "^7.21.0"
-    "@babel/parser" "^7.21.0"
+    "@babel/parser" "^7.21.3"
     "@babel/template" "^7.20.7"
-    "@babel/traverse" "^7.21.0"
-    "@babel/types" "^7.21.0"
+    "@babel/traverse" "^7.21.3"
+    "@babel/types" "^7.21.3"
     convert-source-map "^1.7.0"
     debug "^4.1.0"
     gensync "^1.0.0-beta.2"
@@ -229,20 +229,20 @@
     semver "^6.3.0"
 
 "@babel/eslint-parser@^7.16.3", "@babel/eslint-parser@^7.18.2":
-  version "7.19.1"
-  resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz#4f68f6b0825489e00a24b41b6a1ae35414ecd2f4"
-  integrity sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.21.3.tgz#d79e822050f2de65d7f368a076846e7184234af7"
+  integrity sha512-kfhmPimwo6k4P8zxNs8+T7yR44q1LdpsZdE1NkCsVlfiuTPRfnGgjaF8Qgug9q9Pou17u6wneYF0lDCZJATMFg==
   dependencies:
     "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1"
     eslint-visitor-keys "^2.1.0"
     semver "^6.3.0"
 
-"@babel/generator@^7.20.0", "@babel/generator@^7.21.0", "@babel/generator@^7.21.1", "@babel/generator@^7.7.2":
-  version "7.21.1"
-  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.1.tgz#951cc626057bc0af2c35cd23e9c64d384dea83dd"
-  integrity sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==
+"@babel/generator@^7.20.0", "@babel/generator@^7.21.3", "@babel/generator@^7.7.2":
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce"
+  integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==
   dependencies:
-    "@babel/types" "^7.21.0"
+    "@babel/types" "^7.21.3"
     "@jridgewell/gen-mapping" "^0.3.2"
     "@jridgewell/trace-mapping" "^0.3.17"
     jsesc "^2.5.1"
@@ -348,7 +348,7 @@
   dependencies:
     "@babel/types" "^7.18.6"
 
-"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.0", "@babel/helper-module-transforms@^7.21.2":
+"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2":
   version "7.21.2"
   resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2"
   integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==
@@ -460,10 +460,10 @@
     chalk "^2.0.0"
     js-tokens "^4.0.0"
 
-"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.21.0", "@babel/parser@^7.21.2":
-  version "7.21.2"
-  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.2.tgz#dacafadfc6d7654c3051a66d6fe55b6cb2f2a0b3"
-  integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==
+"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3":
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3"
+  integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==
 
 "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
   version "7.18.6"
@@ -837,9 +837,9 @@
     "@babel/template" "^7.20.7"
 
 "@babel/plugin-transform-destructuring@^7.0.0", "@babel/plugin-transform-destructuring@^7.20.2":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454"
-  integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401"
+  integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==
   dependencies:
     "@babel/helper-plugin-utils" "^7.20.2"
 
@@ -970,9 +970,9 @@
     "@babel/helper-replace-supers" "^7.18.6"
 
 "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.1", "@babel/plugin-transform-parameters@^7.20.7":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f"
-  integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db"
+  integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.20.2"
 
@@ -984,9 +984,9 @@
     "@babel/helper-plugin-utils" "^7.18.6"
 
 "@babel/plugin-transform-react-constant-elements@^7.12.1":
-  version "7.20.2"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.20.2.tgz#3f02c784e0b711970d7d8ccc96c4359d64e27ac7"
-  integrity sha512-KS/G8YI8uwMGKErLFOHS/ekhqdHhpEloxs43NecQHVgo2QuQSyJhGIY1fL8UGl9wy5ItVwwoUL4YxVqsplGq2g==
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.21.3.tgz#b32a5556100d424b25e388dd689050d78396884d"
+  integrity sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.20.2"
 
@@ -1101,10 +1101,11 @@
     "@babel/helper-plugin-utils" "^7.18.9"
 
 "@babel/plugin-transform-typescript@^7.21.0", "@babel/plugin-transform-typescript@^7.5.0":
-  version "7.21.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz#f0956a153679e3b377ae5b7f0143427151e4c848"
-  integrity sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg==
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz#316c5be579856ea890a57ebc5116c5d064658f2b"
+  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"
@@ -1278,26 +1279,26 @@
     "@babel/parser" "^7.20.7"
     "@babel/types" "^7.20.7"
 
-"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.7.2", "@babel/traverse@^7.7.4":
-  version "7.21.2"
-  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.2.tgz#ac7e1f27658750892e815e60ae90f382a46d8e75"
-  integrity sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==
+"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3", "@babel/traverse@^7.7.2", "@babel/traverse@^7.7.4":
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67"
+  integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==
   dependencies:
     "@babel/code-frame" "^7.18.6"
-    "@babel/generator" "^7.21.1"
+    "@babel/generator" "^7.21.3"
     "@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.3"
+    "@babel/types" "^7.21.3"
     debug "^4.1.0"
     globals "^11.1.0"
 
-"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
-  version "7.21.2"
-  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.2.tgz#92246f6e00f91755893c2876ad653db70c8310d1"
-  integrity sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==
+"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05"
+  integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==
   dependencies:
     "@babel/helper-string-parser" "^7.19.4"
     "@babel/helper-validator-identifier" "^7.19.1"
@@ -1436,14 +1437,26 @@
   dependencies:
     "@types/hammerjs" "^2.0.36"
 
-"@eslint/eslintrc@^2.0.0":
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.0.tgz#943309d8697c52fc82c076e90c1c74fbbe69dbff"
-  integrity sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==
+"@eslint-community/eslint-utils@^4.2.0":
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a"
+  integrity sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA==
+  dependencies:
+    eslint-visitor-keys "^3.3.0"
+
+"@eslint-community/regexpp@^4.4.0":
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403"
+  integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==
+
+"@eslint/eslintrc@^2.0.1":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d"
+  integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==
   dependencies:
     ajv "^6.12.4"
     debug "^4.3.2"
-    espree "^9.4.0"
+    espree "^9.5.0"
     globals "^13.19.0"
     ignore "^5.2.0"
     import-fresh "^3.2.1"
@@ -1451,10 +1464,10 @@
     minimatch "^3.1.2"
     strip-json-comments "^3.1.1"
 
-"@eslint/js@8.35.0":
-  version "8.35.0"
-  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.35.0.tgz#b7569632b0b788a0ca0e438235154e45d42813a7"
-  integrity sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==
+"@eslint/js@8.36.0":
+  version "8.36.0"
+  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe"
+  integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==
 
 "@expo/bunyan@4.0.0", "@expo/bunyan@^4.0.0":
   version "4.0.0"
@@ -1831,9 +1844,9 @@
     cross-spawn "^6.0.5"
 
 "@expo/spawn-async@^1.5.0":
-  version "1.7.0"
-  resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.7.0.tgz#3ab6082b24318cccc4e73b13464da91325555500"
-  integrity sha512-sqPAjOEFTrjaTybrh9SnPFLInDXcoMC06psEFmH68jLTmoipSQCq8GCEfIoHhxRDALWB+DsiwXJSbXlE/iVIIQ==
+  version "1.7.2"
+  resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.7.2.tgz#fcfe66c3e387245e72154b1a7eae8cada6a47f58"
+  integrity sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==
   dependencies:
     cross-spawn "^7.0.3"
 
@@ -2502,9 +2515,9 @@
     semver "7.3.8"
 
 "@miblanchard/react-native-slider@^2.2.0":
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/@miblanchard/react-native-slider/-/react-native-slider-2.3.0.tgz#91a3294487a9b91e9ff2f841c7ed855ebadfccc9"
-  integrity sha512-KiAESUOP69eft8U43iD6xeX+s9ZetwiY/cXkGq1xL4nywb37cVnrxQeSi3+rAix8aHeJwXXeyPZkxmZQe0Hjbw==
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/@miblanchard/react-native-slider/-/react-native-slider-2.3.1.tgz#79e0f1f9b1ce43ef25ee51ee9256c012e5dfa412"
+  integrity sha512-J/hZDBWmXq8fJeOnTVHqIUVDHshqMSpJVxJ4WqwuCBKl5Rke9OBYXIdkSlgi75OgtScAr8FKK5KNkDKHUf6JIg==
 
 "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
   version "5.1.1-v1"
@@ -2540,9 +2553,9 @@
     fastq "^1.6.0"
 
 "@notifee/react-native@^7.4.0":
-  version "7.5.0"
-  resolved "https://registry.yarnpkg.com/@notifee/react-native/-/react-native-7.5.0.tgz#3d846b6a607a6352e798a3440cd3880c005a7d56"
-  integrity sha512-FR1xzQI2KpMyLuM7YZyHHjCcOXbjAcBWMeRlarro8peujr6gJZSg4j2Aw4o8O4ifMHDopFrovwyK1uYBaB9MUg==
+  version "7.6.1"
+  resolved "https://registry.yarnpkg.com/@notifee/react-native/-/react-native-7.6.1.tgz#e215428787396ec57ea424106cc88666f7efe70d"
+  integrity sha512-OjhLPODh6FICYZmF9/0UZbcl2JPaPpcrWi1Cvs/OLFbPSJTIEwPZgXFrCHv/cA3wUX4YQCXreSqQGSVQgvNItQ==
 
 "@npmcli/fs@^1.0.0":
   version "1.1.1"
@@ -2580,17 +2593,17 @@
   resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
   integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
 
-"@react-native-async-storage/async-storage@^1.15.15", "@react-native-async-storage/async-storage@^1.15.17", "@react-native-async-storage/async-storage@^1.17.6":
-  version "1.17.11"
-  resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.17.11.tgz#7ec329c1b9f610e344602e806b04d7c928a2341d"
-  integrity sha512-bzs45n5HNcDq6mxXnSsOHysZWn1SbbebNxldBXCQs8dSvF8Aor9KCdpm+TpnnGweK3R6diqsT8lFhX77VX0NFw==
+"@react-native-async-storage/async-storage@^1.15.15", "@react-native-async-storage/async-storage@^1.17.6":
+  version "1.17.12"
+  resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.17.12.tgz#a39e4df5b06795ce49b2ca5b7ca9b8faadf8e621"
+  integrity sha512-BXg4OxFdjPTRt+8MvN6jz4muq0/2zII3s7HeT/11e4Zeh3WCgk/BleLzUcDfVqF3OzFHUqEkSrb76d6Ndjd/Nw==
   dependencies:
     merge-options "^3.0.4"
 
 "@react-native-camera-roll/camera-roll@^5.2.2":
-  version "5.2.4"
-  resolved "https://registry.yarnpkg.com/@react-native-camera-roll/camera-roll/-/camera-roll-5.2.4.tgz#216d0ea4656b6538c10b60f057118c6f5e704c0d"
-  integrity sha512-pEQDartgO8Nqy6QDm1efvIPpv4q5W+AtTgS05JGK/9x8VzSj8fJ/cvHmMZBlm/4sFpJyvJZ+1KYxKsvFJLbGuQ==
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/@react-native-camera-roll/camera-roll/-/camera-roll-5.3.1.tgz#0b6d363c0f6c83fc93ff033826f8fa96274a01a7"
+  integrity sha512-2XKMkb/pLBC6vYkNh+bJ4UEj49V2ZSyWFHmaxsUJU9beLo1QbM3XJnySV6F1uv7aC+I2RBlDuAusCqNiTQiCOw==
 
 "@react-native-clipboard/clipboard@^1.10.0":
   version "1.11.2"
@@ -2632,12 +2645,12 @@
     serve-static "^1.13.1"
 
 "@react-native-community/cli-doctor@^10.1.1":
-  version "10.2.0"
-  resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-10.2.0.tgz#6050030eea9200ce3c35de360cf8455e126b4d45"
-  integrity sha512-yLxJazUmNSPslHxeeev0gLvsK0nQan8BmGWbtqPz2WwbIbD89vbytC7G96OxiQXr46iWEWAwEJiTTdgA7jlA5Q==
+  version "10.2.1"
+  resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-10.2.1.tgz#b6b7a3f0f9cef1a05f1adc6393eb29c6f8f2972c"
+  integrity sha512-IwhdSD+mtgWdxg2eMr0fpkn08XN7r70DC1riGSmqK/DXNyWBzIZlCkDN+/TwlaUEsiFk6LQTjgCiqZSMpmDrsg==
   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"
@@ -2697,10 +2710,10 @@
     glob "^7.1.3"
     ora "^5.4.1"
 
-"@react-native-community/cli-platform-ios@^10.2.0":
-  version "10.2.0"
-  resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.0.tgz#be21c0e3bbf17358d540cc23e5556bf679f6322e"
-  integrity sha512-hIPK3iL/mL+0ChXmQ9uqqzNOKA48H+TAzg+hrxQLll/6dNMxDeK9/wZpktcsh8w+CyhqzKqVernGcQs7tPeKGw==
+"@react-native-community/cli-platform-ios@^10.2.1":
+  version "10.2.1"
+  resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.1.tgz#2e6bd2cb6d48cbb8720d7b7265bb1bab80745f72"
+  integrity sha512-hz4zu4Y6eyj7D0lnZx8Mf2c2si8y+zh/zUTgCTaPPLzQD8jSZNNBtUUiA1cARm2razpe8marCZ1QbTMAGbf3mg==
   dependencies:
     "@react-native-community/cli-tools" "^10.1.1"
     chalk "^4.1.2"
@@ -2963,15 +2976,14 @@
   integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==
 
 "@segment/analytics-react-native@^2.10.1":
-  version "2.13.1"
-  resolved "https://registry.yarnpkg.com/@segment/analytics-react-native/-/analytics-react-native-2.13.1.tgz#c093c145cb1a5a87041fbbfc5fa61c3fb370787c"
-  integrity sha512-IRg0k8rVTNj2U4jpA0VKGioUKYAbvvYf3FNR8BsHDB3Jutmxdh/SPYAttFU1APJ9mQsqMEIEfocbDtwCAL0kAg==
+  version "2.13.4"
+  resolved "https://registry.yarnpkg.com/@segment/analytics-react-native/-/analytics-react-native-2.13.4.tgz#52216972bf0a1f8722ddf18088340c9d4d90ca5a"
+  integrity sha512-47z2TmODJpeA7Pf1P8kE5dNTiqmxJ7khQ/NgiFR3eoiSy/ir0QOpT49QFrwVMeG35fEl+wDGLXUoYWoAMvBy6w==
   dependencies:
-    "@react-native-async-storage/async-storage" "^1.15.17"
-    "@segment/sovran-react-native" "^0.4.5"
+    "@segment/sovran-react-native" "^1"
     deepmerge "^4.2.2"
     js-base64 "^3.7.2"
-    react-native-uuid "^2.0.1"
+    uuid "^9.0.0"
 
 "@segment/loosely-validate-event@^2.0.0":
   version "2.0.0"
@@ -2991,6 +3003,15 @@
     deepmerge "^4.2.2"
     shell-quote "1.7.3"
 
+"@segment/sovran-react-native@^1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@segment/sovran-react-native/-/sovran-react-native-1.0.1.tgz#4311f0af2e2b606d2c17e535b293c096c6a3c2e8"
+  integrity sha512-7VZrIa7/VP59d4QDvAs0ZOhiadlJ+2YC8K8dKOF0fGwiFC0UmQUZVs4IN9GZfbBavXsagVVMgL2GzjVGLLQdBw==
+  dependencies:
+    ansi-regex "5.0.1"
+    deepmerge "^4.2.2"
+    shell-quote "1.7.3"
+
 "@sideway/address@^4.1.3":
   version "4.1.4"
   resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0"
@@ -3370,9 +3391,9 @@
     "@types/estree" "*"
 
 "@types/eslint@*", "@types/eslint@^7.29.0 || ^8.4.1":
-  version "8.21.1"
-  resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.21.1.tgz#110b441a210d53ab47795124dbc3e9bb993d1e7c"
-  integrity sha512-rc9K8ZpVjNcLs8Fp0dkozd5Pt2Apk1glO4Vgz8ix1u6yFByxfqo5Yavpy65o+93TAe24jr7v+eSBtFLvOQtCRQ==
+  version "8.21.3"
+  resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.21.3.tgz#5794b3911f0f19e34e3a272c49cbdf48d6f543f2"
+  integrity sha512-fa7GkppZVEByMWGbTtE5MbmXWJTVbrjjaS8K6uQj+XtuuUv1fsuPAxhygfqLmsb/Ufb3CV8deFCpiMfAgi00Sw==
   dependencies:
     "@types/estree" "*"
     "@types/json-schema" "*"
@@ -3468,9 +3489,9 @@
     "@types/istanbul-lib-report" "*"
 
 "@types/jest@^29.4.0":
-  version "29.4.0"
-  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.4.0.tgz#a8444ad1704493e84dbf07bb05990b275b3b9206"
-  integrity sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.0.tgz#337b90bbcfe42158f39c2fb5619ad044bbb518ac"
+  integrity sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==
   dependencies:
     expect "^29.0.0"
     pretty-format "^29.0.0"
@@ -3559,9 +3580,9 @@
   integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
 
 "@types/node@*":
-  version "18.14.6"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.6.tgz#ae1973dd2b1eeb1825695bb11ebfb746d27e3e93"
-  integrity sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA==
+  version "18.15.3"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.3.tgz#f0b991c32cfc6a4e7f3399d6cb4b8cf9a0315014"
+  integrity sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==
 
 "@types/object.omit@^3.0.0":
   version "3.0.0"
@@ -3731,94 +3752,94 @@
     "@types/yargs-parser" "*"
 
 "@typescript-eslint/eslint-plugin@^5.30.5", "@typescript-eslint/eslint-plugin@^5.48.2", "@typescript-eslint/eslint-plugin@^5.5.0":
-  version "5.54.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz#0c5091289ce28372e38ab8d28e861d2dbe1ab29e"
-  integrity sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew==
-  dependencies:
-    "@typescript-eslint/scope-manager" "5.54.1"
-    "@typescript-eslint/type-utils" "5.54.1"
-    "@typescript-eslint/utils" "5.54.1"
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz#bc2400c3a23305e8c9a9c04aa40933868aaaeb47"
+  integrity sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==
+  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"
     debug "^4.3.4"
     grapheme-splitter "^1.0.4"
     ignore "^5.2.0"
     natural-compare-lite "^1.4.0"
-    regexpp "^3.2.0"
     semver "^7.3.7"
     tsutils "^3.21.0"
 
 "@typescript-eslint/experimental-utils@^5.0.0":
-  version "5.54.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.54.1.tgz#a45609ce43fc6b24b4c4dde215446eaad7805223"
-  integrity sha512-oqSc2Gr4TL/2M0XRJ9abA1o3Wf1cFJTNqWq0kjdStIIvgMQGZ3TSaFFJ2Cvy3Fgqi9UfDZ8u5idbACssIIyHaw==
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.55.0.tgz#ea2dd8737834af3a36b6a7be5bee57f57160c942"
+  integrity sha512-3ZqXIZhdGyGQAIIGATeMtg7prA6VlyxGtcy5hYIR/3qUqp3t18pWWUYhL9mpsDm7y8F9mr3ISMt83TiqCt7OPQ==
   dependencies:
-    "@typescript-eslint/utils" "5.54.1"
+    "@typescript-eslint/utils" "5.55.0"
 
 "@typescript-eslint/parser@^5.30.5", "@typescript-eslint/parser@^5.48.2", "@typescript-eslint/parser@^5.5.0":
-  version "5.54.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.54.1.tgz#05761d7f777ef1c37c971d3af6631715099b084c"
-  integrity sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg==
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.55.0.tgz#8c96a0b6529708ace1dcfa60f5e6aec0f5ed2262"
+  integrity sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==
   dependencies:
-    "@typescript-eslint/scope-manager" "5.54.1"
-    "@typescript-eslint/types" "5.54.1"
-    "@typescript-eslint/typescript-estree" "5.54.1"
+    "@typescript-eslint/scope-manager" "5.55.0"
+    "@typescript-eslint/types" "5.55.0"
+    "@typescript-eslint/typescript-estree" "5.55.0"
     debug "^4.3.4"
 
-"@typescript-eslint/scope-manager@5.54.1":
-  version "5.54.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz#6d864b4915741c608a58ce9912edf5a02bb58735"
-  integrity sha512-zWKuGliXxvuxyM71UA/EcPxaviw39dB2504LqAmFDjmkpO8qNLHcmzlh6pbHs1h/7YQ9bnsO8CCcYCSA8sykUg==
+"@typescript-eslint/scope-manager@5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz#e863bab4d4183ddce79967fe10ceb6c829791210"
+  integrity sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==
   dependencies:
-    "@typescript-eslint/types" "5.54.1"
-    "@typescript-eslint/visitor-keys" "5.54.1"
+    "@typescript-eslint/types" "5.55.0"
+    "@typescript-eslint/visitor-keys" "5.55.0"
 
-"@typescript-eslint/type-utils@5.54.1":
-  version "5.54.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz#4825918ec27e55da8bb99cd07ec2a8e5f50ab748"
-  integrity sha512-WREHsTz0GqVYLIbzIZYbmUUr95DKEKIXZNH57W3s+4bVnuF1TKe2jH8ZNH8rO1CeMY3U4j4UQeqPNkHMiGem3g==
+"@typescript-eslint/type-utils@5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz#74bf0233523f874738677bb73cb58094210e01e9"
+  integrity sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==
   dependencies:
-    "@typescript-eslint/typescript-estree" "5.54.1"
-    "@typescript-eslint/utils" "5.54.1"
+    "@typescript-eslint/typescript-estree" "5.55.0"
+    "@typescript-eslint/utils" "5.55.0"
     debug "^4.3.4"
     tsutils "^3.21.0"
 
-"@typescript-eslint/types@5.54.1":
-  version "5.54.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.54.1.tgz#29fbac29a716d0f08c62fe5de70c9b6735de215c"
-  integrity sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw==
+"@typescript-eslint/types@5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.55.0.tgz#9830f8d3bcbecf59d12f821e5bc6960baaed41fd"
+  integrity sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==
 
-"@typescript-eslint/typescript-estree@5.54.1":
-  version "5.54.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz#df7b6ae05fd8fef724a87afa7e2f57fa4a599be1"
-  integrity sha512-bjK5t+S6ffHnVwA0qRPTZrxKSaFYocwFIkZx5k7pvWfsB1I57pO/0M0Skatzzw1sCkjJ83AfGTL0oFIFiDX3bg==
+"@typescript-eslint/typescript-estree@5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz#8db7c8e47ecc03d49b05362b8db6f1345ee7b575"
+  integrity sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==
   dependencies:
-    "@typescript-eslint/types" "5.54.1"
-    "@typescript-eslint/visitor-keys" "5.54.1"
+    "@typescript-eslint/types" "5.55.0"
+    "@typescript-eslint/visitor-keys" "5.55.0"
     debug "^4.3.4"
     globby "^11.1.0"
     is-glob "^4.0.3"
     semver "^7.3.7"
     tsutils "^3.21.0"
 
-"@typescript-eslint/utils@5.54.1", "@typescript-eslint/utils@^5.10.0", "@typescript-eslint/utils@^5.43.0":
-  version "5.54.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.54.1.tgz#7a3ee47409285387b9d4609ea7e1020d1797ec34"
-  integrity sha512-IY5dyQM8XD1zfDe5X8jegX6r2EVU5o/WJnLu/znLPWCBF7KNGC+adacXnt5jEYS9JixDcoccI6CvE4RCjHMzCQ==
+"@typescript-eslint/utils@5.55.0", "@typescript-eslint/utils@^5.10.0", "@typescript-eslint/utils@^5.43.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.55.0.tgz#34e97322e7ae5b901e7a870aabb01dad90023341"
+  integrity sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==
   dependencies:
+    "@eslint-community/eslint-utils" "^4.2.0"
     "@types/json-schema" "^7.0.9"
     "@types/semver" "^7.3.12"
-    "@typescript-eslint/scope-manager" "5.54.1"
-    "@typescript-eslint/types" "5.54.1"
-    "@typescript-eslint/typescript-estree" "5.54.1"
+    "@typescript-eslint/scope-manager" "5.55.0"
+    "@typescript-eslint/types" "5.55.0"
+    "@typescript-eslint/typescript-estree" "5.55.0"
     eslint-scope "^5.1.1"
-    eslint-utils "^3.0.0"
     semver "^7.3.7"
 
-"@typescript-eslint/visitor-keys@5.54.1":
-  version "5.54.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz#d7a8a0f7181d6ac748f4d47b2306e0513b98bf8b"
-  integrity sha512-q8iSoHTgwCfgcRJ2l2x+xCbu8nBlRAlsQ33k24Adj8eoVBE0f8dUeI+bAa8F84Mv05UGbAx57g2zrRsYIooqQg==
+"@typescript-eslint/visitor-keys@5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz#01ad414fca8367706d76cdb94adf788dc5b664a2"
+  integrity sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==
   dependencies:
-    "@typescript-eslint/types" "5.54.1"
+    "@typescript-eslint/types" "5.55.0"
     eslint-visitor-keys "^3.3.0"
 
 "@ucans/core@0.11.0":
@@ -3837,9 +3858,9 @@
     wonka "^4.0.14"
 
 "@urql/core@>=2.3.1":
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/@urql/core/-/core-3.1.1.tgz#a49cd572360d01f2469a786b294fba2269a65e53"
-  integrity sha512-Mnxtq4I4QeFJsgs7Iytw+HyhiGxISR6qtyk66c9tipozLZ6QVxrCiUPF2HY4BxNIabaxcp+rivadvm8NAnXj4Q==
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/@urql/core/-/core-3.2.2.tgz#2a44015b536d72981822f715c96393d8e0ddc576"
+  integrity sha512-i046Cz8cZ4xIzGMTyHZrbdgzcFMcKD7+yhCAH5FwWBRjcKrc+RjEOuR9X5AMuBvr8c6IAaE92xAqa4wmlGfWTQ==
   dependencies:
     wonka "^6.1.2"
 
@@ -4002,6 +4023,11 @@
   resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
   integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
 
+"@yarnpkg/lockfile@^1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
+  integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==
+
 "@zxing/text-encoding@^0.9.0":
   version "0.9.0"
   resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b"
@@ -4172,9 +4198,9 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.1:
     type-fest "^0.21.3"
 
 ansi-escapes@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-6.0.0.tgz#68c580e87a489f6df3d761028bb93093fde6bd8a"
-  integrity sha512-IG23inYII3dWlU2EyiAiGj6Bwal5GzsgPMwjYGvc1HPE2dgbj4ZB5ToWBKSquKw74nB3TIuOwaI6/jSULzfgrw==
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-6.1.0.tgz#f2912cdaa10785f3f51f4b562a2497b885aadc5e"
+  integrity sha512-bQyg9bzRntwR/8b89DOEhGwctcwCrbWW/TuqTQnpqpy5Fz3aovcOTj5i8NJV6AHc8OGNdMaqdxAWww8pz2kiKg==
   dependencies:
     type-fest "^3.0.0"
 
@@ -4293,6 +4319,14 @@ arr-union@^3.1.0:
   resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
   integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==
 
+array-buffer-byte-length@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead"
+  integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==
+  dependencies:
+    call-bind "^1.0.2"
+    is-array-buffer "^3.0.1"
+
 array-flatten@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
@@ -4448,12 +4482,12 @@ atomic-sleep@^1.0.0:
   integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==
 
 autoprefixer@^10.4.13:
-  version "10.4.13"
-  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.13.tgz#b5136b59930209a321e9fa3dca2e7c4d223e83a8"
-  integrity sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==
+  version "10.4.14"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d"
+  integrity sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==
   dependencies:
-    browserslist "^4.21.4"
-    caniuse-lite "^1.0.30001426"
+    browserslist "^4.21.5"
+    caniuse-lite "^1.0.30001464"
     fraction.js "^4.2.0"
     normalize-range "^0.1.2"
     picocolors "^1.0.0"
@@ -5185,10 +5219,10 @@ caniuse-api@^3.0.0:
     lodash.memoize "^4.1.2"
     lodash.uniq "^4.5.0"
 
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001426, caniuse-lite@^1.0.30001449:
-  version "1.0.30001462"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001462.tgz#b2e801e37536d453731286857c8520d3dcee15fe"
-  integrity sha512-PDd20WuOBPiasZ7KbFnmQRyuLE7cFXW2PVd7dmALzbkUXEP46upAuCDm9eY9vho8fgNMGmbAX92QBZHzcnWIqw==
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001464:
+  version "1.0.30001468"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001468.tgz#0101837c6a4e38e6331104c33dcfb3bdf367a4b7"
+  integrity sha512-zgAo8D5kbOyUcRAgSmgyuvBkjrGk5CGYG5TYgFdpQv+ywcyEpo1LOWoG8YmoflGnh+V+UsNuKYedsoYs0hzV5A==
 
 case-anything@^2.1.10:
   version "2.1.10"
@@ -5201,9 +5235,9 @@ case-sensitive-paths-webpack-plugin@^2.4.0:
   integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==
 
 cborg@^1.6.0:
-  version "1.10.0"
-  resolved "https://registry.yarnpkg.com/cborg/-/cborg-1.10.0.tgz#0fe157961dd47b537ccb84dc9ba681de8b699013"
-  integrity sha512-/eM0JCaL99HDHxjySNQJLaolZFVdl6VA0/hEKIoiQPcQzE5LrG5QHdml0HaBt31brgB9dNe1zMr3f8IVrpotRQ==
+  version "1.10.1"
+  resolved "https://registry.yarnpkg.com/cborg/-/cborg-1.10.1.tgz#24cfe52c69ec0f66f95e23dc57f2086954c8d718"
+  integrity sha512-et6Qm8MOUY2kCWa5GKk2MlBVoPjHv0hQBmlzI/Z7+5V3VJCeIkGehIB3vWknNsm2kOkAIs6wEKJFJo8luWQQ/w==
 
 chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2:
   version "2.4.2"
@@ -5657,21 +5691,21 @@ copy-webpack-plugin@^10.2.0:
     serialize-javascript "^6.0.0"
 
 core-js-compat@^3.25.1:
-  version "3.29.0"
-  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.0.tgz#1b8d9eb4191ab112022e7f6364b99b65ea52f528"
-  integrity sha512-ScMn3uZNAFhK2DGoEfErguoiAHhV2Ju+oJo/jK08p7B3f3UhocUrCCkTvnZaiS+edl5nlIoiBXKcwMc6elv4KQ==
+  version "3.29.1"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b"
+  integrity sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==
   dependencies:
     browserslist "^4.21.5"
 
 core-js-pure@^3.23.3:
-  version "3.29.0"
-  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.29.0.tgz#0e1ac889214398641ea4bb1c6cf25ff0959ec1d2"
-  integrity sha512-v94gUjN5UTe1n0yN/opTihJ8QBWD2O8i19RfTZR7foONPWArnjB96QA/wk5ozu1mm6ja3udQCzOzwQXTxi3xOQ==
+  version "3.29.1"
+  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.29.1.tgz#1be6ca2b8772f6b4df7fc4621743286e676c6162"
+  integrity sha512-4En6zYVi0i0XlXHVz/bi6l1XDjCqkKRq765NXuX+SnaIatlE96Odt5lMLjdxUiNI1v9OXI5DSLWYPlmTfkTktg==
 
 core-js@^3.19.2:
-  version "3.29.0"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.29.0.tgz#0273e142b67761058bcde5615c503c7406b572d6"
-  integrity sha512-VG23vuEisJNkGl6XQmFJd3rEG/so/CNatqeE+7uZAwTSwFeB/qaO0be8xZYUNWprJ/GIwL8aMt9cj1kvbpTZhg==
+  version "3.29.1"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.29.1.tgz#40ff3b41588b091aaed19ca1aa5cb111803fa9a6"
+  integrity sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==
 
 core-util-is@~1.0.0:
   version "1.0.3"
@@ -6124,9 +6158,9 @@ deepmerge@^3.2.0:
   integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==
 
 deepmerge@^4.2.2:
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b"
-  integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
+  integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
 
 default-gateway@^4.2.0:
   version "4.2.0"
@@ -6548,16 +6582,16 @@ ee-first@1.1.1:
   integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
 
 ejs@^3.1.6:
-  version "3.1.8"
-  resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b"
-  integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==
+  version "3.1.9"
+  resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361"
+  integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==
   dependencies:
     jake "^10.8.5"
 
 electron-to-chromium@^1.4.284:
-  version "1.4.325"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.325.tgz#7b97238a61192d85d055d97f3149832b3617d37b"
-  integrity sha512-K1C03NT4I7BuzsRdCU5RWkgZxtswnKDYM6/eMhkEXqKu4e5T+ck610x3FPzu1y7HVFSiQKZqP16gnJzPpji1TQ==
+  version "1.4.333"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.333.tgz#ebb21f860f8a29923717b06ec0cb54e77ed34c04"
+  integrity sha512-YyE8+GKyGtPEP1/kpvqsdhD6rA/TP1DUFDN4uiU/YI52NzDxmwHkEb3qjId8hLBa5siJvG0sfC3O66501jMruQ==
 
 email-validator@^2.0.4:
   version "2.0.4"
@@ -6667,17 +6701,17 @@ errorhandler@^1.5.0:
     escape-html "~1.0.3"
 
 es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.20.4:
-  version "1.21.1"
-  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6"
-  integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==
+  version "1.21.2"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff"
+  integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==
   dependencies:
+    array-buffer-byte-length "^1.0.0"
     available-typed-arrays "^1.0.5"
     call-bind "^1.0.2"
     es-set-tostringtag "^2.0.1"
     es-to-primitive "^1.2.1"
-    function-bind "^1.1.1"
     function.prototype.name "^1.1.5"
-    get-intrinsic "^1.1.3"
+    get-intrinsic "^1.2.0"
     get-symbol-description "^1.0.0"
     globalthis "^1.0.3"
     gopd "^1.0.1"
@@ -6685,8 +6719,8 @@ es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.20.4:
     has-property-descriptors "^1.0.0"
     has-proto "^1.0.1"
     has-symbols "^1.0.3"
-    internal-slot "^1.0.4"
-    is-array-buffer "^3.0.1"
+    internal-slot "^1.0.5"
+    is-array-buffer "^3.0.2"
     is-callable "^1.2.7"
     is-negative-zero "^2.0.2"
     is-regex "^1.1.4"
@@ -6694,11 +6728,12 @@ es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.20.4:
     is-string "^1.0.7"
     is-typed-array "^1.1.10"
     is-weakref "^1.0.2"
-    object-inspect "^1.12.2"
+    object-inspect "^1.12.3"
     object-keys "^1.1.1"
     object.assign "^4.1.4"
     regexp.prototype.flags "^1.4.3"
     safe-regex-test "^1.0.0"
+    string.prototype.trim "^1.2.7"
     string.prototype.trimend "^1.0.6"
     string.prototype.trimstart "^1.0.6"
     typed-array-length "^1.0.4"
@@ -6990,14 +7025,7 @@ eslint-scope@^7.1.1:
     esrecurse "^4.3.0"
     estraverse "^5.2.0"
 
-eslint-utils@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
-  integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
-  dependencies:
-    eslint-visitor-keys "^2.0.0"
-
-eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0:
+eslint-visitor-keys@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
   integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
@@ -7019,12 +7047,14 @@ eslint-webpack-plugin@^3.1.1:
     schema-utils "^4.0.0"
 
 eslint@^8.19.0, eslint@^8.3.0:
-  version "8.35.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.35.0.tgz#fffad7c7e326bae606f0e8f436a6158566d42323"
-  integrity sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==
-  dependencies:
-    "@eslint/eslintrc" "^2.0.0"
-    "@eslint/js" "8.35.0"
+  version "8.36.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf"
+  integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.2.0"
+    "@eslint-community/regexpp" "^4.4.0"
+    "@eslint/eslintrc" "^2.0.1"
+    "@eslint/js" "8.36.0"
     "@humanwhocodes/config-array" "^0.11.8"
     "@humanwhocodes/module-importer" "^1.0.1"
     "@nodelib/fs.walk" "^1.2.8"
@@ -7035,9 +7065,8 @@ eslint@^8.19.0, eslint@^8.3.0:
     doctrine "^3.0.0"
     escape-string-regexp "^4.0.0"
     eslint-scope "^7.1.1"
-    eslint-utils "^3.0.0"
     eslint-visitor-keys "^3.3.0"
-    espree "^9.4.0"
+    espree "^9.5.0"
     esquery "^1.4.2"
     esutils "^2.0.2"
     fast-deep-equal "^3.1.3"
@@ -7059,15 +7088,14 @@ eslint@^8.19.0, eslint@^8.3.0:
     minimatch "^3.1.2"
     natural-compare "^1.4.0"
     optionator "^0.9.1"
-    regexpp "^3.2.0"
     strip-ansi "^6.0.1"
     strip-json-comments "^3.1.0"
     text-table "^0.2.0"
 
-espree@^9.4.0:
-  version "9.4.1"
-  resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd"
-  integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==
+espree@^9.5.0:
+  version "9.5.0"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113"
+  integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==
   dependencies:
     acorn "^8.8.0"
     acorn-jsx "^5.3.2"
@@ -7324,10 +7352,10 @@ expo-manifests@~0.5.0:
   dependencies:
     expo-json-utils "~0.5.0"
 
-expo-media-library@~15.2.1:
-  version "15.2.2"
-  resolved "https://registry.yarnpkg.com/expo-media-library/-/expo-media-library-15.2.2.tgz#71ece905e425f606422cd16019d78b135040e003"
-  integrity sha512-GebBavV9H+m0Qzoy4G7++BWmwUcddLnCee1qGYkCyHT6CvuLNhXUgC3FV9NINEwlii3HGAuCzk1auaEY60SGDA==
+expo-media-library@~15.2.3:
+  version "15.2.3"
+  resolved "https://registry.yarnpkg.com/expo-media-library/-/expo-media-library-15.2.3.tgz#188f3c77f58b354f0ea6250f6756ac1e1a226291"
+  integrity sha512-Oz8b8Xsvfj7YcutUBtI84NUIqSnt7iCM5HZ5DyKoWKKiDK/+aUuj3RXNQELG8jUw6pQPgEwgbZ1+J8SdH/y9jw==
 
 expo-modules-autolinking@1.1.2:
   version "1.1.2"
@@ -7340,10 +7368,10 @@ expo-modules-autolinking@1.1.2:
     find-up "^5.0.0"
     fs-extra "^9.1.0"
 
-expo-modules-core@1.2.4:
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-1.2.4.tgz#06c0e92b952ae2d3b3329dbeadb6da4f01965d63"
-  integrity sha512-AV0NCTy9O8xQqpKgX6gvsDzV1ogpCzYpGxqM85Vw1xHsOF51s7Avu7NdNjBPUZOVuDderUXAvd97dWrtefSKcA==
+expo-modules-core@1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-1.2.5.tgz#3f9166f4c32c68ab8ef3e120c70ce9890b711650"
+  integrity sha512-5pXNlLHNKLayOusAFMbqr27gjgymHuKuWl/Dtbw2MjoyJY1MZCGD2nIJxd1TTcfnyxNxLg6OQmgkyqoBUFqBuw==
   dependencies:
     compare-versions "^3.4.0"
     invariant "^2.2.4"
@@ -7377,9 +7405,9 @@ expo-updates-interface@~0.9.0:
   integrity sha512-wk88LLhseQ7LJvxdN7BTKiryyqALxnrvr+lyHK3/prg76Yy0EGi2Q/oE/rtFyyZ1JmQDRbO/5pdX0EE6QqVQXQ==
 
 expo@~48.0.0-beta.2:
-  version "48.0.6"
-  resolved "https://registry.yarnpkg.com/expo/-/expo-48.0.6.tgz#374e51a6791bc15e998cd66c335d304ea9c4472a"
-  integrity sha512-ylm91v/xYjBBEqFHH+mpNyGijJgFXx4NwgKgHCIEfcAQyTZLXpGCL6teOVzAmHCCVF7EdalLl3If/+n09jOi4g==
+  version "48.0.7"
+  resolved "https://registry.yarnpkg.com/expo/-/expo-48.0.7.tgz#7900bfda316d25127ed9c412daa31db66dc4a869"
+  integrity sha512-4sPW+HWm03z72FKIG9IddwEhF9+RlAUsTh8pnsoZjZbXALVikmV3QjD4zp/Dkt9YuiCAnJN1VBaT2AlhbYk2Rg==
   dependencies:
     "@babel/runtime" "^7.20.0"
     "@expo/cli" "0.6.2"
@@ -7395,7 +7423,7 @@ expo@~48.0.0-beta.2:
     expo-font "~11.1.1"
     expo-keep-awake "~12.0.1"
     expo-modules-autolinking "1.1.2"
-    expo-modules-core "1.2.4"
+    expo-modules-core "1.2.5"
     fbemitter "^3.0.0"
     getenv "^1.0.0"
     invariant "^2.2.4"
@@ -7742,7 +7770,7 @@ find-up@^5.0.0, find-up@~5.0.0:
     locate-path "^6.0.0"
     path-exists "^4.0.0"
 
-find-yarn-workspace-root@~2.0.0:
+find-yarn-workspace-root@^2.0.0, find-yarn-workspace-root@~2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd"
   integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==
@@ -7768,9 +7796,9 @@ flatted@^3.1.0:
   integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
 
 flow-parser@0.*:
-  version "0.201.0"
-  resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.201.0.tgz#d2005d4dae6fddf60d30f9ae0fb49a13c9c51cfe"
-  integrity sha512-G4oeDNpNGyIrweF9EnoHatncAihMT0tQgV6NMdyM5I7fhrz9Pr13PJ2KLQ673O4wj9KooTdBpeeYHdDNAQoyyw==
+  version "0.202.0"
+  resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.202.0.tgz#534178266d3ceec5368415e59990db97eece5bd0"
+  integrity sha512-ZiXxSIXK3zPmY3zrzCofFonM2T+/3Jz5QZKJyPVtUERQEJUnYkXBQ+0H3FzyqiyJs+VXqb/UNU6/K6sziVYdxw==
 
 flow-parser@^0.185.0:
   version "0.185.2"
@@ -8186,9 +8214,9 @@ gopd@^1.0.1:
     get-intrinsic "^1.1.3"
 
 graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
-  version "4.2.10"
-  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
-  integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
+  version "4.2.11"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+  integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
 
 grapheme-splitter@^1.0.4:
   version "1.0.4"
@@ -8672,7 +8700,7 @@ internal-ip@4.3.0:
     default-gateway "^4.2.0"
     ipaddr.js "^1.9.0"
 
-internal-slot@^1.0.3, internal-slot@^1.0.4:
+internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986"
   integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==
@@ -8735,7 +8763,7 @@ is-arguments@^1.1.1:
     call-bind "^1.0.2"
     has-tostringtag "^1.0.0"
 
-is-array-buffer@^3.0.1:
+is-array-buffer@^3.0.1, is-array-buffer@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe"
   integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==
@@ -8786,6 +8814,13 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
   integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
 
+is-ci@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
+  integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
+  dependencies:
+    ci-info "^2.0.0"
+
 is-core-module@^2.11.0, is-core-module@^2.9.0:
   version "2.11.0"
   resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144"
@@ -10116,9 +10151,9 @@ jimp-compact@0.16.1:
   integrity sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==
 
 joi@^17.2.1:
-  version "17.8.3"
-  resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.3.tgz#d772fe27a87a5cda21aace5cf11eee8671ca7e6f"
-  integrity sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w==
+  version "17.8.4"
+  resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.4.tgz#f2d91ab8acd3cca4079ba70669c65891739234aa"
+  integrity sha512-jjdRHb5WtL+KgSHvOULQEPPv4kcl+ixd1ybOFQq3rWLgEEqc03QMmilodL0GVJE14U/SQDXkUhQUSZANGDH/AA==
   dependencies:
     "@hapi/hoek" "^9.0.0"
     "@hapi/topo" "^5.0.0"
@@ -10435,6 +10470,13 @@ kind-of@^6.0.0, kind-of@^6.0.2:
   resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
   integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
 
+klaw-sync@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c"
+  integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==
+  dependencies:
+    graceful-fs "^4.1.11"
+
 kleur@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
@@ -10450,6 +10492,13 @@ kysely@^0.22.0:
   resolved "https://registry.yarnpkg.com/kysely/-/kysely-0.22.0.tgz#8aac53942da3cadc604d7d154a746d983fe8f7b9"
   integrity sha512-ZE3qWtnqLOalodzfK5QUEcm7AEulhxsPNuKaGFsC3XiqO92vMLm+mAHk/NnbSIOtC4RmGm0nsv700i8KDp1gfQ==
 
+lande@^1.0.10:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/lande/-/lande-1.0.10.tgz#1f6c6542e628338eb18def22edd1038f5fce9e7a"
+  integrity sha512-yT52DQh+UV2pEp08jOYrA4drDv0DbjpiRyZYgl25ak9G2cVR2AimzrqkYQWrD9a7Ud+qkAcaiDDoNH9DXfHPmw==
+  dependencies:
+    toygrad "^2.6.0"
+
 language-subtag-registry@~0.3.2:
   version "0.3.22"
   resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d"
@@ -10462,6 +10511,14 @@ language-tags@=1.0.5:
   dependencies:
     language-subtag-registry "~0.3.2"
 
+launch-editor@^2.6.0:
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.0.tgz#4c0c1a6ac126c572bd9ff9a30da1d2cae66defd7"
+  integrity sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==
+  dependencies:
+    picocolors "^1.0.0"
+    shell-quote "^1.7.3"
+
 leven@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
@@ -11310,9 +11367,9 @@ min-indent@^1.0.0:
   integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
 
 mini-css-extract-plugin@^2.4.5, mini-css-extract-plugin@^2.5.2:
-  version "2.7.3"
-  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.3.tgz#794aa4d598bf178a66b2a35fe287c3df3eac394e"
-  integrity sha512-CD9cXeKeXLcnMw8FZdtfrRrLaM7gwCl4nKuKn2YkY2Bw5wdlB8zU2cCzw+w2zS9RFvbrufTBkMCJACNPwqQA0w==
+  version "2.7.5"
+  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.5.tgz#afbb344977659ec0f1f6e050c7aea456b121cfc5"
+  integrity sha512-9HaR++0mlgom81s95vvNjxkg52n2b5s//3ZTI1EtzFb98awsLSivs2LMsVqnQ3ay0PVhqWcGNyDaTE961FOcjQ==
   dependencies:
     schema-utils "^4.0.0"
 
@@ -11376,9 +11433,9 @@ minipass@^3.0.0, minipass@^3.1.1:
     yallist "^4.0.0"
 
 minipass@^4.0.0:
-  version "4.2.4"
-  resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.4.tgz#7d0d97434b6a19f59c5c3221698b48bbf3b2cd06"
-  integrity sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ==
+  version "4.2.5"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb"
+  integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==
 
 minizlib@^2.1.1:
   version "2.1.2"
@@ -11756,7 +11813,7 @@ object-hash@^3.0.0:
   resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
   integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
 
-object-inspect@^1.12.2, object-inspect@^1.9.0:
+object-inspect@^1.12.3, object-inspect@^1.9.0:
   version "1.12.3"
   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9"
   integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==
@@ -11912,6 +11969,14 @@ open@^6.2.0:
   dependencies:
     is-wsl "^1.1.0"
 
+open@^7.4.2:
+  version "7.4.2"
+  resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321"
+  integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==
+  dependencies:
+    is-docker "^2.0.0"
+    is-wsl "^2.1.1"
+
 open@^8.0.4, open@^8.0.9, open@^8.3.0, open@^8.4.0:
   version "8.4.2"
   resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9"
@@ -12170,6 +12235,26 @@ password-prompt@^1.0.4:
     ansi-escapes "^3.1.0"
     cross-spawn "^6.0.5"
 
+patch-package@^6.5.1:
+  version "6.5.1"
+  resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.5.1.tgz#3e5d00c16997e6160291fee06a521c42ac99b621"
+  integrity sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==
+  dependencies:
+    "@yarnpkg/lockfile" "^1.1.0"
+    chalk "^4.1.2"
+    cross-spawn "^6.0.5"
+    find-yarn-workspace-root "^2.0.0"
+    fs-extra "^9.0.0"
+    is-ci "^2.0.0"
+    klaw-sync "^6.0.0"
+    minimist "^1.2.6"
+    open "^7.4.2"
+    rimraf "^2.6.3"
+    semver "^5.6.0"
+    slash "^2.0.0"
+    tmp "^0.0.33"
+    yaml "^1.10.2"
+
 path-browserify@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
@@ -12981,6 +13066,11 @@ postgres-interval@^1.1.0:
   dependencies:
     xtend "^4.0.0"
 
+postinstall-postinstall@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3"
+  integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==
+
 prebuild-install@^7.1.0, prebuild-install@^7.1.1:
   version "7.1.1"
   resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45"
@@ -13288,9 +13378,9 @@ prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transfor
     prosemirror-model "^1.0.0"
 
 prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.27.0, prosemirror-view@^1.28.2:
-  version "1.30.1"
-  resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.30.1.tgz#7cf0ae8dc8553a02c32961e82eca25079c4d8fc9"
-  integrity sha512-pZUfr7lICJkEY7XwzldAKrkflZDeIvnbfuu2RIS01N5NwJmR/dfZzDzJRzhb3SM2QtT/bM8b4Nnib8X3MGpAhA==
+  version "1.30.2"
+  resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.30.2.tgz#57a9d15c5baa454f0d0f4a3028ddbd9be1e8ed9b"
+  integrity sha512-nTNzZvalQf9kHeEyO407LiV6DoOs/pXsid88UqW9Vvybo4ozJW2PJhkfZUxCUF1hR/9vJLdhxX84wuw9P9HsXA==
   dependencies:
     prosemirror-model "^1.16.0"
     prosemirror-state "^1.0.0"
@@ -13328,9 +13418,9 @@ punycode@^2.1.0, punycode@^2.1.1:
   integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
 
 pure-rand@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.0.tgz#701996ceefa253507923a0e864c17ab421c04a7c"
-  integrity sha512-rLSBxJjP+4DQOgcJAx6RZHT2he2pkhQdSnofG5VWyVl6GRq/K02ISOuOLcsMOrtKDIJb8JN2zm3FFzWNbezdPw==
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.1.tgz#31207dddd15d43f299fdcdb2f572df65030c19af"
+  integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==
 
 q@^1.1.2:
   version "1.5.1"
@@ -13538,9 +13628,9 @@ react-native-appstate-hook@^1.0.6:
   integrity sha512-0hPVyf5yLxCSVrrNEuGqN1ZnSSj3Ye2gZex0NtcK/AHYwMc0rXWFNZjBKOoZSouspqu3hXBbQ6NOUSTzrME1AQ==
 
 react-native-background-fetch@^4.1.8:
-  version "4.1.8"
-  resolved "https://registry.yarnpkg.com/react-native-background-fetch/-/react-native-background-fetch-4.1.8.tgz#a21858e5d876de8d9d15a37f40714b244f73713c"
-  integrity sha512-/qe86laa0n4AbD6mrLL8SCGR+K5693URX95e02/bTJh3UkdS3+sU1Jyc/XTlz4MQwlquI929/lm5EZh8AOUqzQ==
+  version "4.1.9"
+  resolved "https://registry.yarnpkg.com/react-native-background-fetch/-/react-native-background-fetch-4.1.9.tgz#10ebff9ca45a8868f1a72b2aa6cea40d499ece6e"
+  integrity sha512-sk4MCXRhGghBXu9ReabuT8U0WRzjsMt2i/nqCwR9eHi0hux+4kUh5ubpLKLByw5G8WifUv1sp6qsA7uvQERrrQ==
 
 react-native-codegen@^0.71.5:
   version "0.71.5"
@@ -13618,6 +13708,11 @@ react-native-linear-gradient@^2.6.2:
   resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.6.2.tgz#56598a76832724b2afa7889747635b5c80948f38"
   integrity sha512-Z8Xxvupsex+9BBFoSYS87bilNPWcRfRsGC0cpJk72Nxb5p2nEkGSBv73xZbEHnW2mUFvP+huYxrVvjZkr/gRjQ==
 
+react-native-pager-view@6.1.2:
+  version "6.1.2"
+  resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-6.1.2.tgz#3522079b9a9d6634ca5e8d153bc0b4d660254552"
+  integrity sha512-qs2KSFc+7N7B+UZ6SG2sTvCkppagm5fVyRclv1KFKc7lDtrhXLzN59tXJw575LDP/dRJoXsNwqUAhZJdws6ABQ==
+
 react-native-progress@bluesky-social/react-native-progress:
   version "5.0.0"
   resolved "https://codeload.github.com/bluesky-social/react-native-progress/tar.gz/5a372f4f2ce5feb26f4f47b6a4d187ab9b923ab4"
@@ -13660,21 +13755,14 @@ react-native-splash-screen@^3.3.0:
   resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.3.0.tgz#3af71ed17afe50fee69590a45aec399d071ead02"
   integrity sha512-rGjt6HkoSXxMqH4SQUJ1gnPQlPJV8+J47+4yhgTIan4bVvAwJhEeJH7wWt9hXSdH4+VfwTS0GTaflj1Tw83IhA==
 
-react-native-svg@^13.4.0:
-  version "13.8.0"
-  resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-13.8.0.tgz#b6a22cf77f8098f910490a13aeb160a37e182f97"
-  integrity sha512-G8Mx6W86da+vFimZBJvA93POw8yz0fgDS5biy6oIjMWVJVQSDzCyzwO/zY0yuZmCDhKSZzogl5m0wXXvW2OcTA==
+react-native-svg@13.4.0:
+  version "13.4.0"
+  resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-13.4.0.tgz#82399ba0956c454144618aa581e2d748dd3f010a"
+  integrity sha512-B3TwK+H0+JuRhYPzF21AgqMt4fjhCwDZ9QUtwNstT5XcslJBXC0FoTkdZo8IEb1Sv4suSqhZwlAY6lwOv3tHag==
   dependencies:
     css-select "^5.1.0"
     css-tree "^1.1.3"
 
-react-native-tab-view@^3.3.0:
-  version "3.5.1"
-  resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-3.5.1.tgz#2ad454afc0e186b43ea8b89053f39180d480d48b"
-  integrity sha512-qdrS5t+AEhfuKQyuCXkwHu4IVppkuTvzWWlkSZKrPaSkjjIa32xrsGxt1UW9YDdro2w4AMw5hKn1hFmg/5mvzA==
-  dependencies:
-    use-latest-callback "^0.1.5"
-
 react-native-url-polyfill@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz#c1763de0f2a8c22cc3e959b654c8790622b6ef6a"
@@ -13717,10 +13805,10 @@ react-native-web@^0.18.11:
     postcss-value-parser "^4.2.0"
     styleq "^0.1.2"
 
-react-native-webview@^11.26.1:
-  version "11.26.1"
-  resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-11.26.1.tgz#658c09ed5162dc170b361e48c2dd26c9712879da"
-  integrity sha512-hC7BkxOpf+z0UKhxFSFTPAM4shQzYmZHoELa6/8a/MspcjEP7ukYKpuSUTLDywQditT8yI9idfcKvfZDKQExGw==
+react-native-webview@11.26.0:
+  version "11.26.0"
+  resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-11.26.0.tgz#e524992876fe4a79e69905f0fab8949b470e9f16"
+  integrity sha512-4T4CKRm8xlaQDz9h/bCMPGAvtkesrhkRWqCX9FDJEzBToaVUIsV0ZOqtC4w/JSnCtFKKYiaC1ReJtCGv+4mFeQ==
   dependencies:
     escape-string-regexp "2.0.0"
     invariant "2.2.4"
@@ -13882,9 +13970,9 @@ readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@~2.3.6:
     util-deprecate "~1.0.1"
 
 readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
-  version "3.6.1"
-  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.1.tgz#f9f9b5f536920253b3d26e7660e7da4ccff9bb62"
-  integrity sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==
+  version "3.6.2"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
+  integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
   dependencies:
     inherits "^2.0.3"
     string_decoder "^1.1.1"
@@ -14002,15 +14090,10 @@ regexp.prototype.flags@^1.4.3:
     define-properties "^1.1.3"
     functions-have-names "^1.2.2"
 
-regexpp@^3.2.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
-  integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
-
 regexpu-core@^5.3.1:
-  version "5.3.1"
-  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.1.tgz#66900860f88def39a5cb79ebd9490e84f17bcdfb"
-  integrity sha512-nCOzW2V/X15XpLsK2rlgdwrysrBq+AauCn+omItIz4R1pIcmeot5zvjdmOBRLzEH/CkC6IxMJVmxDe3QcMuNVQ==
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b"
+  integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==
   dependencies:
     "@babel/regjsgen" "^0.8.0"
     regenerate "^1.4.2"
@@ -14346,9 +14429,9 @@ safe-regex@^1.1.0:
     ret "~0.1.10"
 
 safe-stable-stringify@^2.3.1, safe-stable-stringify@^2.4.1:
-  version "2.4.2"
-  resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz#ec7b037768098bf65310d1d64370de0dc02353aa"
-  integrity sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==
+  version "2.4.3"
+  resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886"
+  integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==
 
 "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
   version "2.1.2"
@@ -14686,6 +14769,11 @@ sisteransi@^1.0.5:
   resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
   integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
 
+slash@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
+  integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
+
 slash@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
@@ -15023,6 +15111,15 @@ string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.8:
     regexp.prototype.flags "^1.4.3"
     side-channel "^1.0.4"
 
+string.prototype.trim@^1.2.7:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533"
+  integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+
 string.prototype.trimend@^1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533"
@@ -15146,9 +15243,9 @@ structured-headers@^0.4.1:
   integrity sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==
 
 style-loader@^3.3.1:
-  version "3.3.1"
-  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575"
-  integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.2.tgz#eaebca714d9e462c19aa1e3599057bc363924899"
+  integrity sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==
 
 stylehacks@^5.1.1:
   version "5.1.1"
@@ -15428,9 +15525,9 @@ terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.2.5, terser-webpack-plugi
     terser "^5.16.5"
 
 terser@^5.0.0, terser@^5.10.0, terser@^5.15.0, terser@^5.16.5:
-  version "5.16.5"
-  resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.5.tgz#1c285ca0655f467f92af1bbab46ab72d1cb08e5a"
-  integrity sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==
+  version "5.16.6"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.6.tgz#f6c7a14a378ee0630fbe3ac8d1f41b4681109533"
+  integrity sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==
   dependencies:
     "@jridgewell/source-map" "^0.3.2"
     acorn "^8.5.0"
@@ -15513,9 +15610,9 @@ tippy.js@^6.3.7:
     "@popperjs/core" "^2.9.0"
 
 tlds@^1.234.0:
-  version "1.237.0"
-  resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.237.0.tgz#71e5ca558878a046bc9e253db7a1f2b602ce1a2d"
-  integrity sha512-4IA6zR7jQop4pEdziQaptOgkIwnnZ537fXM3MKAzOXjXLjiHm77SA3/E0nXWJGSVRnKcn/JxDJmwTqyPgQ+ozg==
+  version "1.238.0"
+  resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.238.0.tgz#ffe7c19c8940c35b497cda187a6927f9450325a4"
+  integrity sha512-lFPF9pZFhLrPodaJ0wt9QIN0l8jOxqmUezGZnm7BfkDSVd9q667oVIJukLVzhF+4oW7uDlrLlfJrL5yu9RWwew==
 
 tmp@^0.0.33:
   version "0.0.33"
@@ -15589,6 +15686,11 @@ tough-cookie@^4.0.0, tough-cookie@^4.1.2:
     universalify "^0.2.0"
     url-parse "^1.5.3"
 
+toygrad@^2.6.0:
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/toygrad/-/toygrad-2.6.0.tgz#e814bb7da026db8e08dc7da14c7155f49cdb4d54"
+  integrity sha512-g4zBmlSbvzOE5FOILxYkAybTSxijKLkj1WoNqVGnbMcWDyj4wWQ+eYSr3ik7XOpIgMq/7eBcPRTJX3DM2E0YMg==
+
 tr46@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
@@ -16033,6 +16135,11 @@ uuid@^8.0.0, uuid@^8.3.2:
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
   integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
 
+uuid@^9.0.0:
+  version "9.0.0"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
+  integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
+
 v8-to-istanbul@^8.1.0:
   version "8.1.1"
   resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed"
@@ -16194,9 +16301,9 @@ webpack-dev-middleware@^5.3.1:
     schema-utils "^4.0.0"
 
 webpack-dev-server@^4.11.1, webpack-dev-server@^4.6.0:
-  version "4.11.1"
-  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz#ae07f0d71ca0438cf88446f09029b92ce81380b5"
-  integrity sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==
+  version "4.13.1"
+  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.13.1.tgz#6417a9b5d2f528e7644b68d6ed335e392dccffe8"
+  integrity sha512-5tWg00bnWbYgkN+pd5yISQKDejRBYGEw15RaEEslH+zdbNDxxaZvEAO2WulaSaFKb5n3YG8JXsGaDsut1D0xdA==
   dependencies:
     "@types/bonjour" "^3.5.9"
     "@types/connect-history-api-fallback" "^1.3.5"
@@ -16217,6 +16324,7 @@ webpack-dev-server@^4.11.1, webpack-dev-server@^4.6.0:
     html-entities "^2.3.2"
     http-proxy-middleware "^2.0.3"
     ipaddr.js "^2.0.1"
+    launch-editor "^2.6.0"
     open "^8.0.9"
     p-retry "^4.5.0"
     rimraf "^3.0.2"
@@ -16226,7 +16334,7 @@ webpack-dev-server@^4.11.1, webpack-dev-server@^4.6.0:
     sockjs "^0.3.24"
     spdy "^4.0.2"
     webpack-dev-middleware "^5.3.1"
-    ws "^8.4.2"
+    ws "^8.13.0"
 
 webpack-manifest-plugin@^4.0.2, webpack-manifest-plugin@^4.1.1:
   version "4.1.1"
@@ -16266,9 +16374,9 @@ webpack-sources@^3.2.3:
   integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
 
 webpack@^5.64.4, webpack@^5.75.0:
-  version "5.76.0"
-  resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.0.tgz#f9fb9fb8c4a7dbdcd0d56a98e56b8a942ee2692c"
-  integrity sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==
+  version "5.76.2"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.2.tgz#6f80d1c1d1e3bf704db571b2504a0461fac80230"
+  integrity sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==
   dependencies:
     "@types/eslint-scope" "^3.7.3"
     "@types/estree" "^0.0.51"
@@ -16444,9 +16552,9 @@ wonka@^4.0.14:
   integrity sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg==
 
 wonka@^6.1.2:
-  version "6.2.3"
-  resolved "https://registry.yarnpkg.com/wonka/-/wonka-6.2.3.tgz#88f7852a23a3d53bca7411c70d66e9ce8f93a366"
-  integrity sha512-EFOYiqDeYLXSzGYt2X3aVe9Hq1XJG+Hz/HjTRRT4dZE9q95khHl5+7pzUSXI19dbMO1/2UMrTf7JT7/7JrSQSQ==
+  version "6.2.5"
+  resolved "https://registry.yarnpkg.com/wonka/-/wonka-6.2.5.tgz#26e54a6827b96a6164b845106f4d925ede4089bb"
+  integrity sha512-adhGYKm5xWIZYXRkzEqHbRbRl2gXHqOudjQJMXpRgSyboFmaKOjGm3RIThBk4tZdiZx1DXuKK0H9wKBgXHhzZg==
 
 word-wrap@^1.2.3, word-wrap@~1.2.3:
   version "1.2.3"
@@ -16689,10 +16797,10 @@ ws@^7, ws@^7.0.0, ws@^7.4.6, ws@^7.5.1:
   resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
   integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
 
-ws@^8.11.0, ws@^8.4.2:
-  version "8.12.1"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.1.tgz#c51e583d79140b5e42e39be48c934131942d4a8f"
-  integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==
+ws@^8.11.0, ws@^8.13.0:
+  version "8.13.0"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
+  integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
 
 xcode@^3.0.0, xcode@^3.0.1:
   version "3.0.1"