From 477e5f4ecfaa0007aeed90b51274c78a730c1a9e Mon Sep 17 00:00:00 2001 From: hailey Date: Thu, 12 Jun 2025 10:46:22 -0700 Subject: [PATCH 1/7] new arch (#8295) Co-authored-by: Samuel Newman Co-authored-by: Charlotte Som Co-authored-by: Hailey --- __tests__/lib/images.test.ts | 51 +- app.config.js | 2 +- jest/jestSetup.js | 9 +- package.json | 78 +- ...t-native-paste-input+0.8.1.patch.disabled} | 111 ++- ...atch => @sentry+react-native+6.14.0.patch} | 0 ....patch => expo-media-library+17.1.7.patch} | 0 ...12.patch => expo-modules-core+2.4.0.patch} | 0 ...ch.md => expo-modules-core+2.4.0.patch.md} | 0 ....patch => expo-notifications+0.31.3.patch} | 0 ....md => expo-notifications+0.31.3.patch.md} | 0 ...28.12.patch => expo-updates+0.28.14.patch} | 0 ...patch.md => expo-updates+0.28.14.patch.md} | 0 ...0.79.2.patch => react-native+0.79.3.patch} | 49 +- ....patch.md => react-native+0.79.3.patch.md} | 0 ...2.patch => react-native-svg+15.12.0.patch} | 0 src/components/forms/TextField.tsx | 14 +- src/lib/hooks/useHandleRef.ts | 39 - src/lib/media/manip.ts | 87 +- src/screens/Profile/Header/Shell.tsx | 24 +- .../com/composer/text-input/TextInput.tsx | 14 +- src/view/com/pager/Pager.tsx | 222 ++--- src/view/com/pager/Pager.web.tsx | 49 +- src/view/com/pager/PagerWithHeader.tsx | 315 ++++--- src/view/com/profile/ProfileSubpageHeader.tsx | 24 +- src/view/com/util/Toast.web.tsx | 1 - src/view/com/util/images/AutoSizedImage.tsx | 22 +- src/view/com/util/images/Gallery.tsx | 16 +- src/view/com/util/images/ImageLayoutGrid.tsx | 20 +- src/view/com/util/post-embeds/index.tsx | 11 +- yarn.lock | 766 +++++++++--------- 31 files changed, 1013 insertions(+), 911 deletions(-) rename patches/{@mattermost+react-native-paste-input+0.7.1.patch => @mattermost+react-native-paste-input+0.8.1.patch.disabled} (50%) rename patches/{@sentry+react-native+6.10.0.patch => @sentry+react-native+6.14.0.patch} (100%) rename patches/{expo-media-library+17.1.6.patch => expo-media-library+17.1.7.patch} (100%) rename patches/{expo-modules-core+2.3.12.patch => expo-modules-core+2.4.0.patch} (100%) rename patches/{expo-modules-core+2.3.12.patch.md => expo-modules-core+2.4.0.patch.md} (100%) rename patches/{expo-notifications+0.31.1.patch => expo-notifications+0.31.3.patch} (100%) rename patches/{expo-notifications+0.31.1.patch.md => expo-notifications+0.31.3.patch.md} (100%) rename patches/{expo-updates+0.28.12.patch => expo-updates+0.28.14.patch} (100%) rename patches/{expo-updates+0.28.12.patch.md => expo-updates+0.28.14.patch.md} (100%) rename patches/{react-native+0.79.2.patch => react-native+0.79.3.patch} (70%) rename patches/{react-native+0.79.2.patch.md => react-native+0.79.3.patch.md} (100%) rename patches/{react-native-svg+15.11.2.patch => react-native-svg+15.12.0.patch} (100%) delete mode 100644 src/lib/hooks/useHandleRef.ts diff --git a/__tests__/lib/images.test.ts b/__tests__/lib/images.test.ts index a5acad25f6..c7a645d39c 100644 --- a/__tests__/lib/images.test.ts +++ b/__tests__/lib/images.test.ts @@ -1,10 +1,9 @@ -import {deleteAsync} from 'expo-file-system' +import {createDownloadResumable, deleteAsync} from 'expo-file-system' import {manipulateAsync, SaveFormat} from 'expo-image-manipulator' -import RNFetchBlob from 'rn-fetch-blob' import { downloadAndResize, - DownloadAndResizeOpts, + type DownloadAndResizeOpts, getResizedDimensions, } from '../../src/lib/media/manip' @@ -32,11 +31,12 @@ describe('downloadAndResize', () => { }) it('should return resized image for valid URI and options', async () => { - const mockedFetch = RNFetchBlob.fetch as jest.Mock - mockedFetch.mockResolvedValueOnce({ - path: jest.fn().mockReturnValue('file://downloaded-image.jpg'), - info: jest.fn().mockReturnValue({status: 200}), - flush: jest.fn(), + const mockedFetch = createDownloadResumable as jest.Mock + mockedFetch.mockReturnValue({ + cancelAsync: jest.fn(), + downloadAsync: jest + .fn() + .mockResolvedValue({uri: 'file://resized-image.jpg'}), }) const opts: DownloadAndResizeOpts = { @@ -50,13 +50,12 @@ describe('downloadAndResize', () => { const result = await downloadAndResize(opts) expect(result).toEqual(mockResizedImage) - expect(RNFetchBlob.config).toHaveBeenCalledWith({ - fileCache: true, - appendExt: 'jpeg', - }) - expect(RNFetchBlob.fetch).toHaveBeenCalledWith( - 'GET', - 'https://example.com/image.jpg', + expect(createDownloadResumable).toHaveBeenCalledWith( + opts.uri, + expect.anything(), + { + cache: true, + }, ) // First time it gets called is to get dimensions @@ -86,28 +85,6 @@ describe('downloadAndResize', () => { expect(result).toBeUndefined() }) - it('should return undefined for non-200 response', async () => { - const mockedFetch = RNFetchBlob.fetch as jest.Mock - mockedFetch.mockResolvedValueOnce({ - path: jest.fn().mockReturnValue('file://downloaded-image'), - info: jest.fn().mockReturnValue({status: 400}), - flush: jest.fn(), - }) - - const opts: DownloadAndResizeOpts = { - uri: 'https://example.com/image', - width: 100, - height: 100, - maxSize: 500000, - mode: 'cover', - timeout: 10000, - } - - const result = await downloadAndResize(opts) - expect(errorSpy).not.toHaveBeenCalled() - expect(result).toBeUndefined() - }) - it('should not downsize whenever dimensions are below the max dimensions', () => { const initialDimensionsOne = { width: 1200, diff --git a/app.config.js b/app.config.js index 0fa91f2ced..36af084158 100644 --- a/app.config.js +++ b/app.config.js @@ -219,7 +219,7 @@ module.exports = function (_config) { compileSdkVersion: 35, targetSdkVersion: 35, buildToolsVersion: '35.0.0', - newArchEnabled: false, + newArchEnabled: true, }, }, ], diff --git a/jest/jestSetup.js b/jest/jestSetup.js index d303225f6c..700d20afc0 100644 --- a/jest/jestSetup.js +++ b/jest/jestSetup.js @@ -33,15 +33,10 @@ jest.mock('react-native-safe-area-context', () => { } }) -jest.mock('rn-fetch-blob', () => ({ - config: jest.fn().mockReturnThis(), - cancel: jest.fn(), - fetch: jest.fn(), -})) - jest.mock('expo-file-system', () => ({ getInfoAsync: jest.fn().mockResolvedValue({exists: true, size: 100}), deleteAsync: jest.fn(), + createDownloadResumable: jest.fn(), })) jest.mock('expo-image-manipulator', () => ({ @@ -101,7 +96,7 @@ jest.mock('expo-modules-core', () => ({ } } }), - requireNativeViewManager: jest.fn().mockImplementation(moduleName => { + requireNativeViewManager: jest.fn().mockImplementation(_ => { return () => null }), })) diff --git a/package.json b/package.json index f0bc5bbe75..38f574f42f 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "@braintree/sanitize-url": "^6.0.2", "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet", "@emoji-mart/react": "^1.1.1", - "@expo/html-elements": "^0.12.4", + "@expo/html-elements": "^0.12.5", "@expo/webpack-config": "^19.0.1", "@floating-ui/dom": "^1.6.3", "@floating-ui/react-dom": "^2.0.8", @@ -85,12 +85,12 @@ "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", "@fortawesome/react-native-fontawesome": "^0.3.2", - "@haileyok/bluesky-video": "0.2.6", + "@haileyok/bluesky-video": "0.3.1", "@ipld/dag-cbor": "^9.2.0", "@lingui/react": "^4.14.1", - "@mattermost/react-native-paste-input": "^0.7.1", - "@miblanchard/react-native-slider": "^2.3.1", - "@mozzius/expo-dynamic-app-icon": "^1.5.0", + "@mattermost/react-native-paste-input": "mattermost/react-native-paste-input", + "@miblanchard/react-native-slider": "^2.6.0", + "@mozzius/expo-dynamic-app-icon": "1.5.0", "@react-native-async-storage/async-storage": "2.1.2", "@react-native-menu/menu": "^1.2.3", "@react-native-picker/picker": "2.11.0", @@ -98,7 +98,7 @@ "@react-navigation/drawer": "^7.3.12", "@react-navigation/native": "^7.1.9", "@react-navigation/native-stack": "^7.3.13", - "@sentry/react-native": "~6.10.0", + "@sentry/react-native": "~6.14.0", "@tanstack/query-async-storage-persister": "^5.25.0", "@tanstack/react-query": "^5.8.1", "@tanstack/react-query-persist-client": "^5.25.0", @@ -130,33 +130,33 @@ "emoji-mart": "^5.5.2", "emoji-regex": "^10.4.0", "eventemitter3": "^5.0.1", - "expo": "^53.0.5", + "expo": "53.0.11", "expo-application": "~6.1.4", - "expo-blur": "~14.1.4", + "expo-blur": "~14.1.5", "expo-build-properties": "~0.14.6", - "expo-camera": "~16.1.6", + "expo-camera": "~16.1.8", "expo-clipboard": "~7.1.4", - "expo-dev-client": "~5.1.7", + "expo-dev-client": "~5.2.0", "expo-device": "~7.1.4", - "expo-file-system": "~18.1.8", - "expo-font": "~13.3.0", + "expo-file-system": "~18.1.10", + "expo-font": "~13.3.1", "expo-haptics": "~14.1.4", - "expo-image": "~2.1.6", + "expo-image": "~2.2.1", "expo-image-crop-tool": "^0.1.8", - "expo-image-manipulator": "~13.1.5", + "expo-image-manipulator": "~13.1.7", "expo-image-picker": "~16.1.4", - "expo-linear-gradient": "~14.1.4", - "expo-linking": "~7.1.4", + "expo-linear-gradient": "~14.1.5", + "expo-linking": "~7.1.5", "expo-localization": "~16.1.5", - "expo-media-library": "~17.1.6", - "expo-notifications": "~0.31.1", - "expo-screen-orientation": "~8.1.5", + "expo-media-library": "~17.1.7", + "expo-notifications": "~0.31.3", + "expo-screen-orientation": "~8.1.7", "expo-sharing": "~13.1.5", - "expo-splash-screen": "~0.30.8", - "expo-system-ui": "~5.0.7", + "expo-splash-screen": "~0.30.9", + "expo-system-ui": "~5.0.8", "expo-task-manager": "~13.1.5", - "expo-updates": "~0.28.12", - "expo-video": "~2.1.8", + "expo-updates": "~0.28.14", + "expo-video": "~2.2.1", "expo-web-browser": "~14.1.6", "fast-text-encoding": "^1.0.6", "history": "^5.3.0", @@ -182,35 +182,34 @@ "react-image-crop": "^11.0.7", "react-is": "19", "react-keyed-flatten-children": "^5.0.0", - "react-native": "0.79.2", - "react-native-compressor": "1.11.0", + "react-native": "^0.79.3", + "react-native-compressor": "^1.11.0", "react-native-date-picker": "^5.0.12", - "react-native-drawer-layout": "^4.1.6", + "react-native-drawer-layout": "^4.1.8", "react-native-edge-to-edge": "^1.6.0", "react-native-gesture-handler": "2.25.0", "react-native-get-random-values": "~1.11.0", "react-native-ios-context-menu": "^1.15.3", "react-native-keyboard-controller": "^1.17.1", "react-native-mmkv": "^2.12.2", - "react-native-pager-view": "6.7.1", + "react-native-pager-view": "^6.7.1", "react-native-progress": "bluesky-social/react-native-progress", "react-native-qrcode-styled": "^0.3.3", "react-native-reanimated": "~3.17.5", - "react-native-root-siblings": "^4.1.1", + "react-native-root-siblings": "^5.0.1", "react-native-safe-area-context": "5.4.0", "react-native-screens": "^4.11.1", - "react-native-svg": "15.11.2", + "react-native-svg": "15.12.0", "react-native-uitextview": "^1.4.0", "react-native-url-polyfill": "^1.3.0", "react-native-uuid": "^2.0.3", "react-native-view-shot": "^4.0.3", "react-native-web": "~0.20.0", "react-native-web-webview": "^1.0.2", - "react-native-webview": "13.13.5", + "react-native-webview": "^13.13.5", "react-remove-scroll-bar": "^2.3.8", "react-responsive": "^9.0.2", "react-textarea-autosize": "^8.5.3", - "rn-fetch-blob": "^0.12.0", "statsig-react-native-expo": "^4.6.1", "tippy.js": "^6.3.7", "tlds": "^1.234.0", @@ -227,8 +226,9 @@ "@lingui/cli": "^4.14.1", "@lingui/macro": "^4.14.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", - "@react-native/eslint-config": "^0.79.2", - "@react-native/typescript-config": "^0.79.2", + "@react-native/babel-preset": "0.79.3", + "@react-native/eslint-config": "^0.79.3", + "@react-native/typescript-config": "^0.79.3", "@sentry/webpack-plugin": "^3.2.2", "@testing-library/jest-native": "^5.4.3", "@testing-library/react-native": "^13.2.0", @@ -247,6 +247,7 @@ "babel-plugin-module-resolver": "^5.0.2", "babel-plugin-react-compiler": "^19.1.0-rc.1", "babel-preset-expo": "~13.1.11", + "browserslist": "^4.25.0", "eslint": "^8.19.0", "eslint-plugin-bsky-internal": "link:./eslint", "eslint-plugin-ft-flow": "^2.0.3", @@ -260,7 +261,7 @@ "husky": "^8.0.3", "is-ci": "^3.0.1", "jest": "^29.7.0", - "jest-expo": "~53.0.3", + "jest-expo": "~53.0.7", "jest-junit": "^16.0.0", "lint-staged": "^13.2.3", "lockfile-lint": "^4.14.0", @@ -275,11 +276,11 @@ }, "resolutions": { "@expo/image-utils": "0.6.3", - "@react-native/babel-preset": "0.79.2", - "@react-native/normalize-colors": "0.79.2", + "@react-native/babel-preset": "0.79.3", + "@react-native/normalize-colors": "0.79.3", "@types/react": "^18", "**/expo-constants": "17.0.3", - "**/expo-device": "7.0.1", + "**/expo-device": "7.1.4", "**/zod": "3.23.8", "**/multiformats": "9.9.0" }, @@ -359,7 +360,8 @@ ], "allowedUrls": [ "https://codeload.github.com/bluesky-social/react-native-bottom-sheet/tar.gz/28a87d1bb55e10fc355fa1455545a30734995908", - "https://codeload.github.com/bluesky-social/react-native-progress/tar.gz/5a372f4f2ce5feb26f4f47b6a4d187ab9b923ab4" + "https://codeload.github.com/bluesky-social/react-native-progress/tar.gz/5a372f4f2ce5feb26f4f47b6a4d187ab9b923ab4", + "https://codeload.github.com/mattermost/react-native-paste-input/tar.gz/f260447edc645a817ab1ba7b46d8341d84dba8e9" ], "emptyHostname": false, "validatePackageNames": true, diff --git a/patches/@mattermost+react-native-paste-input+0.7.1.patch b/patches/@mattermost+react-native-paste-input+0.8.1.patch.disabled similarity index 50% rename from patches/@mattermost+react-native-paste-input+0.7.1.patch rename to patches/@mattermost+react-native-paste-input+0.8.1.patch.disabled index f25b6a776e..a7f1461432 100644 --- a/patches/@mattermost+react-native-paste-input+0.7.1.patch +++ b/patches/@mattermost+react-native-paste-input+0.8.1.patch.disabled @@ -114,7 +114,7 @@ index e916023..5049c33 100644 } #pragma mark - UIScrollViewDelegate -@@ -62,7 +92,6 @@ +@@ -62,7 +92,6 @@ - (void)setSmartPunctuation:(NSString *)smartPunctuation { - (void)scrollViewDidScroll:(UIScrollView *)scrollView { RCTDirectEventBlock onScroll = self.onScroll; @@ -122,7 +122,7 @@ index e916023..5049c33 100644 if (onScroll) { CGPoint contentOffset = scrollView.contentOffset; CGSize contentSize = scrollView.contentSize; -@@ -71,22 +100,22 @@ +@@ -71,22 +100,22 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView onScroll(@{ @"contentOffset": @{ @@ -155,3 +155,110 @@ index e916023..5049c33 100644 }, @"zoomScale": @(scrollView.zoomScale ?: 1), }); +diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInput.mm b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInput.mm +index dd50053..2ed7017 100644 +--- a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInput.mm ++++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInput.mm +@@ -122,8 +122,8 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & + const auto &newTextInputProps = static_cast(*props); + + // Traits: +- if (newTextInputProps.traits.multiline != oldTextInputProps.traits.multiline) { +- [self _setMultiline:newTextInputProps.traits.multiline]; ++ if (newTextInputProps.multiline != oldTextInputProps.multiline) { ++ [self _setMultiline:newTextInputProps.multiline]; + } + + if (newTextInputProps.traits.autocapitalizationType != oldTextInputProps.traits.autocapitalizationType) { +@@ -421,7 +421,7 @@ - (void)textInputDidChangeSelection + return; + } + const auto &props = static_cast(*_props); +- if (props.traits.multiline && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) { ++ if (props.multiline && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) { + [self textInputDidChange]; + _ignoreNextTextInputCall = YES; + } +@@ -708,11 +708,11 @@ - (BOOL)_textOf:(NSAttributedString *)newText equals:(NSAttributedString *)oldTe + - (SubmitBehavior)getSubmitBehavior + { + const auto &props = static_cast(*_props); +- const SubmitBehavior submitBehaviorDefaultable = props.traits.submitBehavior; ++ const SubmitBehavior submitBehaviorDefaultable = props.submitBehavior; + + // We should always have a non-default `submitBehavior`, but in case we don't, set it based on multiline. + if (submitBehaviorDefaultable == SubmitBehavior::Default) { +- return props.traits.multiline ? SubmitBehavior::Newline : SubmitBehavior::BlurAndSubmit; ++ return props.multiline ? SubmitBehavior::Newline : SubmitBehavior::BlurAndSubmit; + } + + return submitBehaviorDefaultable; +diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.cpp b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.cpp +index 29e094f..7ef519a 100644 +--- a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.cpp ++++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.cpp +@@ -22,8 +22,7 @@ PasteTextInputProps::PasteTextInputProps( + const PropsParserContext &context, + const PasteTextInputProps &sourceProps, + const RawProps& rawProps) +- : ViewProps(context, sourceProps, rawProps), +- BaseTextProps(context, sourceProps, rawProps), ++ : BaseTextInputProps(context, sourceProps, rawProps), + traits(convertRawProp(context, rawProps, sourceProps.traits, {})), + smartPunctuation(convertRawProp(context, rawProps, "smartPunctuation", sourceProps.smartPunctuation, {})), + disableCopyPaste(convertRawProp(context, rawProps, "disableCopyPaste", sourceProps.disableCopyPaste, {false})), +@@ -133,7 +132,7 @@ TextAttributes PasteTextInputProps::getEffectiveTextAttributes(Float fontSizeMul + ParagraphAttributes PasteTextInputProps::getEffectiveParagraphAttributes() const { + auto result = paragraphAttributes; + +- if (!traits.multiline) { ++ if (!multiline) { + result.maximumNumberOfLines = 1; + } + +diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.h b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.h +index 723d00c..31cfe66 100644 +--- a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.h ++++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.h +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -25,7 +26,7 @@ + + namespace facebook::react { + +-class PasteTextInputProps final : public ViewProps, public BaseTextProps { ++class PasteTextInputProps final : public BaseTextInputProps { + public: + PasteTextInputProps() = default; + PasteTextInputProps(const PropsParserContext& context, const PasteTextInputProps& sourceProps, const RawProps& rawProps); +diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/ShadowNodes.cpp b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/ShadowNodes.cpp +index 31e07e3..7f0ebfb 100644 +--- a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/ShadowNodes.cpp ++++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/ShadowNodes.cpp +@@ -91,20 +91,11 @@ void PasteTextInputShadowNode::updateStateIfNeeded( + const auto& state = getStateData(); + + react_native_assert(textLayoutManager_); +- react_native_assert( +- (!state.layoutManager || state.layoutManager == textLayoutManager_) && +- "`StateData` refers to a different `TextLayoutManager`"); +- +- if (state.reactTreeAttributedString == reactTreeAttributedString && +- state.layoutManager == textLayoutManager_) { +- return; +- } + + auto newState = TextInputState{}; + newState.attributedStringBox = AttributedStringBox{reactTreeAttributedString}; + newState.paragraphAttributes = getConcreteProps().paragraphAttributes; + newState.reactTreeAttributedString = reactTreeAttributedString; +- newState.layoutManager = textLayoutManager_; + newState.mostRecentEventCount = getConcreteProps().mostRecentEventCount; + setStateData(std::move(newState)); + } diff --git a/patches/@sentry+react-native+6.10.0.patch b/patches/@sentry+react-native+6.14.0.patch similarity index 100% rename from patches/@sentry+react-native+6.10.0.patch rename to patches/@sentry+react-native+6.14.0.patch diff --git a/patches/expo-media-library+17.1.6.patch b/patches/expo-media-library+17.1.7.patch similarity index 100% rename from patches/expo-media-library+17.1.6.patch rename to patches/expo-media-library+17.1.7.patch diff --git a/patches/expo-modules-core+2.3.12.patch b/patches/expo-modules-core+2.4.0.patch similarity index 100% rename from patches/expo-modules-core+2.3.12.patch rename to patches/expo-modules-core+2.4.0.patch diff --git a/patches/expo-modules-core+2.3.12.patch.md b/patches/expo-modules-core+2.4.0.patch.md similarity index 100% rename from patches/expo-modules-core+2.3.12.patch.md rename to patches/expo-modules-core+2.4.0.patch.md diff --git a/patches/expo-notifications+0.31.1.patch b/patches/expo-notifications+0.31.3.patch similarity index 100% rename from patches/expo-notifications+0.31.1.patch rename to patches/expo-notifications+0.31.3.patch diff --git a/patches/expo-notifications+0.31.1.patch.md b/patches/expo-notifications+0.31.3.patch.md similarity index 100% rename from patches/expo-notifications+0.31.1.patch.md rename to patches/expo-notifications+0.31.3.patch.md diff --git a/patches/expo-updates+0.28.12.patch b/patches/expo-updates+0.28.14.patch similarity index 100% rename from patches/expo-updates+0.28.12.patch rename to patches/expo-updates+0.28.14.patch diff --git a/patches/expo-updates+0.28.12.patch.md b/patches/expo-updates+0.28.14.patch.md similarity index 100% rename from patches/expo-updates+0.28.12.patch.md rename to patches/expo-updates+0.28.14.patch.md diff --git a/patches/react-native+0.79.2.patch b/patches/react-native+0.79.3.patch similarity index 70% rename from patches/react-native+0.79.2.patch rename to patches/react-native+0.79.3.patch index 609ae66178..6d465475eb 100644 --- a/patches/react-native+0.79.2.patch +++ b/patches/react-native+0.79.3.patch @@ -1,3 +1,32 @@ +diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h +index 914a249..0deac55 100644 +--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h ++++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h +@@ -19,6 +19,8 @@ NS_ASSUME_NONNULL_BEGIN + */ + @interface RCTPullToRefreshViewComponentView : RCTViewComponentView + ++- (void)beginRefreshingProgrammatically; ++ + @end + + NS_ASSUME_NONNULL_END +diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +index d029337..0f63ea3 100644 +--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm ++++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +@@ -1003,6 +1003,11 @@ - (void)_adjustForMaintainVisibleContentPosition + } + } + +++ (BOOL)shouldBeRecycled ++{ ++ return NO; ++} ++ + @end + + Class RCTScrollViewCls(void) diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h index e9b330f..ec5f58c 100644 --- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h @@ -15,7 +44,7 @@ diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshCont index 53bfd04..ff1b1ed 100644 --- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m +++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m -@@ -23,6 +23,7 @@ +@@ -23,6 +23,7 @@ @implementation RCTRefreshControl { UIColor *_titleColor; CGFloat _progressViewOffset; BOOL _hasMovedToWindow; @@ -23,7 +52,7 @@ index 53bfd04..ff1b1ed 100644 } - (instancetype)init -@@ -58,6 +59,12 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder) +@@ -58,6 +59,12 @@ - (void)layoutSubviews _isInitialRender = false; } @@ -36,7 +65,7 @@ index 53bfd04..ff1b1ed 100644 - (void)didMoveToWindow { [super didMoveToWindow]; -@@ -221,4 +228,50 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder) +@@ -221,4 +228,50 @@ - (void)refreshControlValueChanged } } @@ -91,7 +120,7 @@ diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshCont index 40aaf9c..1c60164 100644 --- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m +++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m -@@ -22,11 +22,12 @@ RCT_EXPORT_MODULE() +@@ -22,11 +22,12 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(onRefresh, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(refreshing, BOOL) @@ -105,15 +134,3 @@ index 40aaf9c..1c60164 100644 RCT_EXPORT_METHOD(setNativeRefreshing : (nonnull NSNumber *)viewTag toRefreshing : (BOOL)refreshing) { [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { -diff --git a/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m b/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m -index cd1e7eb..c1d0172 100644 ---- a/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m -+++ b/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m -@@ -83,6 +83,7 @@ RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL) - RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval) - RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat) - RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets) -+RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets) - RCT_EXPORT_VIEW_PROPERTY(verticalScrollIndicatorInsets, UIEdgeInsets) - RCT_EXPORT_VIEW_PROPERTY(scrollToOverflowEnabled, BOOL) - RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int) diff --git a/patches/react-native+0.79.2.patch.md b/patches/react-native+0.79.3.patch.md similarity index 100% rename from patches/react-native+0.79.2.patch.md rename to patches/react-native+0.79.3.patch.md diff --git a/patches/react-native-svg+15.11.2.patch b/patches/react-native-svg+15.12.0.patch similarity index 100% rename from patches/react-native-svg+15.11.2.patch rename to patches/react-native-svg+15.12.0.patch diff --git a/src/components/forms/TextField.tsx b/src/components/forms/TextField.tsx index e5fb338497..ea7f8e94ef 100644 --- a/src/components/forms/TextField.tsx +++ b/src/components/forms/TextField.tsx @@ -1,12 +1,12 @@ import React from 'react' import { - AccessibilityProps, + type AccessibilityProps, StyleSheet, TextInput, - TextInputProps, - TextStyle, + type TextInputProps, + type TextStyle, View, - ViewStyle, + type ViewStyle, } from 'react-native' import {HITSLOP_20} from '#/lib/constants' @@ -16,13 +16,13 @@ import { applyFonts, atoms as a, ios, - TextStyleProp, + type TextStyleProp, useAlf, useTheme, web, } from '#/alf' import {useInteractionState} from '#/components/hooks/useInteractionState' -import {Props as SVGIconProps} from '#/components/icons/common' +import {type Props as SVGIconProps} from '#/components/icons/common' import {Text} from '#/components/Typography' const Context = React.createContext<{ @@ -196,7 +196,7 @@ export function createInput(Component: typeof TextInput) { minWidth: 0, }, ios({paddingTop: 12, paddingBottom: 13}), - android(a.py_sm), + android(a.py_md), // fix for autofill styles covering border web({ paddingTop: 10, diff --git a/src/lib/hooks/useHandleRef.ts b/src/lib/hooks/useHandleRef.ts deleted file mode 100644 index 167ba270b6..0000000000 --- a/src/lib/hooks/useHandleRef.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {useState} from 'react' -import {AnimatedRef, measure, MeasuredDimensions} from 'react-native-reanimated' - -export type HandleRef = { - (node: any): void - current: null | number -} - -// This is a lighterweight alternative to `useAnimatedRef()` for imperative UI thread actions. -// Render it like , then pass `ref.current` to `measureHandle()` and such. -export function useHandleRef(): HandleRef { - return useState(() => { - const ref = (node: any) => { - if (node) { - ref.current = - node._nativeTag ?? - node.__nativeTag ?? - node.canonical?.nativeTag ?? - null - } else { - ref.current = null - } - } - ref.current = null - return ref - })[0] as HandleRef -} - -// When using this version, you need to read ref.current on the JS thread, and pass it to UI. -export function measureHandle( - current: number | null, -): MeasuredDimensions | null { - 'worklet' - if (current !== null) { - return measure((() => current) as AnimatedRef) - } else { - return null - } -} diff --git a/src/lib/media/manip.ts b/src/lib/media/manip.ts index 62cbc55ac5..c7a429a243 100644 --- a/src/lib/media/manip.ts +++ b/src/lib/media/manip.ts @@ -1,8 +1,9 @@ -import {Image as RNImage, Share as RNShare} from 'react-native' +import {Image as RNImage} from 'react-native' import uuid from 'react-native-uuid' import { cacheDirectory, copyAsync, + createDownloadResumable, deleteAsync, EncodingType, getInfoAsync, @@ -14,7 +15,6 @@ import {manipulateAsync, SaveFormat} from 'expo-image-manipulator' import * as MediaLibrary from 'expo-media-library' import * as Sharing from 'expo-sharing' import {Buffer} from 'buffer' -import RNFetchBlob from 'rn-fetch-blob' import {POST_IMG_MAX} from '#/lib/constants' import {logger} from '#/logger' @@ -68,28 +68,13 @@ export async function downloadAndResize(opts: DownloadAndResizeOpts) { return } - let downloadRes + const path = createPath(appendExt) + try { - const downloadResPromise = RNFetchBlob.config({ - fileCache: true, - appendExt, - }).fetch('GET', opts.uri) - const to1 = setTimeout(() => downloadResPromise.cancel(), opts.timeout) - downloadRes = await downloadResPromise - clearTimeout(to1) - - const status = downloadRes.info().status - if (status !== 200) { - return - } - - const localUri = normalizePath(downloadRes.path(), true) - return await doResize(localUri, opts) + await downloadImage(opts.uri, path, opts.timeout) + return await doResize(path, opts) } finally { - // TODO Whenever we remove `rn-fetch-blob`, we will need to replace this `flush()` with a `deleteAsync()` -hailey - if (downloadRes) { - downloadRes.flush() - } + safeDeleteAsync(path) } } @@ -98,32 +83,16 @@ export async function shareImageModal({uri}: {uri: string}) { // TODO might need to give an error to the user in this case -prf return } - const downloadResponse = await RNFetchBlob.config({ - fileCache: true, - }).fetch('GET', uri) - // NOTE - // assuming PNG // we're currently relying on the fact our CDN only serves pngs // -prf - - let imagePath = downloadResponse.path() - imagePath = normalizePath(await moveToPermanentPath(imagePath, '.png'), true) - - // NOTE - // for some reason expo-sharing refuses to work on iOS - // ...and visa versa - // -prf - if (isIOS) { - await RNShare.share({url: imagePath}) - } else { - await Sharing.shareAsync(imagePath, { - mimeType: 'image/png', - UTI: 'image/png', - }) - } - - safeDeleteAsync(imagePath) + const imageUri = await downloadImage(uri, createPath('png'), 5e3) + const imagePath = await moveToPermanentPath(imageUri, '.png') + safeDeleteAsync(imageUri) + await Sharing.shareAsync(imagePath, { + mimeType: 'image/png', + UTI: 'image/png', + }) } const ALBUM_NAME = 'Bluesky' @@ -134,11 +103,8 @@ export async function saveImageToMediaLibrary({uri}: {uri: string}) { // assuming PNG // we're currently relying on the fact our CDN only serves pngs // -prf - const downloadResponse = await RNFetchBlob.config({ - fileCache: true, - }).fetch('GET', uri) - let imagePath = downloadResponse.path() - imagePath = normalizePath(await moveToPermanentPath(imagePath, '.png'), true) + const imageUri = await downloadImage(uri, createPath('png'), 5e3) + const imagePath = await moveToPermanentPath(imageUri, '.png') // save try { @@ -403,3 +369,24 @@ export function getResizedDimensions(originalDims: { height: Math.round(originalDims.height * ratio), } } + +function createPath(ext: string) { + // cacheDirectory will never be null on native, so the null check here is not necessary except for typescript. + // we use a web-only function for downloadAndResize on web + return `${cacheDirectory ?? ''}/${uuid.v4()}.${ext}` +} + +async function downloadImage(uri: string, path: string, timeout: number) { + const dlResumable = createDownloadResumable(uri, path, {cache: true}) + + const to1 = setTimeout(() => dlResumable.cancelAsync(), timeout) + + const dlRes = await dlResumable.downloadAsync() + clearTimeout(to1) + + if (!dlRes?.uri) { + throw new Error('Failed to download image - dlRes is undefined') + } + + return normalizePath(dlRes.uri) +} diff --git a/src/screens/Profile/Header/Shell.tsx b/src/screens/Profile/Header/Shell.tsx index 9777c8cc78..53585c0947 100644 --- a/src/screens/Profile/Header/Shell.tsx +++ b/src/screens/Profile/Header/Shell.tsx @@ -1,9 +1,11 @@ -import React, {memo, useEffect} from 'react' +import {memo, useCallback, useEffect, useMemo} from 'react' import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native' -import { +import Animated, { + measure, type MeasuredDimensions, runOnJS, runOnUI, + useAnimatedRef, } from 'react-native-reanimated' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {type AppBskyActorDefs, type ModerationDecision} from '@atproto/api' @@ -14,7 +16,6 @@ import {useNavigation} from '@react-navigation/native' import {useActorStatus} from '#/lib/actor-status' import {BACK_HITSLOP} from '#/lib/constants' import {useHaptics} from '#/lib/haptics' -import {measureHandle, useHandleRef} from '#/lib/hooks/useHandleRef' import {type NavigationProp} from '#/lib/routes/types' import {logger} from '#/logger' import {isIOS} from '#/platform/detection' @@ -59,9 +60,9 @@ let ProfileHeaderShell = ({ const playHaptic = useHaptics() const liveStatusControl = useDialogControl() - const aviRef = useHandleRef() + const aviRef = useAnimatedRef() - const onPressBack = React.useCallback(() => { + const onPressBack = useCallback(() => { if (navigation.canGoBack()) { navigation.goBack() } else { @@ -69,7 +70,7 @@ let ProfileHeaderShell = ({ } }, [navigation]) - const _openLightbox = React.useCallback( + const _openLightbox = useCallback( (uri: string, thumbRect: MeasuredDimensions | null) => { openLightbox({ images: [ @@ -92,7 +93,7 @@ let ProfileHeaderShell = ({ [openLightbox], ) - const isMe = React.useMemo( + const isMe = useMemo( () => currentAccount?.did === profile.did, [currentAccount, profile], ) @@ -109,7 +110,7 @@ let ProfileHeaderShell = ({ } }, [live.isActive, profile.did]) - const onPressAvi = React.useCallback(() => { + const onPressAvi = useCallback(() => { if (live.isActive) { playHaptic('Light') logger.metric( @@ -122,10 +123,9 @@ let ProfileHeaderShell = ({ const modui = moderation.ui('avatar') const avatar = profile.avatar if (avatar && !(modui.blur && modui.noOverride)) { - const aviHandle = aviRef.current runOnUI(() => { 'worklet' - const rect = measureHandle(aviHandle) + const rect = measure(aviRef) runOnJS(_openLightbox)(avatar, rect) })() } @@ -223,7 +223,7 @@ let ProfileHeaderShell = ({ styles.avi, profile.associated?.labeler && styles.aviLabeler, ]}> - + {live.isActive && } - + diff --git a/src/view/com/composer/text-input/TextInput.tsx b/src/view/com/composer/text-input/TextInput.tsx index 6f5e812ed2..f927015af9 100644 --- a/src/view/com/composer/text-input/TextInput.tsx +++ b/src/view/com/composer/text-input/TextInput.tsx @@ -1,5 +1,5 @@ import React, { - ComponentProps, + type ComponentProps, forwardRef, useCallback, useMemo, @@ -7,16 +7,16 @@ import React, { useState, } from 'react' import { - NativeSyntheticEvent, + type NativeSyntheticEvent, Text as RNText, - TextInput as RNTextInput, - TextInputSelectionChangeEventData, + type TextInput as RNTextInput, + type TextInputSelectionChangeEventData, View, } from 'react-native' import {AppBskyRichtextFacet, RichText} from '@atproto/api' import PasteInput, { - PastedFile, - PasteInputRef, + type PastedFile, + type PasteInputRef, // @ts-expect-error no types when installing from github } from '@mattermost/react-native-paste-input' import {POST_IMG_MAX} from '#/lib/constants' @@ -27,7 +27,7 @@ import {getMentionAt, insertMentionAt} from '#/lib/strings/mention-manip' import {useTheme} from '#/lib/ThemeContext' import {isAndroid, isNative} from '#/platform/detection' import { - LinkFacetMatch, + type LinkFacetMatch, suggestLinkCardUri, } from '#/view/com/composer/text-input/text-input-util' import {atoms as a, useAlf} from '#/alf' diff --git a/src/view/com/pager/Pager.tsx b/src/view/com/pager/Pager.tsx index f62bffc534..8cc3469032 100644 --- a/src/view/com/pager/Pager.tsx +++ b/src/view/com/pager/Pager.tsx @@ -1,16 +1,22 @@ -import React, {forwardRef, useCallback, useContext} from 'react' +import { + useCallback, + useContext, + useImperativeHandle, + useRef, + useState, +} from 'react' import {View} from 'react-native' import {DrawerGestureContext} from 'react-native-drawer-layout' import {Gesture, GestureDetector} from 'react-native-gesture-handler' import PagerView, { - PagerViewOnPageScrollEventData, - PagerViewOnPageSelectedEvent, - PagerViewOnPageSelectedEventData, - PageScrollStateChangedNativeEventData, + type PagerViewOnPageScrollEventData, + type PagerViewOnPageSelectedEvent, + type PagerViewOnPageSelectedEventData, + type PageScrollStateChangedNativeEventData, } from 'react-native-pager-view' import Animated, { runOnJS, - SharedValue, + type SharedValue, useEvent, useHandler, useSharedValue, @@ -36,8 +42,12 @@ export interface RenderTabBarFnProps { export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element interface Props { + ref?: React.Ref initialPage?: number renderTabBar: RenderTabBarFn + // tab pressed, yet to scroll to page + onTabPressed?: (index: number) => void + // scroll settled onPageSelected?: (index: number) => void onPageScrollStateChanged?: ( scrollState: 'idle' | 'dragging' | 'settling', @@ -47,114 +57,112 @@ interface Props { const AnimatedPagerView = Animated.createAnimatedComponent(PagerView) -export const Pager = forwardRef>( - function PagerImpl( +export function Pager({ + ref, + children, + initialPage = 0, + renderTabBar, + onPageSelected: parentOnPageSelected, + onTabPressed: parentOnTabPressed, + onPageScrollStateChanged: parentOnPageScrollStateChanged, + testID, +}: React.PropsWithChildren) { + const [selectedPage, setSelectedPage] = useState(initialPage) + const pagerView = useRef(null) + + const [isIdle, setIsIdle] = useState(true) + const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() + useFocusEffect( + useCallback(() => { + const canSwipeDrawer = selectedPage === 0 && isIdle + setDrawerSwipeDisabled(!canSwipeDrawer) + return () => { + setDrawerSwipeDisabled(false) + } + }, [setDrawerSwipeDisabled, selectedPage, isIdle]), + ) + + useImperativeHandle(ref, () => ({ + setPage: (index: number) => { + pagerView.current?.setPage(index) + }, + })) + + const onPageSelectedJSThread = useCallback( + (nextPosition: number) => { + setSelectedPage(nextPosition) + parentOnPageSelected?.(nextPosition) + }, + [setSelectedPage, parentOnPageSelected], + ) + + const onTabBarSelect = useCallback( + (index: number) => { + parentOnTabPressed?.(index) + pagerView.current?.setPage(index) + }, + [pagerView, parentOnTabPressed], + ) + + const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle') + const dragProgress = useSharedValue(selectedPage) + const didInit = useSharedValue(false) + const handlePageScroll = usePagerHandlers( { - children, - initialPage = 0, - renderTabBar, - onPageScrollStateChanged: parentOnPageScrollStateChanged, - onPageSelected: parentOnPageSelected, - testID, - }: React.PropsWithChildren, - ref, - ) { - const [selectedPage, setSelectedPage] = React.useState(initialPage) - const pagerView = React.useRef(null) - - const [isIdle, setIsIdle] = React.useState(true) - const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() - useFocusEffect( - useCallback(() => { - const canSwipeDrawer = selectedPage === 0 && isIdle - setDrawerSwipeDisabled(!canSwipeDrawer) - return () => { - setDrawerSwipeDisabled(false) + onPageScroll(e: PagerViewOnPageScrollEventData) { + 'worklet' + if (didInit.get() === false) { + // On iOS, there's a spurious scroll event with 0 position + // even if a different page was supplied as the initial page. + // Ignore it and wait for the first confirmed selection instead. + return } - }, [setDrawerSwipeDisabled, selectedPage, isIdle]), - ) - - React.useImperativeHandle(ref, () => ({ - setPage: (index: number) => { - pagerView.current?.setPage(index) + dragProgress.set(e.offset + e.position) }, - })) - - const onPageSelectedJSThread = React.useCallback( - (nextPosition: number) => { - setSelectedPage(nextPosition) - parentOnPageSelected?.(nextPosition) + onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) { + 'worklet' + runOnJS(setIsIdle)(e.pageScrollState === 'idle') + if (dragState.get() === 'idle' && e.pageScrollState === 'settling') { + // This is a programmatic scroll on Android. + // Stay "idle" to match iOS and avoid confusing downstream code. + return + } + dragState.set(e.pageScrollState) + parentOnPageScrollStateChanged?.(e.pageScrollState) }, - [setSelectedPage, parentOnPageSelected], - ) - - const onTabBarSelect = React.useCallback( - (index: number) => { - pagerView.current?.setPage(index) + onPageSelected(e: PagerViewOnPageSelectedEventData) { + 'worklet' + didInit.set(true) + runOnJS(onPageSelectedJSThread)(e.position) }, - [pagerView], - ) + }, + [parentOnPageScrollStateChanged], + ) - const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle') - const dragProgress = useSharedValue(selectedPage) - const didInit = useSharedValue(false) - const handlePageScroll = usePagerHandlers( - { - onPageScroll(e: PagerViewOnPageScrollEventData) { - 'worklet' - if (didInit.get() === false) { - // On iOS, there's a spurious scroll event with 0 position - // even if a different page was supplied as the initial page. - // Ignore it and wait for the first confirmed selection instead. - return - } - dragProgress.set(e.offset + e.position) - }, - onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) { - 'worklet' - runOnJS(setIsIdle)(e.pageScrollState === 'idle') - if (dragState.get() === 'idle' && e.pageScrollState === 'settling') { - // This is a programmatic scroll on Android. - // Stay "idle" to match iOS and avoid confusing downstream code. - return - } - dragState.set(e.pageScrollState) - parentOnPageScrollStateChanged?.(e.pageScrollState) - }, - onPageSelected(e: PagerViewOnPageSelectedEventData) { - 'worklet' - didInit.set(true) - runOnJS(onPageSelectedJSThread)(e.position) - }, - }, - [parentOnPageScrollStateChanged], - ) + const drawerGesture = useContext(DrawerGestureContext) ?? Gesture.Native() // noop for web + const nativeGesture = + Gesture.Native().requireExternalGestureToFail(drawerGesture) - const drawerGesture = useContext(DrawerGestureContext) ?? Gesture.Native() // noop for web - const nativeGesture = - Gesture.Native().requireExternalGestureToFail(drawerGesture) - - return ( - - {renderTabBar({ - selectedPage, - onSelect: onTabBarSelect, - dragProgress, - dragState, - })} - - - {children} - - - - ) - }, -) + return ( + + {renderTabBar({ + selectedPage, + onSelect: onTabBarSelect, + dragProgress, + dragState, + })} + + + {children} + + + + ) +} function usePagerHandlers( handlers: { diff --git a/src/view/com/pager/Pager.web.tsx b/src/view/com/pager/Pager.web.tsx index c620e73e33..06aac169c8 100644 --- a/src/view/com/pager/Pager.web.tsx +++ b/src/view/com/pager/Pager.web.tsx @@ -1,8 +1,19 @@ -import React from 'react' +import { + Children, + useCallback, + useImperativeHandle, + useRef, + useState, +} from 'react' import {View} from 'react-native' import {flushSync} from 'react-dom' import {s} from '#/lib/styles' +import {atoms as a} from '#/alf' + +export interface PagerRef { + setPage: (index: number) => void +} export interface RenderTabBarFnProps { selectedPage: number @@ -12,30 +23,30 @@ export interface RenderTabBarFnProps { export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element interface Props { + ref?: React.Ref initialPage?: number renderTabBar: RenderTabBarFn onPageSelected?: (index: number) => void } -export const Pager = React.forwardRef(function PagerImpl( - { - children, - initialPage = 0, - renderTabBar, - onPageSelected, - }: React.PropsWithChildren, - ref, -) { - const [selectedPage, setSelectedPage] = React.useState(initialPage) - const scrollYs = React.useRef>([]) - const anchorRef = React.useRef(null) - React.useImperativeHandle(ref, () => ({ +export function Pager({ + ref, + children, + initialPage = 0, + renderTabBar, + onPageSelected, +}: React.PropsWithChildren) { + const [selectedPage, setSelectedPage] = useState(initialPage) + const scrollYs = useRef>([]) + const anchorRef = useRef(null) + + useImperativeHandle(ref, () => ({ setPage: (index: number) => { onTabBarSelect(index) }, })) - const onTabBarSelect = React.useCallback( + const onTabBarSelect = useCallback( (index: number) => { const scrollY = window.scrollY // We want to determine if the tabbar is already "sticking" at the top (in which @@ -75,11 +86,13 @@ export const Pager = React.forwardRef(function PagerImpl( tabBarAnchor: , onSelect: e => onTabBarSelect(e), })} - {React.Children.map(children, (child, i) => ( - + {Children.map(children, (child, i) => ( + {child} ))} ) -}) +} diff --git a/src/view/com/pager/PagerWithHeader.tsx b/src/view/com/pager/PagerWithHeader.tsx index 1746d2ca13..57aaac0740 100644 --- a/src/view/com/pager/PagerWithHeader.tsx +++ b/src/view/com/pager/PagerWithHeader.tsx @@ -1,17 +1,16 @@ -import * as React from 'react' +import {memo, useCallback, useEffect, useRef, useState} from 'react' import { - LayoutChangeEvent, - NativeScrollEvent, - ScrollView, + type LayoutChangeEvent, + type NativeScrollEvent, + type ScrollView, StyleSheet, View, } from 'react-native' import Animated, { - AnimatedRef, - runOnJS, + type AnimatedRef, runOnUI, scrollTo, - SharedValue, + type SharedValue, useAnimatedRef, useAnimatedStyle, useSharedValue, @@ -20,9 +19,13 @@ import Animated, { import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {ScrollProvider} from '#/lib/ScrollContext' import {isIOS} from '#/platform/detection' -import {Pager, PagerRef, RenderTabBarFnProps} from '#/view/com/pager/Pager' +import { + Pager, + type PagerRef, + type RenderTabBarFnProps, +} from '#/view/com/pager/Pager' import {useTheme} from '#/alf' -import {ListMethods} from '../util/List' +import {type ListMethods} from '../util/List' import {PagerHeaderProvider} from './PagerHeaderContext' import {TabBar} from './TabBar' @@ -33,6 +36,7 @@ export interface PagerWithHeaderChildParams { } export interface PagerWithHeaderProps { + ref?: React.Ref testID?: string children: | (((props: PagerWithHeaderChildParams) => JSX.Element) | null)[] @@ -49,97 +53,94 @@ export interface PagerWithHeaderProps { onCurrentPageSelected?: (index: number) => void allowHeaderOverScroll?: boolean } -export const PagerWithHeader = React.forwardRef( - function PageWithHeaderImpl( - { - children, - testID, +export function PagerWithHeader({ + ref, + children, + testID, + items, + isHeaderReady, + renderHeader, + initialPage, + onPageSelected, + onCurrentPageSelected, + allowHeaderOverScroll, +}: PagerWithHeaderProps) { + const [currentPage, setCurrentPage] = useState(0) + const [tabBarHeight, setTabBarHeight] = useState(0) + const [headerOnlyHeight, setHeaderOnlyHeight] = useState(0) + const scrollY = useSharedValue(0) + const headerHeight = headerOnlyHeight + tabBarHeight + + // capture the header bar sizing + const onTabBarLayout = useNonReactiveCallback((evt: LayoutChangeEvent) => { + const height = evt.nativeEvent.layout.height + if (height > 0) { + // The rounding is necessary to prevent jumps on iOS + setTabBarHeight(Math.round(height * 2) / 2) + } + }) + const onHeaderOnlyLayout = useNonReactiveCallback((height: number) => { + if (height > 0) { + // The rounding is necessary to prevent jumps on iOS + setHeaderOnlyHeight(Math.round(height * 2) / 2) + } + }) + + const renderTabBar = useCallback( + (props: RenderTabBarFnProps) => { + return ( + + + + ) + }, + [ + headerOnlyHeight, items, isHeaderReady, renderHeader, - initialPage, - onPageSelected, + currentPage, onCurrentPageSelected, + onTabBarLayout, + onHeaderOnlyLayout, + scrollY, + testID, allowHeaderOverScroll, - }: PagerWithHeaderProps, - ref, - ) { - const [currentPage, setCurrentPage] = React.useState(0) - const [tabBarHeight, setTabBarHeight] = React.useState(0) - const [headerOnlyHeight, setHeaderOnlyHeight] = React.useState(0) - const scrollY = useSharedValue(0) - const headerHeight = headerOnlyHeight + tabBarHeight + ], + ) - // capture the header bar sizing - const onTabBarLayout = useNonReactiveCallback((evt: LayoutChangeEvent) => { - const height = evt.nativeEvent.layout.height - if (height > 0) { - // The rounding is necessary to prevent jumps on iOS - setTabBarHeight(Math.round(height * 2) / 2) - } - }) - const onHeaderOnlyLayout = useNonReactiveCallback((height: number) => { - if (height > 0) { - // The rounding is necessary to prevent jumps on iOS - setHeaderOnlyHeight(Math.round(height * 2) / 2) - } - }) + const scrollRefs = useSharedValue | null>>([]) + const registerRef = useCallback( + (scrollRef: AnimatedRef | null, atIndex: number) => { + scrollRefs.modify(refs => { + 'worklet' + refs[atIndex] = scrollRef + return refs + }) + }, + [scrollRefs], + ) - const renderTabBar = React.useCallback( - (props: RenderTabBarFnProps) => { - return ( - - - - ) - }, - [ - headerOnlyHeight, - items, - isHeaderReady, - renderHeader, - currentPage, - onCurrentPageSelected, - onTabBarLayout, - onHeaderOnlyLayout, - scrollY, - testID, - allowHeaderOverScroll, - ], - ) - - const scrollRefs = useSharedValue | null>>([]) - const registerRef = React.useCallback( - (scrollRef: AnimatedRef | null, atIndex: number) => { - scrollRefs.modify(refs => { - 'worklet' - refs[atIndex] = scrollRef - return refs - }) - }, - [scrollRefs], - ) - - const lastForcedScrollY = useSharedValue(0) - const adjustScrollForOtherPages = () => { + const lastForcedScrollY = useSharedValue(0) + const adjustScrollForOtherPages = useCallback( + (scrollState: 'idle' | 'dragging' | 'settling') => { 'worklet' + if (scrollState !== 'dragging') return const currentScrollY = scrollY.get() const forcedScrollY = Math.min(currentScrollY, headerOnlyHeight) if (lastForcedScrollY.get() !== forcedScrollY) { @@ -152,75 +153,69 @@ export const PagerWithHeader = React.forwardRef( } } } - } + }, + [currentPage, headerOnlyHeight, lastForcedScrollY, scrollRefs, scrollY], + ) - const throttleTimeout = React.useRef | null>( - null, - ) - const queueThrottledOnScroll = useNonReactiveCallback(() => { - if (!throttleTimeout.current) { - throttleTimeout.current = setTimeout(() => { - throttleTimeout.current = null - runOnUI(adjustScrollForOtherPages)() - }, 80 /* Sync often enough you're unlikely to catch it unsynced */) + const onScrollWorklet = useCallback( + (e: NativeScrollEvent) => { + 'worklet' + const nextScrollY = e.contentOffset.y + // HACK: onScroll is reporting some strange values on load (negative header height). + // Highly improbable that you'd be overscrolled by over 400px - + // in fact, I actually can't do it, so let's just ignore those. -sfn + const isPossiblyInvalid = + headerHeight > 0 && Math.round(nextScrollY * 2) / 2 === -headerHeight + if (!isPossiblyInvalid) { + scrollY.set(nextScrollY) } - }) + }, + [scrollY, headerHeight], + ) - const onScrollWorklet = React.useCallback( - (e: NativeScrollEvent) => { - 'worklet' - const nextScrollY = e.contentOffset.y - // HACK: onScroll is reporting some strange values on load (negative header height). - // Highly improbable that you'd be overscrolled by over 400px - - // in fact, I actually can't do it, so let's just ignore those. -sfn - const isPossiblyInvalid = - headerHeight > 0 && Math.round(nextScrollY * 2) / 2 === -headerHeight - if (!isPossiblyInvalid) { - scrollY.set(nextScrollY) - runOnJS(queueThrottledOnScroll)() - } - }, - [scrollY, queueThrottledOnScroll, headerHeight], - ) + const onPageSelectedInner = useCallback( + (index: number) => { + setCurrentPage(index) + onPageSelected?.(index) + }, + [onPageSelected, setCurrentPage], + ) - const onPageSelectedInner = React.useCallback( - (index: number) => { - setCurrentPage(index) - onPageSelected?.(index) - }, - [onPageSelected, setCurrentPage], - ) + const onTabPressed = useCallback(() => { + runOnUI(adjustScrollForOtherPages)('dragging') + }, [adjustScrollForOtherPages]) - return ( - - {toArray(children) - .filter(Boolean) - .map((child, i) => { - const isReady = - isHeaderReady && headerOnlyHeight > 0 && tabBarHeight > 0 - return ( - - - - ) - })} - - ) - }, -) + return ( + + {toArray(children) + .filter(Boolean) + .map((child, i) => { + const isReady = + isHeaderReady && headerOnlyHeight > 0 && tabBarHeight > 0 + return ( + + + + ) + })} + + ) +} let PagerTabBar = ({ currentPage, @@ -258,7 +253,7 @@ let PagerTabBar = ({ dragState: SharedValue<'idle' | 'dragging' | 'settling'> }): React.ReactNode => { const t = useTheme() - const [minimumHeaderHeight, setMinimumHeaderHeight] = React.useState(0) + const [minimumHeaderHeight, setMinimumHeaderHeight] = useState(0) const headerTransform = useAnimatedStyle(() => { const translateY = Math.min( @@ -275,7 +270,7 @@ let PagerTabBar = ({ ], } }) - const headerRef = React.useRef(null) + const headerRef = useRef(null) return ( ) } -PagerTabBar = React.memo(PagerTabBar) +PagerTabBar = memo(PagerTabBar) function PagerItem({ headerHeight, @@ -348,7 +343,7 @@ function PagerItem({ }) { const scrollElRef = useAnimatedRef() - React.useEffect(() => { + useEffect(() => { registerRef(scrollElRef, index) return () => { registerRef(null, index) diff --git a/src/view/com/profile/ProfileSubpageHeader.tsx b/src/view/com/profile/ProfileSubpageHeader.tsx index b0cf4d10e4..02a6704bf9 100644 --- a/src/view/com/profile/ProfileSubpageHeader.tsx +++ b/src/view/com/profile/ProfileSubpageHeader.tsx @@ -1,23 +1,28 @@ import React from 'react' import {Pressable, View} from 'react-native' -import {MeasuredDimensions, runOnJS, runOnUI} from 'react-native-reanimated' -import {AppBskyGraphDefs} from '@atproto/api' +import Animated, { + measure, + type MeasuredDimensions, + runOnJS, + runOnUI, + useAnimatedRef, +} from 'react-native-reanimated' +import {type AppBskyGraphDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' -import {measureHandle, useHandleRef} from '#/lib/hooks/useHandleRef' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {makeProfileLink} from '#/lib/routes/links' -import {NavigationProp} from '#/lib/routes/types' +import {type NavigationProp} from '#/lib/routes/types' import {sanitizeHandle} from '#/lib/strings/handles' import {emitSoftReset} from '#/state/events' import {useLightboxControls} from '#/state/lightbox' import {TextLink} from '#/view/com/util/Link' import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {Text} from '#/view/com/util/text/Text' -import {UserAvatar, UserAvatarType} from '#/view/com/util/UserAvatar' +import {UserAvatar, type UserAvatarType} from '#/view/com/util/UserAvatar' import {StarterPack} from '#/components/icons/StarterPack' import * as Layout from '#/components/Layout' @@ -52,7 +57,7 @@ export function ProfileSubpageHeader({ const {openLightbox} = useLightboxControls() const pal = usePalette('default') const canGoBack = navigation.canGoBack() - const aviRef = useHandleRef() + const aviRef = useAnimatedRef() const _openLightbox = React.useCallback( (uri: string, thumbRect: MeasuredDimensions | null) => { @@ -81,10 +86,9 @@ export function ProfileSubpageHeader({ if ( avatar // TODO && !(view.moderation.avatar.blur && view.moderation.avatar.noOverride) ) { - const aviHandle = aviRef.current runOnUI(() => { 'worklet' - const rect = measureHandle(aviHandle) + const rect = measure(aviRef) runOnJS(_openLightbox)(avatar, rect) })() } @@ -111,7 +115,7 @@ export function ProfileSubpageHeader({ paddingBottom: 14, paddingHorizontal: isMobile ? 12 : 14, }}> - + )} - + {isLoading ? ( void + onPress?: ( + containerRef: AnimatedRef, + fetchedDims: Dimensions | null, + ) => void onLongPress?: () => void onPressIn?: () => void }) { const t = useTheme() const {_} = useLingui() const largeAlt = useLargeAltBadgeEnabled() - const containerRef = useHandleRef() + const containerRef = useAnimatedRef() const fetchedDimsRef = useRef<{width: number; height: number} | null>(null) let aspectRatio: number | undefined @@ -103,7 +109,7 @@ export function AutoSizedImage({ const hasAlt = !!image.alt const contents = ( - + ) : null} - + ) if (cropDisabled) { diff --git a/src/view/com/util/images/Gallery.tsx b/src/view/com/util/images/Gallery.tsx index cc3eda68d2..1d35c88c59 100644 --- a/src/view/com/util/images/Gallery.tsx +++ b/src/view/com/util/images/Gallery.tsx @@ -1,12 +1,12 @@ -import React from 'react' -import {Pressable, StyleProp, View, ViewStyle} from 'react-native' -import {Image, ImageStyle} from 'expo-image' -import {AppBskyEmbedImages} from '@atproto/api' +import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native' +import {type AnimatedRef} from 'react-native-reanimated' +import {Image, type ImageStyle} from 'expo-image' +import {type AppBskyEmbedImages} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' +import type React from 'react' -import {HandleRef} from '#/lib/hooks/useHandleRef' -import {Dimensions} from '#/lib/media/types' +import {type Dimensions} from '#/lib/media/types' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types' import {atoms as a, useTheme} from '#/alf' @@ -20,7 +20,7 @@ interface Props { index: number onPress?: ( index: number, - containerRefs: HandleRef[], + containerRefs: AnimatedRef[], fetchedDims: (Dimensions | null)[], ) => void onLongPress?: EventFunction @@ -28,7 +28,7 @@ interface Props { imageStyle?: StyleProp viewContext?: PostEmbedViewContext insetBorderStyle?: StyleProp - containerRefs: HandleRef[] + containerRefs: AnimatedRef[] thumbDimsRef: React.MutableRefObject<(Dimensions | null)[]> } diff --git a/src/view/com/util/images/ImageLayoutGrid.tsx b/src/view/com/util/images/ImageLayoutGrid.tsx index 16ea9d453f..b91d7a7adb 100644 --- a/src/view/com/util/images/ImageLayoutGrid.tsx +++ b/src/view/com/util/images/ImageLayoutGrid.tsx @@ -1,18 +1,18 @@ import React from 'react' -import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' -import {AppBskyEmbedImages} from '@atproto/api' +import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native' +import {type AnimatedRef, useAnimatedRef} from 'react-native-reanimated' +import {type AppBskyEmbedImages} from '@atproto/api' -import {HandleRef, useHandleRef} from '#/lib/hooks/useHandleRef' import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types' import {atoms as a, useBreakpoints} from '#/alf' -import {Dimensions} from '../../lightbox/ImageViewing/@types' +import {type Dimensions} from '../../lightbox/ImageViewing/@types' import {GalleryItem} from './Gallery' interface ImageLayoutGridProps { images: AppBskyEmbedImages.ViewImage[] onPress?: ( index: number, - containerRefs: HandleRef[], + containerRefs: AnimatedRef[], fetchedDims: (Dimensions | null)[], ) => void onLongPress?: (index: number) => void @@ -43,7 +43,7 @@ interface ImageLayoutGridInnerProps { images: AppBskyEmbedImages.ViewImage[] onPress?: ( index: number, - containerRefs: HandleRef[], + containerRefs: AnimatedRef[], fetchedDims: (Dimensions | null)[], ) => void onLongPress?: (index: number) => void @@ -56,10 +56,10 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { const gap = props.gap const count = props.images.length - const containerRef1 = useHandleRef() - const containerRef2 = useHandleRef() - const containerRef3 = useHandleRef() - const containerRef4 = useHandleRef() + const containerRef1 = useAnimatedRef() + const containerRef2 = useAnimatedRef() + const containerRef3 = useAnimatedRef() + const containerRef4 = useAnimatedRef() const thumbDimsRef = React.useRef<(Dimensions | null)[]>([]) switch (count) { diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index 431baa2b22..4cf71f9486 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -7,6 +7,8 @@ import { type ViewStyle, } from 'react-native' import { + type AnimatedRef, + measure, type MeasuredDimensions, runOnJS, runOnUI, @@ -25,7 +27,6 @@ import { type ModerationDecision, } from '@atproto/api' -import {type HandleRef, measureHandle} from '#/lib/hooks/useHandleRef' import {usePalette} from '#/lib/hooks/usePalette' import {useLightboxControls} from '#/state/lightbox' import {useModerationOpts} from '#/state/preferences/moderation-opts' @@ -162,13 +163,15 @@ export function PostEmbeds({ } const onPress = ( index: number, - refs: HandleRef[], + refs: AnimatedRef[], fetchedDims: (Dimensions | null)[], ) => { - const handles = refs.map(r => r.current) runOnUI(() => { 'worklet' - const rects = handles.map(measureHandle) + const rects: (MeasuredDimensions | null)[] = [] + for (const r of refs) { + rects.push(measure(r)) + } runOnJS(_openLightbox)(index, rects, fetchedDims) })() } diff --git a/yarn.lock b/yarn.lock index 9c6a368472..125c985ea5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3776,29 +3776,29 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.47.0.tgz#5478fdf443ff8158f9de171c704ae45308696c7d" integrity sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og== -"@expo/cli@0.24.10": - version "0.24.10" - resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.24.10.tgz#f23d150f011cdebd6f503343d9be3c6283fb4096" - integrity sha512-auPE4MSRdkJkHsWJk935VoqX/BGMKARgXOLtJybTFUi64K3MkvdUoBrujcn/QzXl5DTGhacFd9qxUeQss6/qwg== +"@expo/cli@0.24.14": + version "0.24.14" + resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.24.14.tgz#af2e7ea5a08e3574e868cb0ec2110e68d32672e0" + integrity sha512-o+QYyfIBhSRTgaywKTLJhm2Fg5PrSeUVCXS+uQySamgoMjLNhHa8QwE64mW/FmJr5hZLiqUEQxb60FK4JcyqXg== dependencies: "@0no-co/graphql.web" "^1.0.8" "@babel/runtime" "^7.20.0" "@expo/code-signing-certificates" "^0.0.5" - "@expo/config" "~11.0.7" + "@expo/config" "~11.0.10" "@expo/config-plugins" "~10.0.2" "@expo/devcert" "^1.1.2" "@expo/env" "~1.0.5" "@expo/image-utils" "^0.7.4" "@expo/json-file" "^9.1.4" - "@expo/metro-config" "~0.20.12" + "@expo/metro-config" "~0.20.14" "@expo/osascript" "^2.2.4" "@expo/package-manager" "^1.8.4" "@expo/plist" "^0.3.4" - "@expo/prebuild-config" "^9.0.5" + "@expo/prebuild-config" "^9.0.6" "@expo/spawn-async" "^1.7.2" "@expo/ws-tunnel" "^1.0.1" "@expo/xcpretty" "^4.3.0" - "@react-native/dev-middleware" "0.79.2" + "@react-native/dev-middleware" "0.79.3" "@urql/core" "^5.0.6" "@urql/exchange-retry" "^1.3.0" accepts "^1.3.8" @@ -3813,9 +3813,9 @@ debug "^4.3.4" env-editor "^0.4.1" freeport-async "^2.0.0" - getenv "^1.0.0" + getenv "^2.0.0" glob "^10.4.2" - lan-network "^0.1.4" + lan-network "^0.1.6" minimatch "^9.0.0" node-forge "^1.3.1" npm-package-arg "^11.0.0" @@ -3871,7 +3871,7 @@ xcode "^3.0.1" xml2js "0.6.0" -"@expo/config-plugins@~10.0.1", "@expo/config-plugins@~10.0.2": +"@expo/config-plugins@~10.0.2": version "10.0.2" resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-10.0.2.tgz#040867991e9c8c527b4f5c13a47bcf040a7479fe" integrity sha512-TzUn3pPdpwCS0yYaSlZOClgDmCX8N4I2lfgitX5oStqmvpPtB+vqtdyqsVM02fQ2tlJIAqwBW+NHaHqqy8Jv7g== @@ -3926,6 +3926,11 @@ resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-53.0.3.tgz#d083d9b095972e89eee96c41d085feb5b92d2749" integrity sha512-V1e6CiM4TXtGxG/W2Msjp/QOx/vikLo5IUGMvEMjgAglBfGYx3PXfqsUb5aZDt6kqA3bDDwFuZoS5vNm/SYwSg== +"@expo/config-types@^53.0.4": + version "53.0.4" + resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-53.0.4.tgz#fe64fac734531ae883d18529b32586c23ffb1ceb" + integrity sha512-0s+9vFx83WIToEr0Iwy4CcmiUXa5BgwBmEjylBB2eojX5XAMm9mJvw9KpjAb8m7zq2G0Q6bRbeufkzgbipuNQg== + "@expo/config@~10.0.4": version "10.0.5" resolved "https://registry.yarnpkg.com/@expo/config/-/config-10.0.5.tgz#2de75e3f5d46a55f9f5140b73e0913265e6a41c6" @@ -3945,14 +3950,14 @@ slugify "^1.3.4" sucrase "3.35.0" -"@expo/config@~11.0.6", "@expo/config@~11.0.7": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@expo/config/-/config-11.0.7.tgz#e6a6071942854269825e2450c3a115c963a4fd56" - integrity sha512-pppH3Cy2IfituiYACMeW7cWYezcjmHKq7lDLfH1gMHT+zZ1QaYNs3EN6Kcc/QAXV//KFFhU0Qq4H/UrLuPp/yg== +"@expo/config@~11.0.10", "@expo/config@~11.0.9": + version "11.0.10" + resolved "https://registry.yarnpkg.com/@expo/config/-/config-11.0.10.tgz#559d9425a4e0de4fab96ccac01ff40f5cebbc04b" + integrity sha512-8S8Krr/c5lnl0eF03tA2UGY9rGBhZcbWKz2UWw5dpL/+zstwUmog8oyuuC8aRcn7GiTQLlbBkxcMeT8sOGlhbA== dependencies: "@babel/code-frame" "~7.10.4" - "@expo/config-plugins" "~10.0.1" - "@expo/config-types" "^53.0.3" + "@expo/config-plugins" "~10.0.2" + "@expo/config-types" "^53.0.4" "@expo/json-file" "^9.1.4" deepmerge "^4.3.1" getenv "^1.0.0" @@ -4005,23 +4010,24 @@ dotenv-expand "~11.0.6" getenv "^1.0.0" -"@expo/fingerprint@0.12.4": - version "0.12.4" - resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.12.4.tgz#d4cc4de50e7b6d4e03b0d38850d1e4a136b74c8c" - integrity sha512-HOJVvjiQYVHIouCOfFf4JRrQvBDIV/12GVG2iwbw1iGwmpQVkPgEXa9lN0f2yuS4J3QXHs73wr9jvuCjMmJlfw== +"@expo/fingerprint@0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.13.0.tgz#5f5600122940ac381ed697743c10bdbddf6c55c1" + integrity sha512-3IwpH0p3uO8jrJSLOUNDzJVh7VEBod0emnCBq0hD72sy6ICmzauM6Xf4he+2Tip7fzImCJRd63GaehV+CCtpvA== dependencies: "@expo/spawn-async" "^1.7.2" arg "^5.0.2" chalk "^4.1.2" debug "^4.3.4" find-up "^5.0.0" - getenv "^1.0.0" + getenv "^2.0.0" + ignore "^5.3.1" minimatch "^9.0.0" p-limit "^3.1.0" resolve-from "^5.0.0" semver "^7.6.0" -"@expo/html-elements@^0.12.4": +"@expo/html-elements@^0.12.5": version "0.12.5" resolved "https://registry.yarnpkg.com/@expo/html-elements/-/html-elements-0.12.5.tgz#be7e7af9f2be6d3f1aa3ec2e7ae1c121c91a9aa1" integrity sha512-28KWO88YKykKU7ke5sEQs5TivFRMs1Aktz13xxgqAf5rTgb+lka0VKVt3W2fG7ksbUQ407rtUqz7SEAq298NvQ== @@ -4068,16 +4074,16 @@ json5 "^2.2.3" write-file-atomic "^2.3.0" -"@expo/metro-config@0.20.12", "@expo/metro-config@~0.20.12": - version "0.20.12" - resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.20.12.tgz#f6e2c33a305cb0ab8b0aa0dadafd6adf09058b9c" - integrity sha512-O9zaAF3gH76EXkwuQCpXLKC5dBy344/pqoszWmtOloKo4gJy74aNPUy2LRS57pDHyjZe1HjrxVkMcr7lZCIsag== +"@expo/metro-config@0.20.14", "@expo/metro-config@~0.20.14": + version "0.20.14" + resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.20.14.tgz#5abf8cd6454fe7f75c1f8529cf79619da32af82d" + integrity sha512-tYDDubuZycK+NX00XN7BMu73kBur/evOPcKfxc+UBeFfgN2EifOITtdwSUDdRsbtJ2OnXwMY1HfRUG3Lq3l4cw== dependencies: "@babel/core" "^7.20.0" "@babel/generator" "^7.20.5" "@babel/parser" "^7.20.0" "@babel/types" "^7.20.0" - "@expo/config" "~11.0.7" + "@expo/config" "~11.0.9" "@expo/env" "~1.0.5" "@expo/json-file" "~9.1.4" "@expo/spawn-async" "^1.7.2" @@ -4140,14 +4146,14 @@ base64-js "^1.2.3" xmlbuilder "^15.1.1" -"@expo/prebuild-config@^9.0.5": - version "9.0.5" - resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-9.0.5.tgz#b8b864b5e19489a1f66442ae30d5d7295f658297" - integrity sha512-oiSVU5ePu9lsOvn5p4xplqjzPlcZHzKYwzuonTa9GCH1GxcOEIBsvMVQiHBXHtqvgV2dztjm34kdXV//+9jtCA== +"@expo/prebuild-config@^9.0.6": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-9.0.6.tgz#f634e7b8f9ebebeaf2e7d2f2be46926c23834d2b" + integrity sha512-HDTdlMkTQZ95rd6EpvuLM+xkZV03yGLc38FqI37qKFLJtUN1WnYVaWsuXKoljd1OrVEVsHe6CfqKwaPZ52D56Q== dependencies: - "@expo/config" "~11.0.7" + "@expo/config" "~11.0.9" "@expo/config-plugins" "~10.0.2" - "@expo/config-types" "^53.0.3" + "@expo/config-types" "^53.0.4" "@expo/image-utils" "^0.7.4" "@expo/json-file" "^9.1.4" "@react-native/normalize-colors" "0.79.2" @@ -4416,10 +4422,10 @@ protobufjs "^7.2.5" yargs "^17.7.2" -"@haileyok/bluesky-video@0.2.6": - version "0.2.6" - resolved "https://registry.yarnpkg.com/@haileyok/bluesky-video/-/bluesky-video-0.2.6.tgz#61bb4ff908498558fd2320f06ba0f74ea03598b4" - integrity sha512-IlzrTATD7ci/a+ehSA7pIhOvxxNTe35zdzBYt8EmVvKKafyeFhxGfDuuP5qxfQmHurcQMIL+3HeZgnAXwQQNmQ== +"@haileyok/bluesky-video@0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@haileyok/bluesky-video/-/bluesky-video-0.3.1.tgz#c996d8433e8f1988f2e9644adf39366fb4305d00" + integrity sha512-TU5c0RCV1yWF/HEZA22E0qXjxNzu0SOuMZpvdo+QVrO4tQcja9k8nHBbKRmyj60Odke4HQMoTOfNDNq/xo2Grw== "@hapi/accept@^6.0.3": version "6.0.3" @@ -5119,12 +5125,11 @@ "@babel/runtime" "^7.20.13" "@lingui/core" "4.14.1" -"@mattermost/react-native-paste-input@^0.7.1": - version "0.7.1" - resolved "https://registry.yarnpkg.com/@mattermost/react-native-paste-input/-/react-native-paste-input-0.7.1.tgz#f14585030b992cf7c9bbd0921225eefa501756ba" - integrity sha512-kY8LKtqRX2T/rtn/HNrzTitijuATvyzd6yl5WNWOsszmyzNcssKStjjCTBup04CyMxfwutUU1CWrYUb3hQO7oA== +"@mattermost/react-native-paste-input@mattermost/react-native-paste-input": + version "0.8.1" + resolved "https://codeload.github.com/mattermost/react-native-paste-input/tar.gz/f260447edc645a817ab1ba7b46d8341d84dba8e9" dependencies: - semver "7.6.0" + semver "7.6.3" "@messageformat/parser@^5.0.0": version "5.1.0" @@ -5133,12 +5138,12 @@ dependencies: moo "^0.5.1" -"@miblanchard/react-native-slider@^2.3.1": - 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== +"@miblanchard/react-native-slider@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@miblanchard/react-native-slider/-/react-native-slider-2.6.0.tgz#9f78c805d637ffaff0e3e7429932d2995a67edc9" + integrity sha512-o7hk/f/8vkqh6QNR5L52m+ws846fQeD/qNCC9CCSRdBqjq66KiCgbxzlhRzKM/gbtxcvMYMIEEJ1yes5cr6I3A== -"@mozzius/expo-dynamic-app-icon@^1.5.0": +"@mozzius/expo-dynamic-app-icon@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@mozzius/expo-dynamic-app-icon/-/expo-dynamic-app-icon-1.5.0.tgz#c5f88c309965b6d6b89cfd5e2c00faa7bda736af" integrity sha512-yE2yEPO+HQmOqsX7cECh7/vu/LXnqhHGsVm3UiVi/3gaK8u5hAkPTNzZ0Qu6vnMwjPnY+uFbN6X+6Aj9c9yjMQ== @@ -6141,23 +6146,23 @@ resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-2.11.0.tgz#4587fbce6a382adedad74311e96ee10bb2b2d63a" integrity sha512-QuZU6gbxmOID5zZgd/H90NgBnbJ3VV6qVzp6c7/dDrmWdX8S0X5YFYgDcQFjE3dRen9wB9FWnj2VVdPU64adSg== -"@react-native/assets-registry@0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.79.2.tgz#731963e664c8543f5b277e56c058bde612b69f50" - integrity sha512-5h2Z7/+/HL/0h88s0JHOdRCW4CXMCJoROxqzHqxdrjGL6EBD1DdaB4ZqkCOEVSW4Vjhir5Qb97C8i/MPWEYPtg== +"@react-native/assets-registry@0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.79.3.tgz#022218d55a5d9d221a6d176987ab0b35c10d388b" + integrity sha512-Vy8DQXCJ21YSAiHxrNBz35VqVlZPpRYm50xRTWRf660JwHuJkFQG8cUkrLzm7AUriqUXxwpkQHcY+b0ibw9ejQ== -"@react-native/babel-plugin-codegen@0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.79.2.tgz#f3f86766a01487aaaa623ec62514af4c84400953" - integrity sha512-d+NB7Uosn2ZWd4O4+7ZkB6q1a+0z2opD/4+Bzhk/Tv6fc5FrSftK2Noqxvo3/bhbdGFVPxf0yvLE8et4W17x/Q== +"@react-native/babel-plugin-codegen@0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.79.3.tgz#acad4acaead398a8c8bcdecbe44040aa0c2dc2d7" + integrity sha512-Zb8F4bSEKKZfms5n1MQ0o5mudDcpAINkKiFuFTU0PErYGjY3kZ+JeIP+gS6KCXsckxCfMEKQwqKicP/4DWgsZQ== dependencies: "@babel/traverse" "^7.25.3" - "@react-native/codegen" "0.79.2" + "@react-native/codegen" "0.79.3" -"@react-native/babel-preset@0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.79.2.tgz#5a683a6efeea357a326f70c84a881be2bafbeae3" - integrity sha512-/HNu869oUq4FUXizpiNWrIhucsYZqu0/0spudJEzk9SEKar0EjVDP7zkg/sKK+KccNypDQGW7nFXT8onzvQ3og== +"@react-native/babel-preset@0.79.2", "@react-native/babel-preset@0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.79.3.tgz#8ad6c149cd488fbc18d62983119bdcbfc15ff651" + integrity sha512-VHGNP02bDD2Ul1my0pLVwe/0dsEBHxR343ySpgnkCNEEm9C1ANQIL2wvnJrHZPcqfAkWfFQ8Ln3t+6fdm4A/Dg== dependencies: "@babel/core" "^7.25.2" "@babel/plugin-proposal-export-default-from" "^7.24.7" @@ -6200,15 +6205,15 @@ "@babel/plugin-transform-typescript" "^7.25.2" "@babel/plugin-transform-unicode-regex" "^7.24.7" "@babel/template" "^7.25.0" - "@react-native/babel-plugin-codegen" "0.79.2" + "@react-native/babel-plugin-codegen" "0.79.3" babel-plugin-syntax-hermes-parser "0.25.1" babel-plugin-transform-flow-enums "^0.0.2" react-refresh "^0.14.0" -"@react-native/codegen@0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/codegen/-/codegen-0.79.2.tgz#75270d8162e78c02b0272396a3c6942e39e8703d" - integrity sha512-8JTlGLuLi1p8Jx2N/enwwEd7/2CfrqJpv90Cp77QLRX3VHF2hdyavRIxAmXMwN95k+Me7CUuPtqn2X3IBXOWYg== +"@react-native/codegen@0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/codegen/-/codegen-0.79.3.tgz#49689132718c81a3b25426769bc6fd8fd2a0469f" + integrity sha512-CZejXqKch/a5/s/MO5T8mkAgvzCXgsTkQtpCF15kWR9HN8T+16k0CsN7TXAxXycltoxiE3XRglOrZNEa/TiZUQ== dependencies: glob "^7.1.1" hermes-parser "0.25.1" @@ -6216,12 +6221,12 @@ nullthrows "^1.1.1" yargs "^17.6.2" -"@react-native/community-cli-plugin@0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.79.2.tgz#d3a0efbdfb554cf3a7e9bfb27865a7caeeeaa1b3" - integrity sha512-E+YEY2dL+68HyR2iahsZdyBKBUi9QyPyaN9vsnda1jNgCjNpSPk2yAF5cXsho+zKK5ZQna3JSeE1Kbi2IfGJbw== +"@react-native/community-cli-plugin@0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.79.3.tgz#84821d3401074e036ba05b8b6ca1ee122cb43e29" + integrity sha512-N/+p4HQqN4yK6IRzn7OgMvUIcrmEWkecglk1q5nj+AzNpfIOzB+mqR20SYmnPfeXF+mZzYCzRANb3KiM+WsSDA== dependencies: - "@react-native/dev-middleware" "0.79.2" + "@react-native/dev-middleware" "0.79.3" chalk "^4.0.0" debug "^2.2.0" invariant "^2.2.4" @@ -6230,18 +6235,18 @@ metro-core "^0.82.0" semver "^7.1.3" -"@react-native/debugger-frontend@0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/debugger-frontend/-/debugger-frontend-0.79.2.tgz#1377de6d9cabe5455bf332e06408167da5f60c19" - integrity sha512-cGmC7X6kju76DopSBNc+PRAEetbd7TWF9J9o84hOp/xL3ahxR2kuxJy0oJX8Eg8oehhGGEXTuMKHzNa3rDBeSg== +"@react-native/debugger-frontend@0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/debugger-frontend/-/debugger-frontend-0.79.3.tgz#9cb57d8e88c22552194ab5f6f257605b151bc5b3" + integrity sha512-ImNDuEeKH6lEsLXms3ZsgIrNF94jymfuhPcVY5L0trzaYNo9ZFE9Ni2/18E1IbfXxdeIHrCSBJlWD6CTm7wu5A== -"@react-native/dev-middleware@0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/dev-middleware/-/dev-middleware-0.79.2.tgz#f09f1a75b4cd0b56dfd82a07bf41157a9c45619c" - integrity sha512-9q4CpkklsAs1L0Bw8XYCoqqyBSrfRALGEw4/r0EkR38Y/6fVfNfdsjSns0pTLO6h0VpxswK34L/hm4uK3MoLHw== +"@react-native/dev-middleware@0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/dev-middleware/-/dev-middleware-0.79.3.tgz#3e315ef7516ebad60a4202b4094d84fedecb4064" + integrity sha512-x88+RGOyG71+idQefnQg7wLhzjn/Scs+re1O5vqCkTVzRAc/f7SdHMlbmECUxJPd08FqMcOJr7/X3nsJBrNuuw== dependencies: "@isaacs/ttlcache" "^1.4.1" - "@react-native/debugger-frontend" "0.79.2" + "@react-native/debugger-frontend" "0.79.3" chrome-launcher "^0.15.2" chromium-edge-launcher "^0.2.0" connect "^3.6.5" @@ -6252,14 +6257,14 @@ serve-static "^1.16.2" ws "^6.2.3" -"@react-native/eslint-config@^0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/eslint-config/-/eslint-config-0.79.2.tgz#b42c95fe2399aae84209356b0971dd68c4149e4e" - integrity sha512-ukb9qGvrFC/3YVlWVy/GGM+auKdGIsbbumCyfOYPfUdhHFWA/twz1zK4bJQmCZ38iINuTENYedRzoE+U57GclA== +"@react-native/eslint-config@^0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/eslint-config/-/eslint-config-0.79.3.tgz#a72352ce98a7d05b5686dc446ba933fee3b944a9" + integrity sha512-pvPXfXFoVfwqGEItIl7emv6bRTpi0NWv4jmb0ZBwSJ7+zM8bRRw7JwxLA+iwrevh0QAqrF66Nu5xl39ysWcavg== dependencies: "@babel/core" "^7.25.2" "@babel/eslint-parser" "^7.25.1" - "@react-native/eslint-plugin" "0.79.2" + "@react-native/eslint-plugin" "0.79.3" "@typescript-eslint/eslint-plugin" "^7.1.1" "@typescript-eslint/parser" "^7.1.1" eslint-config-prettier "^8.5.0" @@ -6270,35 +6275,35 @@ eslint-plugin-react-hooks "^4.6.0" eslint-plugin-react-native "^4.0.0" -"@react-native/eslint-plugin@0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/eslint-plugin/-/eslint-plugin-0.79.2.tgz#23d18226bb4335404e6db561bf1a47ac7d1380ed" - integrity sha512-Abu+0OuwTje9E5eQOvYpTUuXvDgGjHeFhnfNVY9BXalBK8OrX20EFlonWvIbFqii136eS5KLEBm2Wjqk2V5XJg== +"@react-native/eslint-plugin@0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/eslint-plugin/-/eslint-plugin-0.79.3.tgz#c1ac34d45b92f963b9af66e860c45cd891235741" + integrity sha512-6QZzCsV+Wc+HdOAMMoMqDea3SSzsvBBktGc/cqaLubKGiztTb22d+vtzZGWqCqUEVkhQKdK7qhWAs0kJPEbiXw== -"@react-native/gradle-plugin@0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.79.2.tgz#d41d4e2c63baf688a2b47652c6260f2a2f1ec091" - integrity sha512-6MJFemrwR0bOT0QM+2BxX9k3/pvZQNmJ3Js5pF/6owsA0cUDiCO57otiEU8Fz+UywWEzn1FoQfOfQ8vt2GYmoA== +"@react-native/gradle-plugin@0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.79.3.tgz#69ba47ac406ccdb3b3829f311bd7c27e6fad7ebc" + integrity sha512-imfpZLhNBc9UFSzb/MOy2tNcIBHqVmexh/qdzw83F75BmUtLb/Gs1L2V5gw+WI1r7RqDILbWk7gXB8zUllwd+g== -"@react-native/js-polyfills@0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/js-polyfills/-/js-polyfills-0.79.2.tgz#15eb4da0fe9e8d61d2980d08fd06b5f49e133b0f" - integrity sha512-IaY87Ckd4GTPMkO1/Fe8fC1IgIx3vc3q9Tyt/6qS3Mtk9nC0x9q4kSR5t+HHq0/MuvGtu8HpdxXGy5wLaM+zUw== +"@react-native/js-polyfills@0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/js-polyfills/-/js-polyfills-0.79.3.tgz#bf5614363f118c6bdf2f773c578e603c88d0425c" + integrity sha512-PEBtg6Kox6KahjCAch0UrqCAmHiNLEbp2SblUEoFAQnov4DSxBN9safh+QSVaCiMAwLjvNfXrJyygZz60Dqz3Q== -"@react-native/normalize-colors@0.79.2", "@react-native/normalize-colors@^0.73.0", "@react-native/normalize-colors@^0.74.1": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.79.2.tgz#9ab70ca257c7411e4ab74cf7f91332c27d39cc6f" - integrity sha512-+b+GNrupWrWw1okHnEENz63j7NSMqhKeFMOyzYLBwKcprG8fqJQhDIGXfizKdxeIa5NnGSAevKL1Ev1zJ56X8w== +"@react-native/normalize-colors@0.79.2", "@react-native/normalize-colors@0.79.3", "@react-native/normalize-colors@^0.73.0", "@react-native/normalize-colors@^0.74.1": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.79.3.tgz#e491937436a2c287707e24263308c818a66eb447" + integrity sha512-T75NIQPRFCj6DFMxtcVMJTZR+3vHXaUMSd15t+CkJpc5LnyX91GVaPxpRSAdjFh7m3Yppl5MpdjV/fntImheYQ== -"@react-native/typescript-config@^0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/typescript-config/-/typescript-config-0.79.2.tgz#02cb07db89ef80159b3c1b3e82e81b0c0d5ce908" - integrity sha512-krHAkkPRCOEhuqN3iwRUwIyE1rAnUQ9//huzUc1ukcoQ7Y4qFxM6amhNloAmYn4QH1Ay6o5At00VbEh2xoHISA== +"@react-native/typescript-config@^0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/typescript-config/-/typescript-config-0.79.3.tgz#21101b591c67ecef6ae5189f96a448b3bcaf1e9e" + integrity sha512-dqKAU8D3NkExthnpBOPZjZ/NGU5qqBaqZ12v9IlMqP9sVTWSuY1iswfzomp0AYeJBxo4ZpBNmrdlnQZYQSVWiw== -"@react-native/virtualized-lists@0.79.2": - version "0.79.2" - resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.79.2.tgz#ed5a419a30b7ddec978b7816ff698a9d85507e15" - integrity sha512-9G6ROJeP+rdw9Bvr5ruOlag11ET7j1z/En1riFFNo6W3xZvJY+alCuH1ttm12y9+zBm4n8jwCk4lGhjYaV4dKw== +"@react-native/virtualized-lists@0.79.3": + version "0.79.3" + resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.79.3.tgz#4a2799017cd3795f519422f48b3c0bbc4739a245" + integrity sha512-/0rRozkn+iIHya2vnnvprDgT7QkfI54FLrACAN3BLP7MRlfOIGOrZsXpRLndnLBVnjNzkcre84i1RecjoXnwIA== dependencies: invariant "^2.2.4" nullthrows "^1.1.1" @@ -6412,6 +6417,11 @@ resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.2.2.tgz#0c5f26e417b8f524924fa4531b82ad5603216e90" integrity sha512-D+SKQ266ra/wo87s9+UI/rKQi3qhGPCR8eSCDe0VJudhjHsqyNU+JJ5lnIGCgmZaWFTXgdBP/gdr1Iz1zqGs4Q== +"@sentry/babel-plugin-component-annotate@3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.4.0.tgz#f47a7652e16f84556df82cbc38f0004bca1335d1" + integrity sha512-tSzfc3aE7m0PM0Aj7HBDet5llH9AB9oc+tBQ8AvOqUSnWodLrNCuWeQszJ7mIBovD3figgCU3h0cvI6U5cDtsg== + "@sentry/browser@8.54.0": version "8.54.0" resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.54.0.tgz#5487075908aac564892e689e1b6d233fdb314f5b" @@ -6442,70 +6452,75 @@ resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.42.2.tgz#a32a4f226e717122b37d9969e8d4d0e14779f720" integrity sha512-GtJSuxER7Vrp1IpxdUyRZzcckzMnb4N5KTW7sbTwUiwqARRo+wxS+gczYrS8tdgtmXs5XYhzhs+t4d52ITHMIg== -"@sentry/cli-darwin@2.42.4": - version "2.42.4" - resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.42.4.tgz#029521d3052c644e3bac1c926e53d1e658b8cb28" - integrity sha512-PZV4Y97VDWBR4rIt0HkJfXaBXlebIN2s/FDzC3iHINZE5OG62CDFsnC4/lbGlf2/UZLDaGGIK7mYwSHhTvN+HQ== +"@sentry/cli-darwin@2.45.0": + version "2.45.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.45.0.tgz#e3d6feae4fadcfdf91db9c7b9c4689a66d3d8d19" + integrity sha512-p4Uxfv/L2fQdP3/wYnKVVz9gzZJf/1Xp9D+6raax/3Bu5y87yHYUqcdt98y/VAXQD4ofp2QgmhGUVPofvQNZmg== "@sentry/cli-linux-arm64@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.42.2.tgz#1c06c83ff21f51ec23acf5be3b1f8c7553bf86b1" integrity sha512-BOxzI7sgEU5Dhq3o4SblFXdE9zScpz6EXc5Zwr1UDZvzgXZGosUtKVc7d1LmkrHP8Q2o18HcDWtF3WvJRb5Zpw== -"@sentry/cli-linux-arm64@2.42.4": - version "2.42.4" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.42.4.tgz#b5e2d2399764998e3d661f144aae0c3f3495d1f1" - integrity sha512-Ex8vRnryyzC/9e43daEmEqPS+9uirY/l6Hw2lAvhBblFaL7PTWNx52H+8GnYGd9Zy2H3rWNyBDYfHwnErg38zA== +"@sentry/cli-linux-arm64@2.45.0": + version "2.45.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.45.0.tgz#384c8e17f7e7dc007d164033d0e7c75aa83a2e9b" + integrity sha512-gUcLoEjzg7AIc4QQGEZwRHri+EHf3Gcms9zAR1VHiNF3/C/jL4WeDPJF2YiWAQt6EtH84tHiyhw1Ab/R8XFClg== "@sentry/cli-linux-arm@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.42.2.tgz#00cadc359ae3c051efb3e63873c033c61dbd1ca1" integrity sha512-7udCw+YL9lwq+9eL3WLspvnuG+k5Icg92YE7zsteTzWLwgPVzaxeZD2f8hwhsu+wmL+jNqbpCRmktPteh3i2mg== -"@sentry/cli-linux-arm@2.42.4": - version "2.42.4" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.42.4.tgz#286996c3969a553c07a74a2a67c6d3671e2c79b5" - integrity sha512-lBn0oeeg62h68/4Eo6zbPq99Idz5t0VRV48rEU/WKeM4MtQCvG/iGGQ3lBFW2yNiUBzXZIK9poXLEcgbwmcRVw== +"@sentry/cli-linux-arm@2.45.0": + version "2.45.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.45.0.tgz#b9d6f86f3934b4d9ced5b45a8158ff2ac2bdd25d" + integrity sha512-6sEskFLlFKJ+e0MOYgIclBTUX5jYMyYhHIxXahEkI/4vx6JO0uvpyRAkUJRpJkRh/lPog0FM+tbP3so+VxB2qQ== "@sentry/cli-linux-i686@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.42.2.tgz#3b817b715dd806c20dfbffd539725ad8089c310a" integrity sha512-Sw/dQp5ZPvKnq3/y7wIJyxTUJYPGoTX/YeMbDs8BzDlu9to2LWV3K3r7hE7W1Lpbaw4tSquUHiQjP5QHCOS7aQ== -"@sentry/cli-linux-i686@2.42.4": - version "2.42.4" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.42.4.tgz#03e72598dc37e96a99e4329e20db9e74df277f83" - integrity sha512-IBJg0aHjsLCL4LvcFa3cXIjA+4t5kPqBT9y+PoDu4goIFxYD8zl7mbUdGJutvJafTk8Akf4ss4JJXQBjg019zA== +"@sentry/cli-linux-i686@2.45.0": + version "2.45.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.45.0.tgz#39e22beb84cfa26e11bdc198364315fdfb4da4d5" + integrity sha512-VmmOaEAzSW23YdGNdy/+oQjCNAMY+HmOGA77A25/ep/9AV7PQB6FI7xO5Y1PVvlkxZFJ23e373njSsEeg4uDZw== "@sentry/cli-linux-x64@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.42.2.tgz#ddf906bc3071cc79ce6e633eddcb76bb9068e688" integrity sha512-mU4zUspAal6TIwlNLBV5oq6yYqiENnCWSxtSQVzWs0Jyq97wtqGNG9U+QrnwjJZ+ta/hvye9fvL2X25D/RxHQw== -"@sentry/cli-linux-x64@2.42.4": - version "2.42.4" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.42.4.tgz#a3bc31a909f61029620e5d2ae0f8d8625ed8982a" - integrity sha512-gXI5OEiOSNiAEz7VCE6AZcAgHJ47mlgal3+NmbE8XcHmFOnyDws9FNie6PJAy8KZjXi3nqoBP9JVAbnmOix3uA== +"@sentry/cli-linux-x64@2.45.0": + version "2.45.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.45.0.tgz#25cd3699297f9433835fb5edd42dad722c11f041" + integrity sha512-a0Oj68mrb25a0WjX/ShZ6AAd4PPiuLcgyzQr7bl2+DvYxIOajwkGbR+CZFEhOVZcfhTnixKy/qIXEzApEPHPQg== + +"@sentry/cli-win32-arm64@2.45.0": + version "2.45.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.45.0.tgz#50c7d29ea2169bdb4d98bbde81c5f7dac0dd3955" + integrity sha512-vn+CwS4p+52pQSLNPoi20ZOrQmv01ZgAmuMnjkh1oUZfTyBAwWLrAh6Cy4cztcN8DfL5dOWKQBo8DBKURE4ttg== "@sentry/cli-win32-i686@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.42.2.tgz#9036085c7c6ce455ad45fda411c55ff39c06eb95" integrity sha512-iHvFHPGqgJMNqXJoQpqttfsv2GI3cGodeTq4aoVLU/BT3+hXzbV0x1VpvvEhncJkDgDicJpFLM8sEPHb3b8abw== -"@sentry/cli-win32-i686@2.42.4": - version "2.42.4" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.42.4.tgz#0de663fc574f4bce2057e099c5b76b65f99a3bd6" - integrity sha512-vZuR3UPHKqOMniyrijrrsNwn9usaRysXq78F6WV0cL0ZyPLAmY+KBnTDSFk1Oig2pURnzaTm+RtcZu2fc8mlzg== +"@sentry/cli-win32-i686@2.45.0": + version "2.45.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.45.0.tgz#201075c4aec37a3e797160e0b468641245437f0c" + integrity sha512-8mMoDdlwxtcdNIMtteMK7dbi7054jak8wKSHJ5yzMw8UmWxC5thc/gXBc1uPduiaI56VjoJV+phWHBKCD+6I4w== "@sentry/cli-win32-x64@2.42.2": version "2.42.2" resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.42.2.tgz#7d6464b63f32c9f97fff428f246b1f039b402233" integrity sha512-vPPGHjYoaGmfrU7xhfFxG7qlTBacroz5NdT+0FmDn6692D8IvpNXl1K+eV3Kag44ipJBBeR8g1HRJyx/F/9ACw== -"@sentry/cli-win32-x64@2.42.4": - version "2.42.4" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.42.4.tgz#f572a03084f3b1a4f355c1fbeb1339cb56ad91c9" - integrity sha512-OIBj3uaQ6nAERSm5Dcf8UIhyElEEwMNsZEEppQpN4IKl0mrwb/57AznM23Dvpu6GR8WGbVQUSolt879YZR5E9g== +"@sentry/cli-win32-x64@2.45.0": + version "2.45.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.45.0.tgz#2075e9e1ea3c3609e0fa1a758ca033e94e1c600f" + integrity sha512-ZvK9cIqFaq7vZ0jkHJ/xh5au6902Dr+AUxSk6L6vCL7JCe2p93KGL/4d8VFB5PD/P7Y9b+105G/e0QIFKzpeOw== "@sentry/cli@2.42.2": version "2.42.2" @@ -6526,10 +6541,10 @@ "@sentry/cli-win32-i686" "2.42.2" "@sentry/cli-win32-x64" "2.42.2" -"@sentry/cli@2.42.4": - version "2.42.4" - resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.42.4.tgz#df6ac3e92a60a715b231873433894ed77e601086" - integrity sha512-BoSZDAWJiz/40tu6LuMDkSgwk4xTsq6zwqYoUqLU3vKBR/VsaaQGvu6EWxZXORthfZU2/5Agz0+t220cge6VQw== +"@sentry/cli@2.45.0": + version "2.45.0" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.45.0.tgz#35feed7a2fee54faf25daed73001a2a2a3143396" + integrity sha512-4sWu7zgzgHAjIxIjXUA/66qgeEf5ZOlloO+/JaGD5qXNSW0G7KMTR6iYjReNKMgdBCTH6bUUt9qiuA+Ex9Masw== dependencies: https-proxy-agent "^5.0.0" node-fetch "^2.6.7" @@ -6537,27 +6552,28 @@ proxy-from-env "^1.1.0" which "^2.0.2" optionalDependencies: - "@sentry/cli-darwin" "2.42.4" - "@sentry/cli-linux-arm" "2.42.4" - "@sentry/cli-linux-arm64" "2.42.4" - "@sentry/cli-linux-i686" "2.42.4" - "@sentry/cli-linux-x64" "2.42.4" - "@sentry/cli-win32-i686" "2.42.4" - "@sentry/cli-win32-x64" "2.42.4" + "@sentry/cli-darwin" "2.45.0" + "@sentry/cli-linux-arm" "2.45.0" + "@sentry/cli-linux-arm64" "2.45.0" + "@sentry/cli-linux-i686" "2.45.0" + "@sentry/cli-linux-x64" "2.45.0" + "@sentry/cli-win32-arm64" "2.45.0" + "@sentry/cli-win32-i686" "2.45.0" + "@sentry/cli-win32-x64" "2.45.0" "@sentry/core@8.54.0": version "8.54.0" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.54.0.tgz#a2ebec965cadcb6de89e116689feeef79d5862a6" integrity sha512-03bWf+D1j28unOocY/5FDB6bUHtYlm6m6ollVejhg45ZmK9iPjdtxNWbrLsjT1WRym0Tjzowu+A3p+eebYEv0Q== -"@sentry/react-native@~6.10.0": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@sentry/react-native/-/react-native-6.10.0.tgz#9efafb9b85870bd4c5189763edde30709b9f3213" - integrity sha512-B56vc+pnFHMiu3cabFb454v4qD0zObW6JVzJ5Gb6fIMdt93AFIJg10ZErzC+ump7xM4BOEROFFRuLiyvadvlPA== +"@sentry/react-native@~6.14.0": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@sentry/react-native/-/react-native-6.14.0.tgz#bc6bdaf03860bb8946f8c30570a9abd82ed6cfc0" + integrity sha512-BBqixN6oV6tCNp1ABXfzvD531zxj1fUAH0HDPvOR/jX0h9f9pYfxCyI64B+DoQbVZKFsg8nte0QIHkZDhRAW9A== dependencies: - "@sentry/babel-plugin-component-annotate" "3.2.2" + "@sentry/babel-plugin-component-annotate" "3.4.0" "@sentry/browser" "8.54.0" - "@sentry/cli" "2.42.4" + "@sentry/cli" "2.45.0" "@sentry/core" "8.54.0" "@sentry/react" "8.54.0" "@sentry/types" "8.54.0" @@ -8731,6 +8747,33 @@ babel-preset-expo@~13.1.11: react-refresh "^0.14.2" resolve-from "^5.0.0" +babel-preset-expo@~13.2.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-13.2.0.tgz#d4540009d07242e3c3d63184b7a34efda95e8e64" + integrity sha512-oNUeUZPMNRPmx/2jaKJLSQFP/MFI1M91vP+Gp+j8/FPl9p/ps603DNwCaRdcT/Vj3FfREdlIwRio1qDCjY0oAA== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/plugin-proposal-decorators" "^7.12.9" + "@babel/plugin-proposal-export-default-from" "^7.24.7" + "@babel/plugin-syntax-export-default-from" "^7.24.7" + "@babel/plugin-transform-export-namespace-from" "^7.25.9" + "@babel/plugin-transform-flow-strip-types" "^7.25.2" + "@babel/plugin-transform-modules-commonjs" "^7.24.8" + "@babel/plugin-transform-object-rest-spread" "^7.24.7" + "@babel/plugin-transform-parameters" "^7.24.7" + "@babel/plugin-transform-private-methods" "^7.24.7" + "@babel/plugin-transform-private-property-in-object" "^7.24.7" + "@babel/plugin-transform-runtime" "^7.24.7" + "@babel/preset-react" "^7.22.15" + "@babel/preset-typescript" "^7.23.0" + "@react-native/babel-preset" "0.79.3" + babel-plugin-react-native-web "~0.19.13" + babel-plugin-syntax-hermes-parser "^0.25.1" + babel-plugin-transform-flow-enums "^0.0.2" + debug "^4.3.4" + react-refresh "^0.14.2" + resolve-from "^5.0.0" + babel-preset-jest@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" @@ -8749,11 +8792,6 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base-64@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" - integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== - base64-arraybuffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" @@ -8960,6 +8998,16 @@ browserslist@^4.24.0, browserslist@^4.24.2: node-releases "^2.0.18" update-browserslist-db "^1.1.1" +browserslist@^4.25.0: + version "4.25.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.0.tgz#986aa9c6d87916885da2b50d8eb577ac8d133b2c" + integrity sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA== + dependencies: + caniuse-lite "^1.0.30001718" + electron-to-chromium "^1.5.160" + node-releases "^2.0.19" + update-browserslist-db "^1.1.3" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -9102,6 +9150,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001587, can resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001697.tgz" integrity sha512-GwNPlWJin8E+d7Gxq96jxM6w0w+VFeyyXRsjU58emtkYqnbwHqXm5uT2uCmO0RQE9htWknOP4xtBlLmM/gWxvQ== +caniuse-lite@^1.0.30001718: + version "1.0.30001722" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001722.tgz#ec25a2b3085b25b9079b623db83c22a70882ce85" + integrity sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA== + cbor-extract@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/cbor-extract/-/cbor-extract-2.1.1.tgz#f154b31529fdb6b7c70fb3ca448f44eda96a1b42" @@ -10340,6 +10393,11 @@ electron-to-chromium@^1.4.668: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.777.tgz#f846fbba23fd11b3c6f97848cdda94896fdb8baf" integrity sha512-n02NCwLJ3wexLfK/yQeqfywCblZqLcXphzmid5e8yVPdtEcida7li0A5WQKghHNG0FeOMCzeFOzEbtAh5riXFw== +electron-to-chromium@^1.5.160: + version "1.5.166" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.166.tgz#3fff386ed473cc2169dbe2d3ace9592262601114" + integrity sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw== + electron-to-chromium@^1.5.41: version "1.5.51" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.51.tgz#bb99216fed4892d131a8585a8593b00739310163" @@ -11146,18 +11204,18 @@ expo-application@~6.1.4: resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-6.1.4.tgz#34ee2f7a86e3689f15961b296e82934e0f85afd6" integrity sha512-jXVZb3llTQ5j4C/I03GxKjujmhKex9Xo5JDZo/pRjScHSr4NoeMjPKWThyWVlWDM1v5YSEcsRJebVfTvq9SR5Q== -expo-asset@~11.1.4: - version "11.1.4" - resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-11.1.4.tgz#0258156f76c306521eb2a0d27e98d26258d12ad9" - integrity sha512-e3210sF0YHKRTCjVUOVmDAJ0Dk4vepL9RocKe36S7S+VthoCZwsBGLAM2LLvBa1SdmODF92AS0Nrcfi/1/VlbQ== +expo-asset@~11.1.5: + version "11.1.5" + resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-11.1.5.tgz#5cad3d781c9d0edec31b9b3adbba574eb4d5dd3e" + integrity sha512-GEQDCqC25uDBoXHEnXeBuwpeXvI+3fRGvtzwwt0ZKKzWaN+TgeF8H7c76p3Zi4DfBMFDcduM0CmOvJX+yCCLUQ== dependencies: "@expo/image-utils" "^0.7.4" - expo-constants "~17.1.4" + expo-constants "~17.1.5" -expo-blur@~14.1.4: - version "14.1.4" - resolved "https://registry.yarnpkg.com/expo-blur/-/expo-blur-14.1.4.tgz#d246c0a224ce63321d022edfc0e6a8c5fa2cc865" - integrity sha512-55P9tK/RjJZEcu2tU7BqX3wmIOrGMOOkmHztJMMws+ZGHzvtjnPmT7dsQxhOU9vPj77oHnKetYHU2sik3iBcCw== +expo-blur@~14.1.5: + version "14.1.5" + resolved "https://registry.yarnpkg.com/expo-blur/-/expo-blur-14.1.5.tgz#910712389e19286ccdc136275bf569f427aa05ef" + integrity sha512-CCLJHxN4eoAl06ESKT3CbMasJ98WsjF9ZQEJnuxtDb9ffrYbZ+g9ru84fukjNUOTtc8A8yXE5z8NgY1l0OMrmQ== expo-build-properties@~0.14.6: version "0.14.6" @@ -11167,10 +11225,10 @@ expo-build-properties@~0.14.6: ajv "^8.11.0" semver "^7.6.0" -expo-camera@~16.1.6: - version "16.1.6" - resolved "https://registry.yarnpkg.com/expo-camera/-/expo-camera-16.1.6.tgz#9badbc3b93cab3386e3e70721d4f3c1983ab539c" - integrity sha512-caVSfoTUaayYhH5gicrXWCgBQIVrotPOH3jUDr4vhN5VQDB/+TWaY+le2nQtNXgQEz14Af+H/TNvYpvvNj5Ktg== +expo-camera@~16.1.8: + version "16.1.8" + resolved "https://registry.yarnpkg.com/expo-camera/-/expo-camera-16.1.8.tgz#6c30dfb5c982795351f1053c36f048a11869e21b" + integrity sha512-NpBbkUhHG6cs2TNUQBFSEtXb5j1/kTPIhiuqBcHosZG2yb/8MuM/ii4McJaqfe/6pn0YPqkH4k0Uod11DOSLmw== dependencies: invariant "^2.2.4" @@ -11179,7 +11237,7 @@ expo-clipboard@~7.1.4: resolved "https://registry.yarnpkg.com/expo-clipboard/-/expo-clipboard-7.1.4.tgz#f2cda0d3cbfd2d307aa85dd7ba6843d6bbaf4227" integrity sha512-NHhfKnrzb4o0PacUKD93ByadU0JmPBoFTFYbbFJZ9OAX6SImpSqG5gfrMUR3vVj4Qx9f1LpMcdAv5lBzv868ow== -expo-constants@17.0.3, expo-constants@^13.0.2, expo-constants@~17.1.4, expo-constants@~17.1.5: +expo-constants@17.0.3, expo-constants@^13.0.2, expo-constants@~17.1.5, expo-constants@~17.1.6: version "17.0.3" resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-17.0.3.tgz#a05b38e0417d59759ece1642b4d483889e04dbda" integrity sha512-lnbcX2sAu8SucHXEXxSkhiEpqH+jGrf+TF+MO6sHWIESjwOUVVYlT8qYdjR9xbxWmqFtrI4KV44FkeJf2DaFjQ== @@ -11187,25 +11245,25 @@ expo-constants@17.0.3, expo-constants@^13.0.2, expo-constants@~17.1.4, expo-cons "@expo/config" "~10.0.4" "@expo/env" "~0.4.0" -expo-dev-client@~5.1.7: - version "5.1.7" - resolved "https://registry.yarnpkg.com/expo-dev-client/-/expo-dev-client-5.1.7.tgz#b75d5c4650a2b19e8d5c4666a798a4150a7fbd68" - integrity sha512-/xcwNIeZIBA/y6Io7jv1ZbEG8XRUuAynIJyIGJvpMxf6hm7eEw8rEzhO9rZNk6H8bMjTjASs0Vf1bqIV6v3j6A== +expo-dev-client@~5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/expo-dev-client/-/expo-dev-client-5.2.0.tgz#a3d4f8c79fafe747ea8d0bfc6e3918b02011025c" + integrity sha512-7GgO3BGlFM016Zkp3c9bUbi35pubqKh8Z/iHC1arIvckEjDrLER+92zfUTFr49XLk2o64arItRPJyQL49pA/hg== dependencies: - expo-dev-launcher "5.1.10" - expo-dev-menu "6.1.9" + expo-dev-launcher "5.1.12" + expo-dev-menu "6.1.11" expo-dev-menu-interface "1.10.0" - expo-manifests "~0.16.4" + expo-manifests "~0.16.5" expo-updates-interface "~1.1.0" -expo-dev-launcher@5.1.10: - version "5.1.10" - resolved "https://registry.yarnpkg.com/expo-dev-launcher/-/expo-dev-launcher-5.1.10.tgz#eaa8a7a4edcab557e6623f176e60d964606651ff" - integrity sha512-OW4k0efB6cWigYj1GlJGObMuMpg6DIwsAZkECsGqNU3U80zE7pBMPB0sy1xehFuTplO6F+dCTLg0hPPuqkSsTg== +expo-dev-launcher@5.1.12: + version "5.1.12" + resolved "https://registry.yarnpkg.com/expo-dev-launcher/-/expo-dev-launcher-5.1.12.tgz#cad239327e258d84c4221a212ecd30a051f9097d" + integrity sha512-ALedYerjJtSiPa95l41zMAO/m1m1kgS39i2H0io+6Ix4OksYNhILNzMNB1qDht/oWt2yjLBvXfWULfs5+3vnaA== dependencies: ajv "8.11.0" - expo-dev-menu "6.1.8" - expo-manifests "~0.16.4" + expo-dev-menu "6.1.11" + expo-manifests "~0.16.5" resolve-from "^5.0.0" expo-dev-menu-interface@1.10.0: @@ -11213,28 +11271,14 @@ expo-dev-menu-interface@1.10.0: resolved "https://registry.yarnpkg.com/expo-dev-menu-interface/-/expo-dev-menu-interface-1.10.0.tgz#04671bda3c163d1d7b9438ce7095c3913a3f53f9" integrity sha512-NxtM/qot5Rh2cY333iOE87dDg1S8CibW+Wu4WdLua3UMjy81pXYzAGCZGNOeY7k9GpNFqDPNDXWyBSlk9r2pBg== -expo-dev-menu@6.1.8: - version "6.1.8" - resolved "https://registry.yarnpkg.com/expo-dev-menu/-/expo-dev-menu-6.1.8.tgz#cec53379b76f5cb53e2e87a8c62555610a26e6bb" - integrity sha512-i8DW1OXvj4yxQuPP8p2AMGoYdnyhfTkoqWI/AiDYReh2viZv4kZlRloGAemV2bIQwswMq2GsvZehYrQPEK9QBw== +expo-dev-menu@6.1.11: + version "6.1.11" + resolved "https://registry.yarnpkg.com/expo-dev-menu/-/expo-dev-menu-6.1.11.tgz#829118326bcd618aec3941cfbb5b32fd3dd72379" + integrity sha512-yrlDXGcqlbQX3Pgw/iPLRea7+pHFC17MdtkNaXYQ5K5u64mn9l4KZ2ZYUeQ8cKDG5l8ZdC4F9R9vfCJYFi82AA== dependencies: expo-dev-menu-interface "1.10.0" -expo-dev-menu@6.1.9: - version "6.1.9" - resolved "https://registry.yarnpkg.com/expo-dev-menu/-/expo-dev-menu-6.1.9.tgz#897dcb4c49aed9c1f871b13e1359079f23981050" - integrity sha512-Uz02Bsc1xsYzjW4Ld+PxWLRNkbsoJYSbQtw/pZDSrJk5Hj869M4KQSOI8JpZ7WVlKEKkIRA8kBLepjhdFhq+Dg== - dependencies: - expo-dev-menu-interface "1.10.0" - -expo-device@7.0.1, expo-device@~4.1.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/expo-device/-/expo-device-7.0.1.tgz#3702fe8b4475eac63ed27f9d580ec8a78546e0d1" - integrity sha512-/3lk0f9wvle+6svHqWSCBC1B5NYFmXp1D7hmIyecJJVYRLwzrwwTDyNs76oG/UDU5Appdu8QyDKycsx2hqv71w== - dependencies: - ua-parser-js "^0.7.33" - -expo-device@~7.1.4: +expo-device@7.1.4, expo-device@~4.1.1, expo-device@~7.1.4: version "7.1.4" resolved "https://registry.yarnpkg.com/expo-device/-/expo-device-7.1.4.tgz#84ae7c2520cc45f15a9cb0433ae1226c33f7a8ef" integrity sha512-HS04IiE1Fy0FRjBLurr9e5A6yj3kbmQB+2jCZvbSGpsjBnCLdSk/LCii4f5VFhPIBWJLyYuN5QqJyEAw6BcS4Q== @@ -11246,15 +11290,15 @@ expo-eas-client@~0.14.3: resolved "https://registry.yarnpkg.com/expo-eas-client/-/expo-eas-client-0.14.3.tgz#3fc22378cc454953ecba88f70c16c20a74e0aa27" integrity sha512-BW2mSNEjFRFC8/CbkMQ3mfVhBdeZIjZhNfncw7PP80xEptLWhVjGTqwG8Usi0/yPpIu/YNYgop+XGMfhXyh9uA== -expo-file-system@~18.1.8: - version "18.1.8" - resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-18.1.8.tgz#caa0831b9826f568be36deb25aed835978e957b3" - integrity sha512-1HXpunpRMGnoIw0+f2urjUNaePAvac1X9wIwVRsGJTw7A2WHBFATRuFB7jUOhZac/qK1MDm0GZsggzoRi1oteQ== +expo-file-system@~18.1.10: + version "18.1.10" + resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-18.1.10.tgz#22f3bcc2c9a7edcd6bba5ece3c90a8467fda47be" + integrity sha512-SyaWg+HitScLuyEeSG9gMSDT0hIxbM9jiZjSBP9l9zMnwZjmQwsusE6+7qGiddxJzdOhTP4YGUfvEzeeS0YL3Q== -expo-font@~13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-13.3.0.tgz#139e6e1024e414afe1180ffed0add98aa8c8950b" - integrity sha512-TdbHoxCfLWN9Uvnqsrcak+5EkDCbNIWfgtNWx3JZ6sD9WYB7gvbS+Eu5YlZ85NvCOSJ9Khmw4mFQxEi2LUPZfQ== +expo-font@~13.3.1: + version "13.3.1" + resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-13.3.1.tgz#ed69ae14f263a4c447efb2615b60d9e045372e68" + integrity sha512-d+xrHYvSM9WB42wj8vP9OOFWyxed5R1evphfDb6zYBmC1dA9Hf89FpT7TNFtj2Bk3clTnpmVqQTCYbbA2P3CLg== dependencies: fontfaceobserver "^2.1.0" @@ -11273,10 +11317,10 @@ expo-image-loader@~5.1.0: resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-5.1.0.tgz#f7d65f9b9a9714eaaf5d50a406cb34cb25262153" integrity sha512-sEBx3zDQIODWbB5JwzE7ZL5FJD+DK3LVLWBVJy6VzsqIA6nDEnSFnsnWyCfCTSvbGigMATs1lgkC2nz3Jpve1Q== -expo-image-manipulator@~13.1.5: - version "13.1.5" - resolved "https://registry.yarnpkg.com/expo-image-manipulator/-/expo-image-manipulator-13.1.5.tgz#d657dceeb8ce8da9345a1903f1327b0fedfd08c1" - integrity sha512-V9cGJp0zVwAvAyL3w9JsLH8UEQjQZfFmwJM1l9/oXjlDrSDynQrPFwJq4VI8dCOJ+/nhYZK37yytFNG14Sqt4Q== +expo-image-manipulator@~13.1.7: + version "13.1.7" + resolved "https://registry.yarnpkg.com/expo-image-manipulator/-/expo-image-manipulator-13.1.7.tgz#e891ce9b49d75962eafdf5b7d670116583379e76" + integrity sha512-DBy/Xdd0E/yFind14x36XmwfWuUxOHI/oH97/giKjjPaRc2dlyjQ3tuW3x699hX6gAs9Sixj5WEJ1qNf3c8sag== dependencies: expo-image-loader "~5.1.0" @@ -11287,10 +11331,10 @@ expo-image-picker@~16.1.4: dependencies: expo-image-loader "~5.1.0" -expo-image@~2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-2.1.6.tgz#f046fa631768e37326cc14c49de9113ceffa9e8b" - integrity sha512-AFQxeAI1iTXFZ4dMxUB+SOACGMQxEk+t7PvT3j4mrvffRFoOLfHZ4uJc8SAdDFJak7ByqKhCIPIEhL+1Deq4Sg== +expo-image@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-2.2.1.tgz#b4aa706a25f7e8902ac854a8da249caf4a90cd67" + integrity sha512-5ZSggMi0X2G9AN0aM+sdkCyyZ6YcWvGs9KYLYrRBVUN3ph6RBiu6mKGpaNN1TAscySRnH1eHbUE1H+Qeq7qm1g== expo-json-utils@~0.15.0: version "0.15.0" @@ -11302,17 +11346,17 @@ expo-keep-awake@~14.1.4: resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-14.1.4.tgz#80197728563e0e17523e5a606fbd6fbed9639503" integrity sha512-wU9qOnosy4+U4z/o4h8W9PjPvcFMfZXrlUoKTMBW7F4pLqhkkP/5G4EviPZixv4XWFMjn1ExQ5rV6BX8GwJsWA== -expo-linear-gradient@~14.1.4: - version "14.1.4" - resolved "https://registry.yarnpkg.com/expo-linear-gradient/-/expo-linear-gradient-14.1.4.tgz#fcb2b586cfd21dd5f3076de467f92b55d9c47cb2" - integrity sha512-bImj2qqIjnl+VHYGnIwan9LxmGvb8e4hFqHpxsPzUiK7Ady7uERrXPhJcyTKTxRf4RL2sQRDpoOKzBYNdQDmuw== +expo-linear-gradient@~14.1.5: + version "14.1.5" + resolved "https://registry.yarnpkg.com/expo-linear-gradient/-/expo-linear-gradient-14.1.5.tgz#414bf0c8145089087198d4dd5a419eb324af2a02" + integrity sha512-BSN3MkSGLZoHMduEnAgfhoj3xqcDWaoICgIr4cIYEx1GcHfKMhzA/O4mpZJ/WC27BP1rnAqoKfbclk1eA70ndQ== -expo-linking@~7.1.4: - version "7.1.4" - resolved "https://registry.yarnpkg.com/expo-linking/-/expo-linking-7.1.4.tgz#7d398d99788d8d95b67d1ae463d24686cbeb1c1b" - integrity sha512-zLAbUzTB3+KGjqqLeIdhhkXayyN0qulHGjRI24X7W/0Mq/4oPbPZklKtCP0k7XOn/k4553m8OgJ7GPC03PlV9g== +expo-linking@~7.1.5: + version "7.1.5" + resolved "https://registry.yarnpkg.com/expo-linking/-/expo-linking-7.1.5.tgz#99633892712d5442ddb1c6c3857346eb7a67119b" + integrity sha512-8g20zOpROW78bF+bLI4a3ZWj4ntLgM0rCewKycPL0jk9WGvBrBtFtwwADJgOiV1EurNp3lcquerXGlWS+SOQyA== dependencies: - expo-constants "~17.1.4" + expo-constants "~17.1.6" invariant "^2.2.4" expo-localization@~16.1.5: @@ -11322,23 +11366,23 @@ expo-localization@~16.1.5: dependencies: rtl-detect "^1.0.2" -expo-manifests@~0.16.4: - version "0.16.4" - resolved "https://registry.yarnpkg.com/expo-manifests/-/expo-manifests-0.16.4.tgz#d1a648bab0068a2712cf49009a5f26377a585849" - integrity sha512-zB6ohgnsNbJDaLI/KRZQXxEHadhMJt+gA4LCqbiZQNa3P4FJq4JFRXPV6QQjgjJ998g9vY7eDCTduxTJYBqUaA== +expo-manifests@~0.16.5: + version "0.16.5" + resolved "https://registry.yarnpkg.com/expo-manifests/-/expo-manifests-0.16.5.tgz#bb57ceff3db4eb74679d4a155b2ca2050375ce10" + integrity sha512-zLUeJogn2C7qOE75Zz7jcmJorMfIbSRR35ctspN0OK/Hq/+PAAptA8p9jNVC8xp/91uP9uI8f3xPhh+A11eR2A== dependencies: - "@expo/config" "~11.0.6" + "@expo/config" "~11.0.10" expo-json-utils "~0.15.0" -expo-media-library@~17.1.6: - version "17.1.6" - resolved "https://registry.yarnpkg.com/expo-media-library/-/expo-media-library-17.1.6.tgz#118ada2d28139d540861b338acf1d088bc03d51f" - integrity sha512-Py8Y9wJlNXBZkhtJYy9acj0oRoUV09WXsZnjcvy6xZjpniPQIq0wkIkgS2DLhlYmMdjBrSux2TGp7Omi2WHp1g== +expo-media-library@~17.1.7: + version "17.1.7" + resolved "https://registry.yarnpkg.com/expo-media-library/-/expo-media-library-17.1.7.tgz#71ed3d2d246d33410d6aecc335098a23519bd890" + integrity sha512-hLCoMvlhjtt+iYxPe71P1F6t06mYGysuNOfjQzDbbf64PCkglCZJYmywPyUSV1V5Hu9DhRj//gEg+Ki+7VWXog== -expo-modules-autolinking@2.1.9: - version "2.1.9" - resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-2.1.9.tgz#7bf8338d4b7a1b6e8eccab51634de9b339e90c04" - integrity sha512-54InfnWy1BR54IDZoawqdFAaF2lyLHe9J+2dZ7y91/36jVpBtAval39ZKt2IISFJZ7TVglsojl4P5BDcDGcvjQ== +expo-modules-autolinking@2.1.11: + version "2.1.11" + resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-2.1.11.tgz#efc2e756ccc8b9e0b927596ba074aefe31b5cbe4" + integrity sha512-KrWQo+cE4gWYNePBBhmHGVzf63gYV19ZLXe9EIH3GHTkViVzIX+Lp618H/7GxfawpN5kbhvilATH1QEKKnUUww== dependencies: "@expo/spawn-async" "^1.7.2" chalk "^4.1.0" @@ -11348,10 +11392,10 @@ expo-modules-autolinking@2.1.9: require-from-string "^2.0.2" resolve-from "^5.0.0" -expo-modules-core@2.3.12: - version "2.3.12" - resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-2.3.12.tgz#1c06402564c02b32f192adfe6946e671d8a95e79" - integrity sha512-bOm83mskw1S7xuDX50DlLdx68u0doQ6BZHSU2qTv8P1/5QYeAae3pCgFLq2hoptUNeMF7W+68ShJFTOHAe68BQ== +expo-modules-core@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-2.4.0.tgz#3081d62fadff913090cc5abfe46d9ec6b0e75789" + integrity sha512-Ko5eHBdvuMykjw9P9C9PF54/wBSsGOxaOjx92I5BwgKvEmUwN3UrXFV4CXzlLVbLfSYUQaLcB220xmPfgvT7Fg== dependencies: invariant "^2.2.4" @@ -11362,10 +11406,10 @@ expo-modules-core@^2.1.1: dependencies: invariant "^2.2.4" -expo-notifications@~0.31.1: - version "0.31.1" - resolved "https://registry.yarnpkg.com/expo-notifications/-/expo-notifications-0.31.1.tgz#3be669ab78c6099d82f4fc3607f77f7d9e1805b6" - integrity sha512-g1CMi+3wUaMuX+tAU+Fhnfs5bJtJ3JVRXihO415aOInQkox+Mh79n38RlT6snLWCHjfSojQIxmydzUHt6ywTIQ== +expo-notifications@~0.31.3: + version "0.31.3" + resolved "https://registry.yarnpkg.com/expo-notifications/-/expo-notifications-0.31.3.tgz#eb82c9975e26dcc4fa694b79970792c897ad8d16" + integrity sha512-AATxKoav5ZvwcRel2SKYNZc+EvOAKvAjxyBezC8y3J5fMNe/uKIhMzh3FN4fKdOi9ao/UBHkvLiUO2MqVnvBNg== dependencies: "@expo/image-utils" "^0.7.4" "@ide/backoff" "^1.0.0" @@ -11373,7 +11417,7 @@ expo-notifications@~0.31.1: assert "^2.0.0" badgin "^1.1.5" expo-application "~6.1.4" - expo-constants "~17.1.4" + expo-constants "~17.1.6" expo-pwa@0.0.127: version "0.0.127" @@ -11385,34 +11429,34 @@ expo-pwa@0.0.127: commander "2.20.0" update-check "1.5.3" -expo-screen-orientation@~8.1.5: - version "8.1.5" - resolved "https://registry.yarnpkg.com/expo-screen-orientation/-/expo-screen-orientation-8.1.5.tgz#447c19b8ad59d1d2cf0894900ee273836367ca0d" - integrity sha512-t2ss7a52f1VNrugmIYj1wlnGICUgrkqu2+J3Csew8ixczI8s6r6EXT4E4EeyQ/9WvcEUOOyh8TWC9EbbswPhUw== +expo-screen-orientation@~8.1.7: + version "8.1.7" + resolved "https://registry.yarnpkg.com/expo-screen-orientation/-/expo-screen-orientation-8.1.7.tgz#3751b441f2bfcbde798b1508c0ff9f099f4be911" + integrity sha512-nYwadYtdU6mMDk0MCHMPPPQtBoeFYJ2FspLRW+J35CMLqzE4nbpwGeiImfXzkvD94fpOCfI4KgLj5vGauC3pfA== expo-sharing@~13.1.5: version "13.1.5" resolved "https://registry.yarnpkg.com/expo-sharing/-/expo-sharing-13.1.5.tgz#73d86cdcc037b46ddc82be224dfd3d6bceec497c" integrity sha512-X/5sAEiWXL2kdoGE3NO5KmbfcmaCWuWVZXHu8OQef7Yig4ZgHFkGD11HKJ5KqDrDg+SRZe4ISd6MxE7vGUgm4w== -expo-splash-screen@~0.30.8: - version "0.30.8" - resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.30.8.tgz#2e960ccff053bc8ace85eb56f7d6745e4ddfc6b6" - integrity sha512-2eh+uA543brfeG5HILXmtNKA7E2/pfywKzNumzy3Ef6OtDjYy6zJUGNSbhnZRbVEjUZo3/QNRs0JRBfY80okZg== +expo-splash-screen@~0.30.9: + version "0.30.9" + resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.30.9.tgz#8f2a86b3b802ea46065fc761ed60e77e81bdb84c" + integrity sha512-curHUaZxUTZ2dWvz32ao3xPv5mJr1LBqn5V8xm/IULAehB9RGCn8iKiROMN1PYebSG+56vPMuJmBm9P+ayvJpA== dependencies: - "@expo/prebuild-config" "^9.0.5" + "@expo/prebuild-config" "^9.0.6" expo-structured-headers@~4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/expo-structured-headers/-/expo-structured-headers-4.1.0.tgz#5475fc3f9559701cc755fd2d50605f8817d42ad0" integrity sha512-2X+aUNzC/qaw7/WyUhrVHNDB0uQ5rE12XA2H/rJXaAiYQSuOeU90ladaN0IJYV9I2XlhYrjXLktLXWbO7zgbag== -expo-system-ui@~5.0.7: - version "5.0.7" - resolved "https://registry.yarnpkg.com/expo-system-ui/-/expo-system-ui-5.0.7.tgz#ccf047a689ab488d9bcda375afd063419578f494" - integrity sha512-ijSnSFA4VfuQc84N6WyCUNsKKTIyQb6QuC8q2zGvYC/sBXTMrOtZg0zrisQGzCRW+WhritQTiVqHlp3Ix9xDmQ== +expo-system-ui@~5.0.8: + version "5.0.8" + resolved "https://registry.yarnpkg.com/expo-system-ui/-/expo-system-ui-5.0.8.tgz#1eaaa95cfa8b5e20750e5fb30918635a58276199" + integrity sha512-2sI7ALq3W8sKKa3FRW7PmuNznk+48cb1VzFy96vYZLZgTDZViz+fEJNdp1RHgLui/mAl3f8md1LneygSJvZ1EQ== dependencies: - "@react-native/normalize-colors" "0.79.2" + "@react-native/normalize-colors" "0.79.3" debug "^4.3.2" expo-task-manager@~13.1.5: @@ -11427,55 +11471,55 @@ expo-updates-interface@~1.1.0: resolved "https://registry.yarnpkg.com/expo-updates-interface/-/expo-updates-interface-1.1.0.tgz#62497d4647b381da9fdb68868ed180203ae737ef" integrity sha512-DeB+fRe0hUDPZhpJ4X4bFMAItatFBUPjw/TVSbJsaf3Exeami+2qbbJhWkcTMoYHOB73nOIcaYcWXYJnCJXO0w== -expo-updates@~0.28.12: - version "0.28.12" - resolved "https://registry.yarnpkg.com/expo-updates/-/expo-updates-0.28.12.tgz#abf6e0c593837b20af64feee31aa018e1a85b332" - integrity sha512-GUQNI7apaQa8mVLGyeUQZsSaY75lq3yIp2OO+gX0BZIeD5hr8ADZ0Gw2+9+uRSB70B7AWQklUCMBxvOsA2DVfg== +expo-updates@~0.28.14: + version "0.28.14" + resolved "https://registry.yarnpkg.com/expo-updates/-/expo-updates-0.28.14.tgz#bc75b00c0744fec33ba36f8c96e7c86ed0d3c7f6" + integrity sha512-kxI428W7LGSdDWmN/ud5cIg8+SjmQ5XSaUrYauZ0DKsHm2qq1Lh+NYSUWLvYmps+Baalafe6mILmAX8ZnNg26Q== dependencies: "@expo/code-signing-certificates" "0.0.5" - "@expo/config" "~11.0.7" + "@expo/config" "~11.0.10" "@expo/config-plugins" "~10.0.2" "@expo/spawn-async" "^1.7.2" arg "4.1.0" chalk "^4.1.2" expo-eas-client "~0.14.3" - expo-manifests "~0.16.4" + expo-manifests "~0.16.5" expo-structured-headers "~4.1.0" expo-updates-interface "~1.1.0" glob "^10.4.2" ignore "^5.3.1" resolve-from "^5.0.0" -expo-video@~2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/expo-video/-/expo-video-2.1.8.tgz#507305ae2ea18f435a51ababd6b2dec21291d328" - integrity sha512-OEToLVEGLvfTq7ypjgOf1DUNGHLGxUiL/1K6WHwdew5tgFxiLBZesGQhKToyrSVPVmWaxMBSP4sQuwtTjWUpHg== +expo-video@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/expo-video/-/expo-video-2.2.1.tgz#d45357ee7a7da5a87c49177587183d1c7feabc57" + integrity sha512-dw3h0eMLK8WpY1Tnwsgrxx3sFqXiOujmurjGdr+RFG63ZurAze/H9uuKMVl3ps/ZNuK4q/2ifIiJudoFJfwKwA== expo-web-browser@~14.1.6: version "14.1.6" resolved "https://registry.yarnpkg.com/expo-web-browser/-/expo-web-browser-14.1.6.tgz#26d66e641e6e96d155be6fa513e7e667a719a0b0" integrity sha512-/4P8eWqRyfXIMZna3acg320LXNA+P2cwyEVbjDX8vHnWU+UnOtyRKWy3XaAIyMPQ9hVjBNUQTh4MPvtnPRzakw== -expo@^53.0.5: - version "53.0.5" - resolved "https://registry.yarnpkg.com/expo/-/expo-53.0.5.tgz#f8c30643c4e45d769934415ea9196531612cb657" - integrity sha512-A9nh7ojGcdsv05TCH39q/7RRQsTfgcQ3qD2CFzlADVW8jIIFNiIg80shE8DQiXHkeCKWT78Iy7iog2yIu4sq3Q== +expo@53.0.11: + version "53.0.11" + resolved "https://registry.yarnpkg.com/expo/-/expo-53.0.11.tgz#66053862520ce2a6700d13346ebaf8210a68f24b" + integrity sha512-+QtvU+6VPd7/o4vmtwuRE/Li2rAiJtD25I6BOnoQSxphaWWaD0PdRQnIV3VQ0HESuJYRuKJ3DkAHNJ3jI6xwzA== dependencies: "@babel/runtime" "^7.20.0" - "@expo/cli" "0.24.10" - "@expo/config" "~11.0.7" + "@expo/cli" "0.24.14" + "@expo/config" "~11.0.10" "@expo/config-plugins" "~10.0.2" - "@expo/fingerprint" "0.12.4" - "@expo/metro-config" "0.20.12" + "@expo/fingerprint" "0.13.0" + "@expo/metro-config" "0.20.14" "@expo/vector-icons" "^14.0.0" - babel-preset-expo "~13.1.11" - expo-asset "~11.1.4" - expo-constants "~17.1.5" - expo-file-system "~18.1.8" - expo-font "~13.3.0" + babel-preset-expo "~13.2.0" + expo-asset "~11.1.5" + expo-constants "~17.1.6" + expo-file-system "~18.1.10" + expo-font "~13.3.1" expo-keep-awake "~14.1.4" - expo-modules-autolinking "2.1.9" - expo-modules-core "2.3.12" + expo-modules-autolinking "2.1.11" + expo-modules-core "2.4.0" react-native-edge-to-edge "1.6.0" whatwg-url-without-unicode "8.0.0-3" @@ -12055,6 +12099,11 @@ getenv@^1.0.0: resolved "https://registry.yarnpkg.com/getenv/-/getenv-1.0.0.tgz#874f2e7544fbca53c7a4738f37de8605c3fcfc31" integrity sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg== +getenv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/getenv/-/getenv-2.0.0.tgz#b1698c7b0f29588f4577d06c42c73a5b475c69e0" + integrity sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ== + github-from-package@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" @@ -12079,18 +12128,6 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@7.0.6: - version "7.0.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" - integrity sha512-f8c0rE8JiCxpa52kWPAOa3ZaYEnzofDzCQLCn3Vdk0Z5OVLq3BsRFJI4S4ykpeVW6QMGBUkMeUpoEgWnMTnw5Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@^10.3.10: version "10.3.12" resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b" @@ -13469,12 +13506,12 @@ jest-environment-node@^29.7.0: jest-mock "^29.7.0" jest-util "^29.7.0" -jest-expo@~53.0.3: - version "53.0.3" - resolved "https://registry.yarnpkg.com/jest-expo/-/jest-expo-53.0.3.tgz#094da673d5b7953565c4cc6c41af4337465ddfc9" - integrity sha512-aOJyy/i0wPAWrQpSVNTTFWjcNTbUE/0eXr2cpdM3WW57K3EB1sueuKj8j2tWlzeKz4bk3+DVRy8K44V/XMoy3Q== +jest-expo@~53.0.7: + version "53.0.7" + resolved "https://registry.yarnpkg.com/jest-expo/-/jest-expo-53.0.7.tgz#ab1c288940dcdf470c25b7b18e2ed99f03d26bc6" + integrity sha512-Uiu3ES0sWbsxpifQuBzXMI1/N9JygfJfwEby/Qw/OPndIQ1YeeIQqkbP52xn6UhdSM4qYQiteX3EjY8TfrZIoA== dependencies: - "@expo/config" "~11.0.7" + "@expo/config" "~11.0.10" "@expo/json-file" "^9.1.4" "@jest/create-cache-key-function" "^29.2.1" "@jest/globals" "^29.2.1" @@ -14061,10 +14098,10 @@ kysely@^0.23.4: resolved "https://registry.yarnpkg.com/kysely/-/kysely-0.23.5.tgz#60c63d94e1c42cc0411be8aaa688a0f27405f514" integrity sha512-TH+b56pVXQq0tsyooYLeNfV11j6ih7D50dyN8tkM0e7ndiUH28Nziojiog3qRFlmEj9XePYdZUrNJ2079Qjdow== -lan-network@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/lan-network/-/lan-network-0.1.5.tgz#e781889b7bd4dbedd9126fff3ceddd809a83c3ff" - integrity sha512-CV3k7l8jW0Z1b+G41tB7JInVyJEKQzh/YPl2v9uXpZMusp0aa+rh3OqG77xWuX7+eVBa8PsdTuMznTAssF4qwg== +lan-network@^0.1.6: + version "0.1.7" + resolved "https://registry.yarnpkg.com/lan-network/-/lan-network-0.1.7.tgz#9fcb9967c6d951f10b2f9a9ffabe4a312d63f69d" + integrity sha512-mnIlAEMu4OyEvUNdzco9xpuB9YVcPkQec+QsgycBCtPZvEqWPCDPfbAE4OJMdBBWpZWtpCn1xw9jJYlwjWI5zQ== lande@^1.0.10: version "1.0.10" @@ -14846,7 +14883,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -15097,6 +15134,11 @@ node-releases@^2.0.18: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + nodemailer-html-to-text@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/nodemailer-html-to-text/-/nodemailer-html-to-text-3.2.0.tgz#91b959491fef8f7d91796047abb728aa86d4a12b" @@ -15755,7 +15797,7 @@ picocolors@^1.0.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== -picocolors@^1.1.0: +picocolors@^1.1.0, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -16770,7 +16812,7 @@ react-keyed-flatten-children@^5.0.0: resolved "https://registry.yarnpkg.com/react-keyed-flatten-children/-/react-keyed-flatten-children-5.0.0.tgz#3024fc8819f7b60fc5039b527f133d9ac3a02a82" integrity sha512-XA5ah02sZAeDrbz4Lusd4acqG5q5BtVwPHWierruVhrgX6CMCldbGcTZZM14cQZ+GWq+tzRzEpsCvnTtLODvjw== -react-native-compressor@1.11.0: +react-native-compressor@^1.11.0: version "1.11.0" resolved "https://registry.yarnpkg.com/react-native-compressor/-/react-native-compressor-1.11.0.tgz#e297fa650b09cc754392153c38ae2ca510aee024" integrity sha512-XaI0U2CtlW6ZYjwdQ4jdpnJa3C9CD1pc1a4jiUMtnUxWtCqgT7PNjOiEqlYLLwwGTvorXXNuby5In1yy7Vdmhg== @@ -16787,20 +16829,13 @@ react-native-dotenv@^3.4.11: dependencies: dotenv "^16.4.5" -react-native-drawer-layout@^4.1.10: +react-native-drawer-layout@^4.1.10, react-native-drawer-layout@^4.1.8: version "4.1.10" resolved "https://registry.yarnpkg.com/react-native-drawer-layout/-/react-native-drawer-layout-4.1.10.tgz#9007cb747767ca8e1c9c3337671ad35ed95ad4d9" integrity sha512-wejQo0F+EffCkOkRh+DP6ENWMB+aWEHkXV8Pd564PmtoySZLUsV/ksYrh/mrufh7T7EuvGT8+fNHz7mMRYftWg== dependencies: use-latest-callback "^0.2.3" -react-native-drawer-layout@^4.1.6: - version "4.1.7" - resolved "https://registry.yarnpkg.com/react-native-drawer-layout/-/react-native-drawer-layout-4.1.7.tgz#1c741c9bf9c739d6672201692e4ba4839ca0c8ff" - integrity sha512-KeTGZsNEDbOmgo8ICwr1vBmvWjRrRsvbLc2IAfQnW5h5UtxVZVRxY4QaN84BSBQPXm6tQ6AXfII8TCXCv3c0Ew== - dependencies: - use-latest-callback "^0.2.3" - react-native-edge-to-edge@1.6.0, react-native-edge-to-edge@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz#2ba63b941704a7f713e298185c26cde4d9e4b973" @@ -16858,10 +16893,10 @@ react-native-mmkv@^2.12.2: resolved "https://registry.yarnpkg.com/react-native-mmkv/-/react-native-mmkv-2.12.2.tgz#4bba0f5f04e2cf222494cce3a9794ba6a4894dee" integrity sha512-6058Aq0p57chPrUutLGe9fYoiDVDNMU2PKV+lLFUJ3GhoHvUrLdsS1PDSCLr00yqzL4WJQ7TTzH+V8cpyrNcfg== -react-native-pager-view@6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-6.7.1.tgz#60d52dedbcc92ee7037a13287ebeed5f74e49df7" - integrity sha512-cBSr6xw4g5N7Kd3VGWcf+kmaH7iBWb0DXAf2bVo3bXkzBcBbTOmYSvc0LVLHhUPW8nEq5WjT9LCIYAzgF++EXw== +react-native-pager-view@^6.7.1: + version "6.8.1" + resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-6.8.1.tgz#fa0ec09ea7c44190c7c013d75dd09fdc17b96100" + integrity sha512-XIyVEMhwq7sZqM7GobOJZXxFCfdFgVNq/CFB2rZIRNRSVPJqE1k1fsc8xfQKfdzsp6Rpt6I7VOIvhmP7/YHdVg== react-native-progress@bluesky-social/react-native-progress: version "5.0.0" @@ -16895,10 +16930,10 @@ react-native-reanimated@~3.17.5: invariant "^2.2.4" react-native-is-edge-to-edge "1.1.7" -react-native-root-siblings@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/react-native-root-siblings/-/react-native-root-siblings-4.1.1.tgz#b7742db7634a87f507eb99a5fd699c4f10c46ab0" - integrity sha512-sdmLElNs5PDWqmZmj4/aNH4anyxreaPm61c4ZkRiR8SO/GzLg6KjAbb0e17RmMdnBdD0AIQbS38h/l55YKN4ZA== +react-native-root-siblings@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/react-native-root-siblings/-/react-native-root-siblings-5.0.1.tgz#97e050e5155228f65810fb1c466ff8e769c5272c" + integrity sha512-Ay3k/fBj6ReUkWX5WNS+oEAcgPLEGOK8n7K/L7D85mf3xvd8rm/b4spsv26E4HlFzluVx5HKbxEt9cl0wQ1u3g== react-native-safe-area-context@5.4.0: version "5.4.0" @@ -16914,10 +16949,10 @@ react-native-screens@^4.11.1: react-native-is-edge-to-edge "^1.1.7" warn-once "^0.1.0" -react-native-svg@15.11.2: - version "15.11.2" - resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-15.11.2.tgz#7540e8e1eabc4dcd3b1e35ada5a1d9f1b96d37c4" - integrity sha512-+YfF72IbWQUKzCIydlijV1fLuBsQNGMT6Da2kFlo1sh+LE3BIm/2Q7AR1zAAR6L0BFLi1WaQPLfFUC9bNZpOmw== +react-native-svg@15.12.0: + version "15.12.0" + resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-15.12.0.tgz#0e2d476961e8b07f8c549fe4489c99b5130dc150" + integrity sha512-iE25PxIJ6V0C6krReLquVw6R0QTsRTmEQc4K2Co3P6zsimU/jltcDBKYDy1h/5j9S/fqmMeXnpM+9LEWKJKI6A== dependencies: css-select "^5.1.0" css-tree "^1.1.3" @@ -16968,27 +17003,27 @@ react-native-web@~0.20.0: postcss-value-parser "^4.2.0" styleq "^0.1.3" -react-native-webview@13.13.5: - version "13.13.5" - resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.13.5.tgz#4ef5f9310ddff5747f884a6655228ec9c7d52c73" - integrity sha512-MfC2B+woL4Hlj2WCzcb1USySKk+SteXnUKmKktOk/H/AQy5+LuVdkPKm8SknJ0/RxaxhZ48WBoTRGaqgR137hw== +react-native-webview@^13.13.5: + version "13.15.0" + resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.15.0.tgz#b6d2f8d8dd65897db76659ddd8198d2c74ec5a79" + integrity sha512-Vzjgy8mmxa/JO6l5KZrsTC7YemSdq+qB01diA0FqjUTaWGAGwuykpJ73MDj3+mzBSlaDxAEugHzTtkUQkQEQeQ== dependencies: escape-string-regexp "^4.0.0" invariant "2.2.4" -react-native@0.79.2: - version "0.79.2" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.79.2.tgz#f1a53099701c1736d09e441eb79f97cfc90dd202" - integrity sha512-AnGzb56JvU5YCL7cAwg10+ewDquzvmgrMddiBM0GAWLwQM/6DJfGd2ZKrMuKKehHerpDDZgG+EY64gk3x3dEkw== +react-native@^0.79.3: + version "0.79.3" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.79.3.tgz#16580ca202016c75e3c61116fcfe3b30f6d762fc" + integrity sha512-EzH1+9gzdyEo9zdP6u7Sh3Jtf5EOMwzy+TK65JysdlgAzfEVfq4mNeXcAZ6SmD+CW6M7ARJbvXLyTD0l2S5rpg== dependencies: "@jest/create-cache-key-function" "^29.7.0" - "@react-native/assets-registry" "0.79.2" - "@react-native/codegen" "0.79.2" - "@react-native/community-cli-plugin" "0.79.2" - "@react-native/gradle-plugin" "0.79.2" - "@react-native/js-polyfills" "0.79.2" - "@react-native/normalize-colors" "0.79.2" - "@react-native/virtualized-lists" "0.79.2" + "@react-native/assets-registry" "0.79.3" + "@react-native/codegen" "0.79.3" + "@react-native/community-cli-plugin" "0.79.3" + "@react-native/gradle-plugin" "0.79.3" + "@react-native/js-polyfills" "0.79.3" + "@react-native/normalize-colors" "0.79.3" + "@react-native/virtualized-lists" "0.79.3" abort-controller "^3.0.0" anser "^1.4.9" ansi-regex "^5.0.0" @@ -17541,14 +17576,6 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -rn-fetch-blob@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/rn-fetch-blob/-/rn-fetch-blob-0.12.0.tgz#ec610d2f9b3f1065556b58ab9c106eeb256f3cba" - integrity sha512-+QnR7AsJ14zqpVVUbzbtAjq0iI8c9tCg49tIoKO2ezjzRunN7YL6zFSFSWZm6d+mE/l9r+OeDM3jmb2tBb2WbA== - dependencies: - base-64 "0.1.0" - glob "7.0.6" - roarr@^7.0.4: version "7.15.1" resolved "https://registry.yarnpkg.com/roarr/-/roarr-7.15.1.tgz#e4d93105c37b5ea7dd1200d96a3500f757ddc39f" @@ -17736,12 +17763,10 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== -semver@7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" - integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== - dependencies: - lru-cache "^6.0.0" +semver@7.6.3, semver@^7.1.3, semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== semver@^5.5.0, semver@^5.6.0: version "5.7.2" @@ -17753,11 +17778,6 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.1.3, semver@^7.6.3: - version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== - semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@~7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" @@ -19372,6 +19392,14 @@ update-browserslist-db@^1.1.1: escalade "^3.2.0" picocolors "^1.1.0" +update-browserslist-db@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + update-check@1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.3.tgz#45240fcfb8755a7c7fa68bbdd9eda026a41639ed" From 8a5a7db1466f20af90809a6fe7c07bb0e1e000ac Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 12 Jun 2025 21:21:16 +0300 Subject: [PATCH 2/7] rm browserslist (#8481) --- package.json | 1 - yarn.lock | 35 +---------------------------------- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/package.json b/package.json index 38f574f42f..f490f108f0 100644 --- a/package.json +++ b/package.json @@ -247,7 +247,6 @@ "babel-plugin-module-resolver": "^5.0.2", "babel-plugin-react-compiler": "^19.1.0-rc.1", "babel-preset-expo": "~13.1.11", - "browserslist": "^4.25.0", "eslint": "^8.19.0", "eslint-plugin-bsky-internal": "link:./eslint", "eslint-plugin-ft-flow": "^2.0.3", diff --git a/yarn.lock b/yarn.lock index 125c985ea5..d9dbc18063 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8998,16 +8998,6 @@ browserslist@^4.24.0, browserslist@^4.24.2: node-releases "^2.0.18" update-browserslist-db "^1.1.1" -browserslist@^4.25.0: - version "4.25.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.0.tgz#986aa9c6d87916885da2b50d8eb577ac8d133b2c" - integrity sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA== - dependencies: - caniuse-lite "^1.0.30001718" - electron-to-chromium "^1.5.160" - node-releases "^2.0.19" - update-browserslist-db "^1.1.3" - bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -9150,11 +9140,6 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001587, can resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001697.tgz" integrity sha512-GwNPlWJin8E+d7Gxq96jxM6w0w+VFeyyXRsjU58emtkYqnbwHqXm5uT2uCmO0RQE9htWknOP4xtBlLmM/gWxvQ== -caniuse-lite@^1.0.30001718: - version "1.0.30001722" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001722.tgz#ec25a2b3085b25b9079b623db83c22a70882ce85" - integrity sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA== - cbor-extract@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/cbor-extract/-/cbor-extract-2.1.1.tgz#f154b31529fdb6b7c70fb3ca448f44eda96a1b42" @@ -10393,11 +10378,6 @@ electron-to-chromium@^1.4.668: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.777.tgz#f846fbba23fd11b3c6f97848cdda94896fdb8baf" integrity sha512-n02NCwLJ3wexLfK/yQeqfywCblZqLcXphzmid5e8yVPdtEcida7li0A5WQKghHNG0FeOMCzeFOzEbtAh5riXFw== -electron-to-chromium@^1.5.160: - version "1.5.166" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.166.tgz#3fff386ed473cc2169dbe2d3ace9592262601114" - integrity sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw== - electron-to-chromium@^1.5.41: version "1.5.51" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.51.tgz#bb99216fed4892d131a8585a8593b00739310163" @@ -15134,11 +15114,6 @@ node-releases@^2.0.18: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== -node-releases@^2.0.19: - version "2.0.19" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" - integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== - nodemailer-html-to-text@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/nodemailer-html-to-text/-/nodemailer-html-to-text-3.2.0.tgz#91b959491fef8f7d91796047abb728aa86d4a12b" @@ -15797,7 +15772,7 @@ picocolors@^1.0.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== -picocolors@^1.1.0, picocolors@^1.1.1: +picocolors@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -19392,14 +19367,6 @@ update-browserslist-db@^1.1.1: escalade "^3.2.0" picocolors "^1.1.0" -update-browserslist-db@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" - integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.1" - update-check@1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.3.tgz#45240fcfb8755a7c7fa68bbdd9eda026a41639ed" From 18b4fcf8eb1ec4d27c1245726a4dbf50c534f651 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 12 Jun 2025 23:16:52 +0300 Subject: [PATCH 3/7] Revert "Instant Feed Update on Mute or Moderation Action" (#8482) --- src/state/cache/post-shadow.ts | 12 ++---------- src/state/queries/profile.ts | 4 +--- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/state/cache/post-shadow.ts b/src/state/cache/post-shadow.ts index 90fddda2bd..d7f1eb8b93 100644 --- a/src/state/cache/post-shadow.ts +++ b/src/state/cache/post-shadow.ts @@ -15,7 +15,6 @@ import {findAllPostsInQueryData as findAllPostsInQuoteQueryData} from '#/state/q import {findAllPostsInQueryData as findAllPostsInThreadQueryData} from '#/state/queries/post-thread' import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from '#/state/queries/search-posts' import {findAllPostsInQueryData as findAllPostsInThreadV2QueryData} from '#/state/queries/usePostThread/queryCache' -import {useProfileShadow} from './profile-shadow' import {castAsShadow, type Shadow} from './types' export type {Shadow} from './types' @@ -45,10 +44,6 @@ export function usePostShadow( setShadow(shadows.get(post)) } - const authorShadow = useProfileShadow(post.author) - const wasMuted = !!authorShadow.viewer?.muted - const wasBlocked = !!authorShadow.viewer?.blocking - useEffect(() => { function onUpdate() { setShadow(shadows.get(post)) @@ -60,18 +55,15 @@ export function usePostShadow( }, [post, setShadow]) return useMemo(() => { - if (wasMuted || wasBlocked) { - return POST_TOMBSTONE - } if (shadow) { return mergeShadow(post, shadow) } else { return castAsShadow(post) } - }, [post, shadow, wasMuted, wasBlocked]) + }, [post, shadow]) } -export function mergeShadow( +function mergeShadow( post: AppBskyFeedDefs.PostView, shadow: Partial, ): Shadow | typeof POST_TOMBSTONE { diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts index b0af57c4a7..eb65fef7c2 100644 --- a/src/state/queries/profile.ts +++ b/src/state/queries/profile.ts @@ -499,10 +499,9 @@ function useProfileBlockMutation() { {subject: did, createdAt: new Date().toISOString()}, ) }, - onSuccess(data, {did}) { + onSuccess(_, {did}) { queryClient.invalidateQueries({queryKey: RQKEY_MY_BLOCKED()}) resetProfilePostsQueries(queryClient, did, 1000) - updateProfileShadow(queryClient, did, {blockingUri: data.uri}) }, }) } @@ -524,7 +523,6 @@ function useProfileUnblockMutation() { }, onSuccess(_, {did}) { resetProfilePostsQueries(queryClient, did, 1000) - updateProfileShadow(queryClient, did, {blockingUri: undefined}) }, }) } From ba0f5a9bdef5bd0447ded23cab1af222b65511cc Mon Sep 17 00:00:00 2001 From: pfrazee <1270099+pfrazee@users.noreply.github.com> Date: Fri, 13 Jun 2025 02:41:11 +0000 Subject: [PATCH 4/7] Nightly source-language update --- src/locale/locales/en/messages.po | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po index 8bb3dca9e5..1b2b03a19d 100644 --- a/src/locale/locales/en/messages.po +++ b/src/locale/locales/en/messages.po @@ -4469,11 +4469,11 @@ msgstr "" msgid "List by {0}" msgstr "" -#: src/view/com/profile/ProfileSubpageHeader.tsx:156 +#: src/view/com/profile/ProfileSubpageHeader.tsx:160 msgid "List by <0/>" msgstr "" -#: src/view/com/profile/ProfileSubpageHeader.tsx:154 +#: src/view/com/profile/ProfileSubpageHeader.tsx:158 msgid "List by you" msgstr "" @@ -4740,12 +4740,12 @@ msgstr "" msgid "Moderation list by {0}" msgstr "" -#: src/view/com/profile/ProfileSubpageHeader.tsx:169 +#: src/view/com/profile/ProfileSubpageHeader.tsx:173 msgid "Moderation list by <0/>" msgstr "" #: src/view/com/modals/UserAddRemoveLists.tsx:220 -#: src/view/com/profile/ProfileSubpageHeader.tsx:167 +#: src/view/com/profile/ProfileSubpageHeader.tsx:171 msgid "Moderation list by you" msgstr "" @@ -5905,17 +5905,17 @@ msgstr "" msgid "Porn" msgstr "" -#: src/view/com/composer/Composer.tsx:1030 -msgctxt "action" -msgid "Post" -msgstr "" - #: src/screens/PostThread/index.tsx:490 #: src/view/com/post-thread/PostThread.tsx:561 msgctxt "description" msgid "Post" msgstr "" +#: src/view/com/composer/Composer.tsx:1030 +msgctxt "action" +msgid "Post" +msgstr "" + #: src/view/com/composer/Composer.tsx:1028 msgctxt "action" msgid "Post All" @@ -7681,12 +7681,12 @@ msgstr "" msgid "Starter pack by {0}" msgstr "" -#: src/view/com/profile/ProfileSubpageHeader.tsx:182 +#: src/view/com/profile/ProfileSubpageHeader.tsx:186 msgid "Starter pack by <0/>" msgstr "" #: src/components/StarterPack/StarterPackCard.tsx:89 -#: src/view/com/profile/ProfileSubpageHeader.tsx:180 +#: src/view/com/profile/ProfileSubpageHeader.tsx:184 msgid "Starter pack by you" msgstr "" @@ -9126,7 +9126,7 @@ msgstr "" msgid "View profile" msgstr "" -#: src/view/com/profile/ProfileSubpageHeader.tsx:119 +#: src/view/com/profile/ProfileSubpageHeader.tsx:123 msgid "View the avatar" msgstr "" @@ -9171,8 +9171,8 @@ msgstr "" msgid "View your verifications" msgstr "" -#: src/view/com/util/images/AutoSizedImage.tsx:199 -#: src/view/com/util/images/AutoSizedImage.tsx:221 +#: src/view/com/util/images/AutoSizedImage.tsx:205 +#: src/view/com/util/images/AutoSizedImage.tsx:227 msgid "Views full image" msgstr "" From 45f0f7eefecae1922c2f30d4e7760d2b93b1ae56 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 13 Jun 2025 12:05:41 -0500 Subject: [PATCH 5/7] Port post embeds to new arch (#7408) * Direct port of embeds to new arch (cherry picked from commit cc3fa1f6cea396dd9222486c633a508bfee1ecd6) * Re-org * Split out ListEmbed and FeedEmbed * Split out ImageEmbed * DRY up a bit * Port over ExternalLinkEmbed * Port over Player and Gif embeds * Migrate ComposerReplyTo * Replace other usages of old post-embeds * Migrate view contexts * Copy pasta VideoEmbed * Copy pasta GifEmbed * Swap in new file location * Clean up * Fix up native * Add back in correct moderation on List and Feed embeds * Format * Prettier * delete old video utils * move bandwidth-estimate.ts * Remove log * Add LazyQuoteEmbed for composer use * Clean up unused things * Remove remaining items * Prettier * Fix imports * Handle nested quotes same as prod * Add back silenced error handling * Fix lint --------- Co-authored-by: Samuel Newman --- bskylink/src/routes/index.ts | 4 +- bskylink/src/routes/redirect.ts | 4 +- bskylink/src/routes/root.ts | 4 +- src/App.native.tsx | 2 +- src/App.web.tsx | 4 +- src/alf/util/systemUI.ts | 2 +- src/components/ContextMenu/Backdrop.tsx | 2 +- src/components/FeedInterstitials.tsx | 18 +- src/components/Layout/Header/index.tsx | 8 +- src/components/Menu/context.tsx | 2 +- .../Post/Embed/ExternalEmbed/ExternalGif.tsx} | 2 +- .../Embed/ExternalEmbed/ExternalPlayer.tsx} | 2 +- .../Post/Embed/ExternalEmbed/Gif.tsx} | 0 .../Post/Embed/ExternalEmbed/index.tsx} | 10 +- src/components/Post/Embed/FeedEmbed.tsx | 52 +++ src/components/Post/Embed/ImageEmbed.tsx | 106 ++++++ src/components/Post/Embed/LazyQuoteEmbed.tsx | 37 ++ src/components/Post/Embed/ListEmbed.tsx | 42 +++ src/components/Post/Embed/PostPlaceholder.tsx | 33 ++ .../VideoEmbed}/ActiveVideoWebContext.tsx | 0 .../VideoEmbedInner/TimeIndicator.tsx | 0 .../VideoEmbedInner/VideoEmbedInnerNative.tsx | 2 +- .../VideoEmbedInnerNative.web.tsx | 0 .../VideoEmbedInnerWeb.native.tsx | 0 .../VideoEmbedInner/VideoEmbedInnerWeb.tsx | 0 .../VideoEmbedInner/VideoFallback.tsx | 0 .../VideoEmbedInner/bandwidth-estimate.ts | 0 .../web-controls/ControlButton.tsx | 2 +- .../VideoEmbedInner/web-controls/Scrubber.tsx | 0 .../web-controls/VideoControls.native.tsx | 0 .../web-controls/VideoControls.tsx | 0 .../web-controls/VolumeControl.tsx | 2 +- .../VideoEmbedInner/web-controls/utils.tsx | 6 +- .../Embed/VideoEmbed}/VideoVolumeContext.tsx | 0 .../Post/Embed/VideoEmbed/index.tsx} | 4 +- .../Post/Embed/VideoEmbed/index.web.tsx} | 10 +- src/components/Post/Embed/index.tsx | 332 +++++++++++++++++ src/components/Post/Embed/types.ts | 25 ++ src/components/dms/ActionsWrapper.tsx | 2 +- src/components/dms/MessageItemEmbed.tsx | 9 +- src/components/hooks/dates.ts | 4 +- src/components/moderation/PostHider.tsx | 16 +- src/locale/helpers.ts | 2 +- src/screens/Login/LoginForm.tsx | 4 +- src/screens/Onboarding/StepProfile/index.tsx | 6 +- .../components/ThreadItemAnchor.tsx | 4 +- .../PostThread/components/ThreadItemPost.tsx | 4 +- .../components/ThreadItemTreePost.tsx | 4 +- src/screens/Profile/Header/Handle.tsx | 4 +- src/screens/Signup/StepInfo/Policies.tsx | 4 +- .../StarterPack/StarterPackLandingScreen.tsx | 2 +- src/screens/StarterPack/Wizard/State.tsx | 4 +- src/screens/Takendown.tsx | 2 +- src/screens/VideoFeed/components/Scrubber.tsx | 2 +- src/state/messages/events/agent.ts | 8 +- src/state/queries/postgate/util.ts | 7 +- src/state/session/types.ts | 4 +- src/state/threadgate-hidden-replies.tsx | 2 +- src/view/com/composer/Composer.tsx | 15 +- src/view/com/composer/ComposerReplyTo.tsx | 13 +- src/view/com/composer/ExternalEmbed.tsx | 37 +- .../com/composer/ExternalEmbedRemoveBtn.tsx | 13 +- src/view/com/composer/GifAltText.tsx | 8 +- src/view/com/composer/labels/LabelsBtn.tsx | 6 +- .../composer/photos/ImageAltTextDialog.tsx | 6 +- .../com/composer/photos/OpenCameraBtn.tsx | 2 +- src/view/com/post-thread/PostThreadItem.tsx | 6 +- src/view/com/post/Post.tsx | 4 +- src/view/com/posts/PostFeedItem.tsx | 5 +- src/view/com/util/Views.web.tsx | 8 +- src/view/com/util/images/Gallery.tsx | 2 +- src/view/com/util/images/ImageLayoutGrid.tsx | 2 +- src/view/com/util/post-embeds/QuoteEmbed.tsx | 337 ------------------ src/view/com/util/post-embeds/index.tsx | 327 ----------------- src/view/com/util/post-embeds/types.ts | 9 - 75 files changed, 812 insertions(+), 799 deletions(-) rename src/{view/com/util/post-embeds/ExternalGifEmbed.tsx => components/Post/Embed/ExternalEmbed/ExternalGif.tsx} (99%) rename src/{view/com/util/post-embeds/ExternalPlayerEmbed.tsx => components/Post/Embed/ExternalEmbed/ExternalPlayer.tsx} (99%) rename src/{view/com/util/post-embeds/GifEmbed.tsx => components/Post/Embed/ExternalEmbed/Gif.tsx} (100%) rename src/{view/com/util/post-embeds/ExternalLinkEmbed.tsx => components/Post/Embed/ExternalEmbed/index.tsx} (94%) create mode 100644 src/components/Post/Embed/FeedEmbed.tsx create mode 100644 src/components/Post/Embed/ImageEmbed.tsx create mode 100644 src/components/Post/Embed/LazyQuoteEmbed.tsx create mode 100644 src/components/Post/Embed/ListEmbed.tsx create mode 100644 src/components/Post/Embed/PostPlaceholder.tsx rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/ActiveVideoWebContext.tsx (100%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/TimeIndicator.tsx (100%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/VideoEmbedInnerNative.tsx (98%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/VideoEmbedInnerNative.web.tsx (100%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/VideoEmbedInnerWeb.native.tsx (100%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/VideoEmbedInnerWeb.tsx (100%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/VideoFallback.tsx (100%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/bandwidth-estimate.ts (100%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/web-controls/ControlButton.tsx (93%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/web-controls/Scrubber.tsx (100%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/web-controls/VideoControls.native.tsx (100%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/web-controls/VideoControls.tsx (100%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/web-controls/VolumeControl.tsx (97%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoEmbedInner/web-controls/utils.tsx (96%) rename src/{view/com/util/post-embeds => components/Post/Embed/VideoEmbed}/VideoVolumeContext.tsx (100%) rename src/{view/com/util/post-embeds/VideoEmbed.tsx => components/Post/Embed/VideoEmbed/index.tsx} (96%) rename src/{view/com/util/post-embeds/VideoEmbed.web.tsx => components/Post/Embed/VideoEmbed/index.web.tsx} (97%) create mode 100644 src/components/Post/Embed/index.tsx create mode 100644 src/components/Post/Embed/types.ts delete mode 100644 src/view/com/util/post-embeds/QuoteEmbed.tsx delete mode 100644 src/view/com/util/post-embeds/index.tsx delete mode 100644 src/view/com/util/post-embeds/types.ts diff --git a/bskylink/src/routes/index.ts b/bskylink/src/routes/index.ts index 9fd20d276b..d0122ff8bf 100644 --- a/bskylink/src/routes/index.ts +++ b/bskylink/src/routes/index.ts @@ -1,6 +1,6 @@ -import {Express} from 'express' +import {type Express} from 'express' -import {AppContext} from '../context.js' +import {type AppContext} from '../context.js' import {default as createShortLink} from './createShortLink.js' import {default as health} from './health.js' import {default as redirect} from './redirect.js' diff --git a/bskylink/src/routes/redirect.ts b/bskylink/src/routes/redirect.ts index 468d250192..7d68e4245d 100644 --- a/bskylink/src/routes/redirect.ts +++ b/bskylink/src/routes/redirect.ts @@ -2,9 +2,9 @@ import assert from 'node:assert' import {DAY, SECOND} from '@atproto/common' import escapeHTML from 'escape-html' -import {Express} from 'express' +import {type Express} from 'express' -import {AppContext} from '../context.js' +import {type AppContext} from '../context.js' import {handler} from './util.js' const INTERNAL_IP_REGEX = new RegExp( diff --git a/bskylink/src/routes/root.ts b/bskylink/src/routes/root.ts index 12bdf15155..8c6c4afc3b 100644 --- a/bskylink/src/routes/root.ts +++ b/bskylink/src/routes/root.ts @@ -1,6 +1,6 @@ -import {Express} from 'express' +import {type Express} from 'express' -import {AppContext} from '../context.js' +import {type AppContext} from '../context.js' import {handler} from './util.js' export default function (ctx: AppContext, app: Express) { diff --git a/src/App.native.tsx b/src/App.native.tsx index 25d186dcfb..81d4a870e9 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -59,7 +59,6 @@ import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed' import {Provider as StarterPackProvider} from '#/state/shell/starter-pack' import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies' import {TestCtrls} from '#/view/com/testing/TestCtrls' -import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext' import * as Toast from '#/view/com/util/Toast' import {Shell} from '#/view/shell' import {ThemeProvider as Alf} from '#/alf' @@ -69,6 +68,7 @@ import {NuxDialogs} from '#/components/dialogs/nuxs' import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry' import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialogs' import {Provider as PortalProvider} from '#/components/Portal' +import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' import {Splash} from '#/Splash' import {BottomSheetProvider} from '../modules/bottom-sheet' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' diff --git a/src/App.web.tsx b/src/App.web.tsx index fa8e24e53d..b706774fdc 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -48,8 +48,6 @@ import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide' import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed' import {Provider as StarterPackProvider} from '#/state/shell/starter-pack' import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies' -import {Provider as ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoWebContext' -import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext' import * as Toast from '#/view/com/util/Toast' import {ToastContainer} from '#/view/com/util/Toast.web' import {Shell} from '#/view/shell/index' @@ -60,6 +58,8 @@ import {NuxDialogs} from '#/components/dialogs/nuxs' import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry' import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialogs' import {Provider as PortalProvider} from '#/components/Portal' +import {Provider as ActiveVideoProvider} from '#/components/Post/Embed/VideoEmbed/ActiveVideoWebContext' +import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottomBarBorder' diff --git a/src/alf/util/systemUI.ts b/src/alf/util/systemUI.ts index c973e10ea6..9e5769c4c6 100644 --- a/src/alf/util/systemUI.ts +++ b/src/alf/util/systemUI.ts @@ -1,7 +1,7 @@ import * as SystemUI from 'expo-system-ui' import {isAndroid} from '#/platform/detection' -import {Theme} from '../types' +import {type Theme} from '../types' export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) { if (isAndroid) { diff --git a/src/components/ContextMenu/Backdrop.tsx b/src/components/ContextMenu/Backdrop.tsx index 027bf9849a..37fcebf493 100644 --- a/src/components/ContextMenu/Backdrop.tsx +++ b/src/components/ContextMenu/Backdrop.tsx @@ -2,7 +2,7 @@ import {Pressable} from 'react-native' import Animated, { Extrapolation, interpolate, - SharedValue, + type SharedValue, useAnimatedStyle, } from 'react-native-reanimated' import {msg} from '@lingui/macro' diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx index 6ecc3f5a87..a92e7be7f2 100644 --- a/src/components/FeedInterstitials.tsx +++ b/src/components/FeedInterstitials.tsx @@ -1,24 +1,30 @@ import React from 'react' import {View} from 'react-native' import {ScrollView} from 'react-native-gesture-handler' -import {AppBskyFeedDefs, AtUri} from '@atproto/api' +import {type AppBskyFeedDefs, AtUri} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' -import {NavigationProp} from '#/lib/routes/types' +import {type NavigationProp} from '#/lib/routes/types' import {logEvent} from '#/lib/statsig/statsig' import {logger} from '#/logger' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useGetPopularFeedsQuery} from '#/state/queries/feed' -import {FeedDescriptor} from '#/state/queries/post-feed' +import {type FeedDescriptor} from '#/state/queries/post-feed' import {useProfilesQuery} from '#/state/queries/profile' import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows' import {useSession} from '#/state/session' import * as userActionHistory from '#/state/userActionHistory' -import {SeenPost} from '#/state/userActionHistory' +import {type SeenPost} from '#/state/userActionHistory' import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture' -import {atoms as a, useBreakpoints, useTheme, ViewStyleProp, web} from '#/alf' +import { + atoms as a, + useBreakpoints, + useTheme, + type ViewStyleProp, + web, +} from '#/alf' import {Button} from '#/components/Button' import * as FeedCard from '#/components/FeedCard' import {ArrowRight_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow' @@ -27,7 +33,7 @@ import {PersonPlus_Stroke2_Corner0_Rounded as Person} from '#/components/icons/P import {InlineLinkText} from '#/components/Link' import * as ProfileCard from '#/components/ProfileCard' import {Text} from '#/components/Typography' -import * as bsky from '#/types/bsky' +import type * as bsky from '#/types/bsky' import {ProgressGuideList} from './ProgressGuide/List' const MOBILE_CARD_WIDTH = 300 diff --git a/src/components/Layout/Header/index.tsx b/src/components/Layout/Header/index.tsx index 44faa96498..d68f4bd1d2 100644 --- a/src/components/Layout/Header/index.tsx +++ b/src/components/Layout/Header/index.tsx @@ -1,24 +1,24 @@ import {createContext, useCallback, useContext} from 'react' -import {GestureResponderEvent, Keyboard, View} from 'react-native' +import {type GestureResponderEvent, Keyboard, View} from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' import {HITSLOP_30} from '#/lib/constants' -import {NavigationProp} from '#/lib/routes/types' +import {type NavigationProp} from '#/lib/routes/types' import {isIOS} from '#/platform/detection' import {useSetDrawerOpen} from '#/state/shell' import { atoms as a, platform, - TextStyleProp, + type TextStyleProp, useBreakpoints, useGutters, useLayoutBreakpoints, useTheme, web, } from '#/alf' -import {Button, ButtonIcon, ButtonProps} from '#/components/Button' +import {Button, ButtonIcon, type ButtonProps} from '#/components/Button' import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeft} from '#/components/icons/Arrow' import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu' import { diff --git a/src/components/Menu/context.tsx b/src/components/Menu/context.tsx index d810a03de4..076bc81511 100644 --- a/src/components/Menu/context.tsx +++ b/src/components/Menu/context.tsx @@ -1,6 +1,6 @@ import React from 'react' -import type {ContextType, ItemContextType} from '#/components/Menu/types' +import {type ContextType, type ItemContextType} from '#/components/Menu/types' export const Context = React.createContext(null) diff --git a/src/view/com/util/post-embeds/ExternalGifEmbed.tsx b/src/components/Post/Embed/ExternalEmbed/ExternalGif.tsx similarity index 99% rename from src/view/com/util/post-embeds/ExternalGifEmbed.tsx rename to src/components/Post/Embed/ExternalEmbed/ExternalGif.tsx index 39c1d109e1..8a12f0374d 100644 --- a/src/view/com/util/post-embeds/ExternalGifEmbed.tsx +++ b/src/components/Post/Embed/ExternalEmbed/ExternalGif.tsx @@ -14,7 +14,7 @@ import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent' import {Fill} from '#/components/Fill' import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' -export function ExternalGifEmbed({ +export function ExternalGif({ link, params, }: { diff --git a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx b/src/components/Post/Embed/ExternalEmbed/ExternalPlayer.tsx similarity index 99% rename from src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx rename to src/components/Post/Embed/ExternalEmbed/ExternalPlayer.tsx index e78abdf176..7f6d533408 100644 --- a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx +++ b/src/components/Post/Embed/ExternalEmbed/ExternalPlayer.tsx @@ -25,12 +25,12 @@ import {NavigationProp} from '#/lib/routes/types' import {EmbedPlayerParams, getPlayerAspect} from '#/lib/strings/embed-player' import {isNative} from '#/platform/detection' import {useExternalEmbedsPrefs} from '#/state/preferences' +import {EventStopper} from '#/view/com/util/EventStopper' import {atoms as a, useTheme} from '#/alf' import {useDialogControl} from '#/components/Dialog' import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent' import {Fill} from '#/components/Fill' import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' -import {EventStopper} from '../EventStopper' interface ShouldStartLoadRequest { url: string diff --git a/src/view/com/util/post-embeds/GifEmbed.tsx b/src/components/Post/Embed/ExternalEmbed/Gif.tsx similarity index 100% rename from src/view/com/util/post-embeds/GifEmbed.tsx rename to src/components/Post/Embed/ExternalEmbed/Gif.tsx diff --git a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx b/src/components/Post/Embed/ExternalEmbed/index.tsx similarity index 94% rename from src/view/com/util/post-embeds/ExternalLinkEmbed.tsx rename to src/components/Post/Embed/ExternalEmbed/index.tsx index 7ca11f60d0..714eaecd63 100644 --- a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx +++ b/src/components/Post/Embed/ExternalEmbed/index.tsx @@ -12,16 +12,16 @@ import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player' import {toNiceDomain} from '#/lib/strings/url-helpers' import {isNative} from '#/platform/detection' import {useExternalEmbedsPrefs} from '#/state/preferences' -import {ExternalGifEmbed} from '#/view/com/util/post-embeds/ExternalGifEmbed' -import {ExternalPlayer} from '#/view/com/util/post-embeds/ExternalPlayerEmbed' -import {GifEmbed} from '#/view/com/util/post-embeds/GifEmbed' import {atoms as a, useTheme} from '#/alf' import {Divider} from '#/components/Divider' import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' import {Link} from '#/components/Link' import {Text} from '#/components/Typography' +import {ExternalGif} from './ExternalGif' +import {ExternalPlayer} from './ExternalPlayer' +import {GifEmbed} from './Gif' -export const ExternalLinkEmbed = ({ +export const ExternalEmbed = ({ link, onOpen, style, @@ -106,7 +106,7 @@ export const ExternalLinkEmbed = ({ ) : undefined} {embedPlayerParams?.isGif ? ( - + ) : embedPlayerParams ? ( ) : undefined} diff --git a/src/components/Post/Embed/FeedEmbed.tsx b/src/components/Post/Embed/FeedEmbed.tsx new file mode 100644 index 0000000000..fad4cd4d8b --- /dev/null +++ b/src/components/Post/Embed/FeedEmbed.tsx @@ -0,0 +1,52 @@ +import React from 'react' +import {StyleSheet} from 'react-native' +import {moderateFeedGenerator} from '@atproto/api' + +import {usePalette} from '#/lib/hooks/usePalette' +import {useModerationOpts} from '#/state/preferences/moderation-opts' +import {FeedSourceCard} from '#/view/com/feeds/FeedSourceCard' +import {ContentHider} from '#/components/moderation/ContentHider' +import {type EmbedType} from '#/types/bsky/post' +import {type CommonProps} from './types' + +export function FeedEmbed({ + embed, +}: CommonProps & { + embed: EmbedType<'feed'> +}) { + const pal = usePalette('default') + return ( + + ) +} + +export function ModeratedFeedEmbed({ + embed, +}: CommonProps & { + embed: EmbedType<'feed'> +}) { + const moderationOpts = useModerationOpts() + const moderation = React.useMemo(() => { + return moderationOpts + ? moderateFeedGenerator(embed.view, moderationOpts) + : undefined + }, [embed.view, moderationOpts]) + return ( + + + + ) +} + +const styles = StyleSheet.create({ + customFeedOuter: { + borderWidth: StyleSheet.hairlineWidth, + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 12, + }, +}) diff --git a/src/components/Post/Embed/ImageEmbed.tsx b/src/components/Post/Embed/ImageEmbed.tsx new file mode 100644 index 0000000000..030d237a03 --- /dev/null +++ b/src/components/Post/Embed/ImageEmbed.tsx @@ -0,0 +1,106 @@ +import {InteractionManager, View} from 'react-native' +import { + type AnimatedRef, + measure, + type MeasuredDimensions, + runOnJS, + runOnUI, +} from 'react-native-reanimated' +import {Image} from 'expo-image' + +import {useLightboxControls} from '#/state/lightbox' +import {type Dimensions} from '#/view/com/lightbox/ImageViewing/@types' +import {AutoSizedImage} from '#/view/com/util/images/AutoSizedImage' +import {ImageLayoutGrid} from '#/view/com/util/images/ImageLayoutGrid' +import {atoms as a} from '#/alf' +import {PostEmbedViewContext} from '#/components/Post/Embed/types' +import {type EmbedType} from '#/types/bsky/post' +import {type CommonProps} from './types' + +export function ImageEmbed({ + embed, + ...rest +}: CommonProps & { + embed: EmbedType<'images'> +}) { + const {openLightbox} = useLightboxControls() + const {images} = embed.view + + if (images.length > 0) { + const items = images.map(img => ({ + uri: img.fullsize, + thumbUri: img.thumb, + alt: img.alt, + dimensions: img.aspectRatio ?? null, + })) + const _openLightbox = ( + index: number, + thumbRects: (MeasuredDimensions | null)[], + fetchedDims: (Dimensions | null)[], + ) => { + openLightbox({ + images: items.map((item, i) => ({ + ...item, + thumbRect: thumbRects[i] ?? null, + thumbDimensions: fetchedDims[i] ?? null, + type: 'image', + })), + index, + }) + } + const onPress = ( + index: number, + refs: AnimatedRef[], + fetchedDims: (Dimensions | null)[], + ) => { + runOnUI(() => { + 'worklet' + const rects: (MeasuredDimensions | null)[] = [] + for (const r of refs) { + rects.push(measure(r)) + } + runOnJS(_openLightbox)(index, rects, fetchedDims) + })() + } + const onPressIn = (_: number) => { + InteractionManager.runAfterInteractions(() => { + Image.prefetch(items.map(i => i.uri)) + }) + } + + if (images.length === 1) { + const image = images[0] + return ( + + onPress(0, [containerRef], [dims])} + onPressIn={() => onPressIn(0)} + hideBadge={ + rest.viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia + } + /> + + ) + } + + return ( + + + + ) + } +} diff --git a/src/components/Post/Embed/LazyQuoteEmbed.tsx b/src/components/Post/Embed/LazyQuoteEmbed.tsx new file mode 100644 index 0000000000..fdc1c63091 --- /dev/null +++ b/src/components/Post/Embed/LazyQuoteEmbed.tsx @@ -0,0 +1,37 @@ +import {useMemo} from 'react' +import {View} from 'react-native' + +import {createEmbedViewRecordFromPost} from '#/state/queries/postgate/util' +import {useResolveLinkQuery} from '#/state/queries/resolve-link' +import {atoms as a, useTheme} from '#/alf' +import {QuoteEmbed} from '#/components/Post/Embed' + +export function LazyQuoteEmbed({uri}: {uri: string}) { + const t = useTheme() + const {data} = useResolveLinkQuery(uri) + + const view = useMemo(() => { + if (!data || data.type !== 'record' || data.kind !== 'post') return + return createEmbedViewRecordFromPost(data.view) + }, [data]) + + return view ? ( + + ) : ( + + ) +} diff --git a/src/components/Post/Embed/ListEmbed.tsx b/src/components/Post/Embed/ListEmbed.tsx new file mode 100644 index 0000000000..dc79a75798 --- /dev/null +++ b/src/components/Post/Embed/ListEmbed.tsx @@ -0,0 +1,42 @@ +import React from 'react' +import {View} from 'react-native' +import {moderateUserList} from '@atproto/api' + +import {useModerationOpts} from '#/state/preferences/moderation-opts' +import {atoms as a, useTheme} from '#/alf' +import * as ListCard from '#/components/ListCard' +import {ContentHider} from '#/components/moderation/ContentHider' +import {EmbedType} from '#/types/bsky/post' +import {CommonProps} from './types' + +export function ListEmbed({ + embed, +}: CommonProps & { + embed: EmbedType<'list'> +}) { + const t = useTheme() + return ( + + + + ) +} + +export function ModeratedListEmbed({ + embed, +}: CommonProps & { + embed: EmbedType<'list'> +}) { + const moderationOpts = useModerationOpts() + const moderation = React.useMemo(() => { + return moderationOpts + ? moderateUserList(embed.view, moderationOpts) + : undefined + }, [embed.view, moderationOpts]) + return ( + + + + ) +} diff --git a/src/components/Post/Embed/PostPlaceholder.tsx b/src/components/Post/Embed/PostPlaceholder.tsx new file mode 100644 index 0000000000..8402340269 --- /dev/null +++ b/src/components/Post/Embed/PostPlaceholder.tsx @@ -0,0 +1,33 @@ +import {StyleSheet, View} from 'react-native' + +import {usePalette} from '#/lib/hooks/usePalette' +import {InfoCircleIcon} from '#/lib/icons' +import {Text} from '#/view/com/util/text/Text' +import {atoms as a, useTheme} from '#/alf' + +export function PostPlaceholder({children}: {children: React.ReactNode}) { + const t = useTheme() + const pal = usePalette('default') + return ( + + + + {children} + + + ) +} + +const styles = StyleSheet.create({ + errorContainer: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + borderRadius: 8, + marginTop: 8, + paddingVertical: 14, + paddingHorizontal: 14, + borderWidth: StyleSheet.hairlineWidth, + }, +}) diff --git a/src/view/com/util/post-embeds/ActiveVideoWebContext.tsx b/src/components/Post/Embed/VideoEmbed/ActiveVideoWebContext.tsx similarity index 100% rename from src/view/com/util/post-embeds/ActiveVideoWebContext.tsx rename to src/components/Post/Embed/VideoEmbed/ActiveVideoWebContext.tsx diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/TimeIndicator.tsx similarity index 100% rename from src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/TimeIndicator.tsx diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx similarity index 98% rename from src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx index 8b44f54483..88879d45a7 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx @@ -7,7 +7,6 @@ import {useLingui} from '@lingui/react' import {HITSLOP_30} from '#/lib/constants' import {useAutoplayDisabled} from '#/state/preferences' -import {useVideoMuteState} from '#/view/com/util/post-embeds/VideoVolumeContext' import {atoms as a, useTheme} from '#/alf' import {useIsWithinMessage} from '#/components/dms/MessageContext' import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' @@ -15,6 +14,7 @@ import {Pause_Filled_Corner0_Rounded as PauseIcon} from '#/components/icons/Paus import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play' import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' import {MediaInsetBorder} from '#/components/MediaInsetBorder' +import {useVideoMuteState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' import {TimeIndicator} from './TimeIndicator' export const VideoEmbedInnerNative = React.forwardRef( diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.web.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.web.tsx similarity index 100% rename from src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.web.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.web.tsx diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.native.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.native.tsx similarity index 100% rename from src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.native.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.native.tsx diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx similarity index 100% rename from src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoFallback.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx similarity index 100% rename from src/view/com/util/post-embeds/VideoEmbedInner/VideoFallback.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/bandwidth-estimate.ts b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/bandwidth-estimate.ts similarity index 100% rename from src/view/com/util/post-embeds/VideoEmbedInner/bandwidth-estimate.ts rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/bandwidth-estimate.ts diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/ControlButton.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/ControlButton.tsx similarity index 93% rename from src/view/com/util/post-embeds/VideoEmbedInner/web-controls/ControlButton.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/ControlButton.tsx index 6510464453..1b69a3e253 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/ControlButton.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/ControlButton.tsx @@ -1,8 +1,8 @@ import React from 'react' import {SvgProps} from 'react-native-svg' +import {PressableWithHover} from '#/view/com/util/PressableWithHover' import {atoms as a, useTheme, web} from '#/alf' -import {PressableWithHover} from '../../../PressableWithHover' export function ControlButton({ active, diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/Scrubber.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/Scrubber.tsx similarity index 100% rename from src/view/com/util/post-embeds/VideoEmbedInner/web-controls/Scrubber.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/Scrubber.tsx diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.native.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.native.tsx similarity index 100% rename from src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.native.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.native.tsx diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx similarity index 100% rename from src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VolumeControl.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx similarity index 97% rename from src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VolumeControl.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx index 90ffb9e6b1..e0b6880757 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VolumeControl.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx @@ -8,7 +8,7 @@ import {isSafari, isTouchDevice} from '#/lib/browser' import {atoms as a} from '#/alf' import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' -import {useVideoVolumeState} from '../../VideoVolumeContext' +import {useVideoVolumeState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' import {ControlButton} from './ControlButton' export function VolumeControl({ diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/utils.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/utils.tsx similarity index 96% rename from src/view/com/util/post-embeds/VideoEmbedInner/web-controls/utils.tsx rename to src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/utils.tsx index 108814ea2b..320f61a5f8 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/utils.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/utils.tsx @@ -1,9 +1,9 @@ -import React, {useCallback, useEffect, useRef, useState} from 'react' +import {type RefObject, useCallback, useEffect, useRef, useState} from 'react' import {isSafari} from '#/lib/browser' -import {useVideoVolumeState} from '../../VideoVolumeContext' +import {useVideoVolumeState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' -export function useVideoElement(ref: React.RefObject) { +export function useVideoElement(ref: RefObject) { const [playing, setPlaying] = useState(false) const [muted, setMuted] = useState(true) const [currentTime, setCurrentTime] = useState(0) diff --git a/src/view/com/util/post-embeds/VideoVolumeContext.tsx b/src/components/Post/Embed/VideoEmbed/VideoVolumeContext.tsx similarity index 100% rename from src/view/com/util/post-embeds/VideoVolumeContext.tsx rename to src/components/Post/Embed/VideoEmbed/VideoVolumeContext.tsx diff --git a/src/view/com/util/post-embeds/VideoEmbed.tsx b/src/components/Post/Embed/VideoEmbed/index.tsx similarity index 96% rename from src/view/com/util/post-embeds/VideoEmbed.tsx rename to src/components/Post/Embed/VideoEmbed/index.tsx index b45027089a..fe29ecad63 100644 --- a/src/view/com/util/post-embeds/VideoEmbed.tsx +++ b/src/components/Post/Embed/VideoEmbed/index.tsx @@ -5,13 +5,13 @@ import {AppBskyEmbedVideo} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {ConstrainedImage} from '#/view/com/util/images/AutoSizedImage' -import {VideoEmbedInnerNative} from '#/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative' import {atoms as a, useTheme} from '#/alf' import {Button} from '#/components/Button' import {useThrottledValue} from '#/components/hooks/useThrottledValue' import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' -import {ErrorBoundary} from '../ErrorBoundary' +import {VideoEmbedInnerNative} from './VideoEmbedInner/VideoEmbedInnerNative' import * as VideoFallback from './VideoEmbedInner/VideoFallback' interface Props { diff --git a/src/view/com/util/post-embeds/VideoEmbed.web.tsx b/src/components/Post/Embed/VideoEmbed/index.web.tsx similarity index 97% rename from src/view/com/util/post-embeds/VideoEmbed.web.tsx rename to src/components/Post/Embed/VideoEmbed/index.web.tsx index b0ded67548..53adc3b6aa 100644 --- a/src/view/com/util/post-embeds/VideoEmbed.web.tsx +++ b/src/components/Post/Embed/VideoEmbed/index.web.tsx @@ -5,16 +5,16 @@ import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {isFirefox} from '#/lib/browser' +import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {ConstrainedImage} from '#/view/com/util/images/AutoSizedImage' +import {atoms as a} from '#/alf' +import {useIsWithinMessage} from '#/components/dms/MessageContext' +import {useFullscreen} from '#/components/hooks/useFullscreen' import { HLSUnsupportedError, VideoEmbedInnerWeb, VideoNotFoundError, -} from '#/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb' -import {atoms as a} from '#/alf' -import {useIsWithinMessage} from '#/components/dms/MessageContext' -import {useFullscreen} from '#/components/hooks/useFullscreen' -import {ErrorBoundary} from '../ErrorBoundary' +} from '#/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb' import {useActiveVideoWeb} from './ActiveVideoWebContext' import * as VideoFallback from './VideoEmbedInner/VideoFallback' diff --git a/src/components/Post/Embed/index.tsx b/src/components/Post/Embed/index.tsx new file mode 100644 index 0000000000..ace85dc984 --- /dev/null +++ b/src/components/Post/Embed/index.tsx @@ -0,0 +1,332 @@ +import React from 'react' +import {View} from 'react-native' +import { + type $Typed, + type AppBskyFeedDefs, + AppBskyFeedPost, + AtUri, + moderatePost, + RichText as RichTextAPI, +} from '@atproto/api' +import {Trans} from '@lingui/macro' +import {useQueryClient} from '@tanstack/react-query' + +import {usePalette} from '#/lib/hooks/usePalette' +import {makeProfileLink} from '#/lib/routes/links' +import {useModerationOpts} from '#/state/preferences/moderation-opts' +import {unstableCacheProfileView} from '#/state/queries/profile' +import {useSession} from '#/state/session' +import {Link} from '#/view/com/util/Link' +import {PostMeta} from '#/view/com/util/PostMeta' +import {atoms as a, useTheme} from '#/alf' +import {ContentHider} from '#/components/moderation/ContentHider' +import {PostAlerts} from '#/components/moderation/PostAlerts' +import {RichText} from '#/components/RichText' +import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard' +import {SubtleWebHover} from '#/components/SubtleWebHover' +import * as bsky from '#/types/bsky' +import { + type Embed as TEmbed, + type EmbedType, + parseEmbed, +} from '#/types/bsky/post' +import {ExternalEmbed} from './ExternalEmbed' +import {ModeratedFeedEmbed} from './FeedEmbed' +import {ImageEmbed} from './ImageEmbed' +import {ModeratedListEmbed} from './ListEmbed' +import {PostPlaceholder as PostPlaceholderText} from './PostPlaceholder' +import { + type CommonProps, + type EmbedProps, + PostEmbedViewContext, + QuoteEmbedViewContext, +} from './types' +import {VideoEmbed} from './VideoEmbed' + +export {PostEmbedViewContext, QuoteEmbedViewContext} from './types' + +export function Embed({embed: rawEmbed, ...rest}: EmbedProps) { + const embed = parseEmbed(rawEmbed) + + switch (embed.type) { + case 'images': + case 'link': + case 'video': { + return + } + case 'feed': + case 'list': + case 'starter_pack': + case 'labeler': + case 'post': + case 'post_not_found': + case 'post_blocked': + case 'post_detached': { + return + } + case 'post_with_media': { + return ( + + + + + ) + } + default: { + return null + } + } +} + +function MediaEmbed({ + embed, + ...rest +}: CommonProps & { + embed: TEmbed +}) { + switch (embed.type) { + case 'images': { + return ( + + + + ) + } + case 'link': { + return ( + + + + ) + } + case 'video': { + return ( + + + + ) + } + default: { + return null + } + } +} + +function RecordEmbed({ + embed, + ...rest +}: CommonProps & { + embed: TEmbed +}) { + switch (embed.type) { + case 'feed': { + return ( + + + + ) + } + case 'list': { + return ( + + + + ) + } + case 'starter_pack': { + return ( + + + + ) + } + case 'labeler': { + // not implemented + return null + } + case 'post': { + if (rest.isWithinQuote && !rest.allowNestedQuotes) { + return null + } + + return ( + + ) + } + case 'post_not_found': { + return ( + + Deleted + + ) + } + case 'post_blocked': { + return ( + + Blocked + + ) + } + case 'post_detached': { + return + } + default: { + return null + } + } +} + +export function PostDetachedEmbed({ + embed, +}: { + embed: EmbedType<'post_detached'> +}) { + const {currentAccount} = useSession() + const isViewerOwner = currentAccount?.did + ? embed.view.uri.includes(currentAccount.did) + : false + + return ( + + {isViewerOwner ? ( + Removed by you + ) : ( + Removed by author + )} + + ) +} + +/* + * Nests parent `Embed` component and therefore must live in this file to avoid + * circular imports. + */ +export function QuoteEmbed({ + embed, + onOpen, + style, + isWithinQuote: parentIsWithinQuote, + allowNestedQuotes: parentAllowNestedQuotes, +}: Omit & { + embed: EmbedType<'post'> + viewContext?: QuoteEmbedViewContext +}) { + const moderationOpts = useModerationOpts() + const quote = React.useMemo<$Typed>( + () => ({ + ...embed.view, + $type: 'app.bsky.feed.defs#postView', + record: embed.view.value, + embed: embed.view.embeds?.[0], + }), + [embed], + ) + const moderation = React.useMemo(() => { + return moderationOpts ? moderatePost(quote, moderationOpts) : undefined + }, [quote, moderationOpts]) + + const t = useTheme() + const queryClient = useQueryClient() + const pal = usePalette('default') + const itemUrip = new AtUri(quote.uri) + const itemHref = makeProfileLink(quote.author, 'post', itemUrip.rkey) + const itemTitle = `Post by ${quote.author.handle}` + + const richText = React.useMemo(() => { + if ( + !bsky.dangerousIsType( + quote.record, + AppBskyFeedPost.isRecord, + ) + ) + return undefined + const {text, facets} = quote.record + return text.trim() + ? new RichTextAPI({text: text, facets: facets}) + : undefined + }, [quote.record]) + + const onBeforePress = React.useCallback(() => { + unstableCacheProfileView(queryClient, quote.author) + onOpen?.() + }, [queryClient, quote.author, onOpen]) + + const [hover, setHover] = React.useState(false) + return ( + { + setHover(true) + }} + onPointerLeave={() => { + setHover(false) + }}> + + + + + + + {moderation ? ( + + ) : null} + {richText ? ( + + ) : null} + {quote.embed && ( + + )} + + + + ) +} diff --git a/src/components/Post/Embed/types.ts b/src/components/Post/Embed/types.ts new file mode 100644 index 0000000000..b719d00b4e --- /dev/null +++ b/src/components/Post/Embed/types.ts @@ -0,0 +1,25 @@ +import {type StyleProp, type ViewStyle} from 'react-native' +import {type AppBskyFeedDefs, type ModerationDecision} from '@atproto/api' + +export enum PostEmbedViewContext { + ThreadHighlighted = 'ThreadHighlighted', + Feed = 'Feed', + FeedEmbedRecordWithMedia = 'FeedEmbedRecordWithMedia', +} + +export enum QuoteEmbedViewContext { + FeedEmbedRecordWithMedia = PostEmbedViewContext.FeedEmbedRecordWithMedia, +} + +export type CommonProps = { + moderation?: ModerationDecision + onOpen?: () => void + style?: StyleProp + viewContext?: PostEmbedViewContext + isWithinQuote?: boolean + allowNestedQuotes?: boolean +} + +export type EmbedProps = CommonProps & { + embed?: AppBskyFeedDefs.PostView['embed'] +} diff --git a/src/components/dms/ActionsWrapper.tsx b/src/components/dms/ActionsWrapper.tsx index 120a5f8ad9..eb9f0a09a4 100644 --- a/src/components/dms/ActionsWrapper.tsx +++ b/src/components/dms/ActionsWrapper.tsx @@ -1,5 +1,5 @@ import {View} from 'react-native' -import {ChatBskyConvoDefs} from '@atproto/api' +import {type ChatBskyConvoDefs} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' diff --git a/src/components/dms/MessageItemEmbed.tsx b/src/components/dms/MessageItemEmbed.tsx index f1c6189d06..6390300c1a 100644 --- a/src/components/dms/MessageItemEmbed.tsx +++ b/src/components/dms/MessageItemEmbed.tsx @@ -1,15 +1,16 @@ import React from 'react' import {useWindowDimensions, View} from 'react-native' -import {AppBskyEmbedRecord} from '@atproto/api' +import {type $Typed, type AppBskyEmbedRecord} from '@atproto/api' -import {PostEmbeds, PostEmbedViewContext} from '#/view/com/util/post-embeds' import {atoms as a, native, tokens, useTheme, web} from '#/alf' +import {PostEmbedViewContext} from '#/components/Post/Embed' +import {Embed} from '#/components/Post/Embed' import {MessageContextProvider} from './MessageContext' let MessageItemEmbed = ({ embed, }: { - embed: AppBskyEmbedRecord.View + embed: $Typed }): React.ReactNode => { const t = useTheme() const screen = useWindowDimensions() @@ -32,7 +33,7 @@ let MessageItemEmbed = ({ }), ]}> - - - - diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 17d0f94f7c..de060c6c22 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -72,7 +72,7 @@ import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {mimeToExt} from '#/lib/media/video/util' import {logEvent} from '#/lib/statsig/statsig' import {cleanError} from '#/lib/strings/errors' -import {colors, s} from '#/lib/styles' +import {colors} from '#/lib/styles' import {logger} from '#/logger' import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection' import {useDialogStateControlContext} from '#/state/dialogs' @@ -97,6 +97,7 @@ import { ExternalEmbedGif, ExternalEmbedLink, } from '#/view/com/composer/ExternalEmbed' +import {ExternalEmbedRemoveBtn} from '#/view/com/composer/ExternalEmbedRemoveBtn' import {GifAltTextDialog} from '#/view/com/composer/GifAltText' import {LabelsBtn} from '#/view/com/composer/labels/LabelsBtn' import {Gallery} from '#/view/com/composer/photos/Gallery' @@ -116,7 +117,6 @@ import {SelectVideoBtn} from '#/view/com/composer/videos/SelectVideoBtn' import {SubtitleDialogBtn} from '#/view/com/composer/videos/SubtitleDialog' import {VideoPreview} from '#/view/com/composer/videos/VideoPreview' import {VideoTranscodeProgress} from '#/view/com/composer/videos/VideoTranscodeProgress' -import {LazyQuoteEmbed, QuoteX} from '#/view/com/util/post-embeds/QuoteEmbed' import {Text} from '#/view/com/util/text/Text' import * as Toast from '#/view/com/util/Toast' import {UserAvatar} from '#/view/com/util/UserAvatar' @@ -125,6 +125,7 @@ import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' +import {LazyQuoteEmbed} from '#/components/Post/Embed/LazyQuoteEmbed' import * as Prompt from '#/components/Prompt' import {Text as NewText} from '#/components/Typography' import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet' @@ -1149,13 +1150,17 @@ function ComposerEmbeds({ )} {embed.quote?.uri ? ( - - + + {canRemoveQuote && ( - dispatch({type: 'embed_remove_quote'})} /> + dispatch({type: 'embed_remove_quote'})} + style={{top: 16}} + /> )} diff --git a/src/view/com/composer/ComposerReplyTo.tsx b/src/view/com/composer/ComposerReplyTo.tsx index 0ced143597..acab84f659 100644 --- a/src/view/com/composer/ComposerReplyTo.tsx +++ b/src/view/com/composer/ComposerReplyTo.tsx @@ -13,12 +13,13 @@ import {useLingui} from '@lingui/react' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' import {type ComposerOptsPostRef} from '#/state/shell/composer' -import {MaybeQuoteEmbed} from '#/view/com/util/post-embeds/QuoteEmbed' import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useTheme, web} from '#/alf' +import {QuoteEmbed} from '#/components/Post/Embed' import {Text} from '#/components/Typography' import {useSimpleVerificationState} from '#/components/verification' import {VerificationCheck} from '#/components/verification/VerificationCheck' +import {parseEmbed} from '#/types/bsky/post' export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) { const t = useTheme() @@ -51,6 +52,12 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) { } return null }, [embed]) + const parsedQuoteEmbed = quoteEmbed + ? parseEmbed({ + $type: 'app.bsky.embed.record#view', + ...quoteEmbed, + }) + : null const images = useMemo(() => { if (AppBskyEmbedImages.isView(embed)) { @@ -124,7 +131,9 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) { )} - {showFull && quoteEmbed && } + {showFull && parsedQuoteEmbed && parsedQuoteEmbed.type === 'post' && ( + + )} ) diff --git a/src/view/com/composer/ExternalEmbed.tsx b/src/view/com/composer/ExternalEmbed.tsx index d819b28b72..e4bdabac32 100644 --- a/src/view/com/composer/ExternalEmbed.tsx +++ b/src/view/com/composer/ExternalEmbed.tsx @@ -1,19 +1,20 @@ import React from 'react' -import {StyleProp, View, ViewStyle} from 'react-native' +import {type StyleProp, View, type ViewStyle} from 'react-native' import {cleanError} from '#/lib/strings/errors' import { useResolveGifQuery, useResolveLinkQuery, } from '#/state/queries/resolve-link' -import {Gif} from '#/state/queries/tenor' +import {type Gif} from '#/state/queries/tenor' import {ExternalEmbedRemoveBtn} from '#/view/com/composer/ExternalEmbedRemoveBtn' -import {ExternalLinkEmbed} from '#/view/com/util/post-embeds/ExternalLinkEmbed' import {atoms as a, useTheme} from '#/alf' import {Loader} from '#/components/Loader' +import {ExternalEmbed} from '#/components/Post/Embed/ExternalEmbed' +import {ModeratedFeedEmbed} from '#/components/Post/Embed/FeedEmbed' +import {ModeratedListEmbed} from '#/components/Post/Embed/ListEmbed' import {Embed as StarterPackEmbed} from '#/components/StarterPack/StarterPackCard' import {Text} from '#/components/Typography' -import {MaybeFeedCard, MaybeListCard} from '../util/post-embeds' export const ExternalEmbedGif = ({ onRemove, @@ -44,7 +45,7 @@ export const ExternalEmbedGif = ({ {linkInfo ? ( - + ) : error ? ( @@ -80,7 +81,7 @@ export const ExternalEmbedLink = ({ if (data) { if (data.type === 'external') { return ( - ) } else if (data.kind === 'feed') { - return + return ( + + ) } else if (data.kind === 'list') { - return + return ( + + ) } else if (data.kind === 'starter-pack') { return } diff --git a/src/view/com/composer/ExternalEmbedRemoveBtn.tsx b/src/view/com/composer/ExternalEmbedRemoveBtn.tsx index 92102f8478..1e363d0184 100644 --- a/src/view/com/composer/ExternalEmbedRemoveBtn.tsx +++ b/src/view/com/composer/ExternalEmbedRemoveBtn.tsx @@ -2,22 +2,27 @@ import {View} from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {atoms as a} from '#/alf' +import {atoms as a, useTheme, type ViewStyleProp} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' -export function ExternalEmbedRemoveBtn({onRemove}: {onRemove: () => void}) { +export function ExternalEmbedRemoveBtn({ + onRemove, + style, +}: {onRemove: () => void} & ViewStyleProp) { + const t = useTheme() const {_} = useLingui() return ( - + diff --git a/src/view/com/composer/GifAltText.tsx b/src/view/com/composer/GifAltText.tsx index 4d2539c4e3..ceee17eaa0 100644 --- a/src/view/com/composer/GifAltText.tsx +++ b/src/view/com/composer/GifAltText.tsx @@ -6,23 +6,23 @@ import {useLingui} from '@lingui/react' import {HITSLOP_10, MAX_ALT_TEXT} from '#/lib/constants' import {parseAltFromGIFDescription} from '#/lib/gif-alt-text' import { - EmbedPlayerParams, + type EmbedPlayerParams, parseEmbedPlayerFromUrl, } from '#/lib/strings/embed-player' import {isAndroid} from '#/platform/detection' import {useResolveGifQuery} from '#/state/queries/resolve-link' -import {Gif} from '#/state/queries/tenor' +import {type Gif} from '#/state/queries/tenor' import {AltTextCounterWrapper} from '#/view/com/composer/AltTextCounterWrapper' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' -import {DialogControlProps} from '#/components/Dialog' +import {type DialogControlProps} from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' +import {GifEmbed} from '#/components/Post/Embed/ExternalEmbed/Gif' import {Text} from '#/components/Typography' -import {GifEmbed} from '../util/post-embeds/GifEmbed' import {AltTextReminder} from './photos/Gallery' export function GifAltTextDialog({ diff --git a/src/view/com/composer/labels/LabelsBtn.tsx b/src/view/com/composer/labels/LabelsBtn.tsx index 9548ed0655..902d89b7bb 100644 --- a/src/view/com/composer/labels/LabelsBtn.tsx +++ b/src/view/com/composer/labels/LabelsBtn.tsx @@ -4,10 +4,10 @@ import {useLingui} from '@lingui/react' import { ADULT_CONTENT_LABELS, - AdultSelfLabel, + type AdultSelfLabel, OTHER_SELF_LABELS, - OtherSelfLabel, - SelfLabel, + type OtherSelfLabel, + type SelfLabel, } from '#/lib/moderation' import {isWeb} from '#/platform/detection' import {atoms as a, native, useTheme, web} from '#/alf' diff --git a/src/view/com/composer/photos/ImageAltTextDialog.tsx b/src/view/com/composer/photos/ImageAltTextDialog.tsx index c0ce32af31..724149937c 100644 --- a/src/view/com/composer/photos/ImageAltTextDialog.tsx +++ b/src/view/com/composer/photos/ImageAltTextDialog.tsx @@ -1,5 +1,5 @@ import React from 'react' -import {ImageStyle, useWindowDimensions, View} from 'react-native' +import {type ImageStyle, useWindowDimensions, View} from 'react-native' import {Image} from 'expo-image' import {msg, Plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -7,12 +7,12 @@ import {useLingui} from '@lingui/react' import {MAX_ALT_TEXT} from '#/lib/constants' import {enforceLen} from '#/lib/strings/helpers' import {isAndroid, isWeb} from '#/platform/detection' -import {ComposerImage} from '#/state/gallery' +import {type ComposerImage} from '#/state/gallery' import {AltTextCounterWrapper} from '#/view/com/composer/AltTextCounterWrapper' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' -import {DialogControlProps} from '#/components/Dialog' +import {type DialogControlProps} from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {Text} from '#/components/Typography' diff --git a/src/view/com/composer/photos/OpenCameraBtn.tsx b/src/view/com/composer/photos/OpenCameraBtn.tsx index 1c9440eb16..8bd1aa27b0 100644 --- a/src/view/com/composer/photos/OpenCameraBtn.tsx +++ b/src/view/com/composer/photos/OpenCameraBtn.tsx @@ -8,7 +8,7 @@ import {useCameraPermission} from '#/lib/hooks/usePermissions' import {openCamera} from '#/lib/media/picker' import {logger} from '#/logger' import {isMobileWeb, isNative} from '#/platform/detection' -import {ComposerImage, createComposerImage} from '#/state/gallery' +import {type ComposerImage, createComposerImage} from '#/state/gallery' import {atoms as a, useTheme} from '#/alf' import {Button} from '#/components/Button' import {Camera_Stroke2_Corner0_Rounded as Camera} from '#/components/icons/Camera' diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 5184047cbb..15f5539c9d 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -46,7 +46,6 @@ import {PostThreadFollowBtn} from '#/view/com/post-thread/PostThreadFollowBtn' import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' import {Link, TextLink} from '#/view/com/util/Link' import {formatCount} from '#/view/com/util/numeric/format' -import {PostEmbeds, PostEmbedViewContext} from '#/view/com/util/post-embeds' import {PostMeta} from '#/view/com/util/PostMeta' import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useTheme} from '#/alf' @@ -62,6 +61,7 @@ import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe' import {PostAlerts} from '#/components/moderation/PostAlerts' import {PostHider} from '#/components/moderation/PostHider' import {type AppModerationCause} from '#/components/Pills' +import {Embed, PostEmbedViewContext} from '#/components/Post/Embed' import {PostControls} from '#/components/PostControls' import * as Prompt from '#/components/Prompt' import {RichText} from '#/components/RichText' @@ -465,7 +465,7 @@ let PostThreadItemLoaded = ({ ) : undefined} {post.embed && ( - - ) : undefined} {post.embed ? ( - - void diff --git a/src/view/com/util/images/ImageLayoutGrid.tsx b/src/view/com/util/images/ImageLayoutGrid.tsx index b91d7a7adb..757d952a19 100644 --- a/src/view/com/util/images/ImageLayoutGrid.tsx +++ b/src/view/com/util/images/ImageLayoutGrid.tsx @@ -3,8 +3,8 @@ import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native' import {type AnimatedRef, useAnimatedRef} from 'react-native-reanimated' import {type AppBskyEmbedImages} from '@atproto/api' -import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types' import {atoms as a, useBreakpoints} from '#/alf' +import {PostEmbedViewContext} from '#/components/Post/Embed/types' import {type Dimensions} from '../../lightbox/ImageViewing/@types' import {GalleryItem} from './Gallery' diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx deleted file mode 100644 index f788af1f86..0000000000 --- a/src/view/com/util/post-embeds/QuoteEmbed.tsx +++ /dev/null @@ -1,337 +0,0 @@ -import React from 'react' -import { - StyleProp, - StyleSheet, - TouchableOpacity, - View, - ViewStyle, -} from 'react-native' -import { - AppBskyEmbedExternal, - AppBskyEmbedImages, - AppBskyEmbedRecord, - AppBskyEmbedRecordWithMedia, - AppBskyEmbedVideo, - AppBskyFeedDefs, - AppBskyFeedPost, - moderatePost, - ModerationDecision, - RichText as RichTextAPI, -} from '@atproto/api' -import {AtUri} from '@atproto/api' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {useQueryClient} from '@tanstack/react-query' - -import {HITSLOP_20} from '#/lib/constants' -import {usePalette} from '#/lib/hooks/usePalette' -import {InfoCircleIcon} from '#/lib/icons' -import {makeProfileLink} from '#/lib/routes/links' -import {s} from '#/lib/styles' -import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {precacheProfile} from '#/state/queries/profile' -import {useResolveLinkQuery} from '#/state/queries/resolve-link' -import {useSession} from '#/state/session' -import {atoms as a, useTheme} from '#/alf' -import {RichText} from '#/components/RichText' -import {SubtleWebHover} from '#/components/SubtleWebHover' -import * as bsky from '#/types/bsky' -import {ContentHider} from '../../../../components/moderation/ContentHider' -import {PostAlerts} from '../../../../components/moderation/PostAlerts' -import {Link} from '../Link' -import {PostMeta} from '../PostMeta' -import {Text} from '../text/Text' -import {PostEmbeds} from '.' -import {QuoteEmbedViewContext} from './types' - -export function MaybeQuoteEmbed({ - embed, - onOpen, - style, - allowNestedQuotes, - viewContext, -}: { - embed: AppBskyEmbedRecord.View - onOpen?: () => void - style?: StyleProp - allowNestedQuotes?: boolean - viewContext?: QuoteEmbedViewContext -}) { - const t = useTheme() - const pal = usePalette('default') - const {currentAccount} = useSession() - if ( - AppBskyEmbedRecord.isViewRecord(embed.record) && - AppBskyFeedPost.isRecord(embed.record.value) && - AppBskyFeedPost.validateRecord(embed.record.value).success - ) { - return ( - - ) - } else if (AppBskyEmbedRecord.isViewBlocked(embed.record)) { - return ( - - - - Blocked - - - ) - } else if (AppBskyEmbedRecord.isViewNotFound(embed.record)) { - return ( - - - - Deleted - - - ) - } else if (AppBskyEmbedRecord.isViewDetached(embed.record)) { - const isViewerOwner = currentAccount?.did - ? embed.record.uri.includes(currentAccount.did) - : false - return ( - - - - {isViewerOwner ? ( - Removed by you - ) : ( - Removed by author - )} - - - ) - } - return null -} - -function QuoteEmbedModerated({ - viewRecord, - onOpen, - style, - allowNestedQuotes, - viewContext, -}: { - viewRecord: AppBskyEmbedRecord.ViewRecord - onOpen?: () => void - style?: StyleProp - allowNestedQuotes?: boolean - viewContext?: QuoteEmbedViewContext -}) { - const moderationOpts = useModerationOpts() - const postView = React.useMemo( - () => viewRecordToPostView(viewRecord), - [viewRecord], - ) - const moderation = React.useMemo(() => { - return moderationOpts ? moderatePost(postView, moderationOpts) : undefined - }, [postView, moderationOpts]) - - return ( - - ) -} - -export function QuoteEmbed({ - quote, - moderation, - onOpen, - style, - allowNestedQuotes, -}: { - quote: AppBskyFeedDefs.PostView - moderation?: ModerationDecision - onOpen?: () => void - style?: StyleProp - allowNestedQuotes?: boolean - viewContext?: QuoteEmbedViewContext -}) { - const t = useTheme() - const queryClient = useQueryClient() - const pal = usePalette('default') - const itemUrip = new AtUri(quote.uri) - const itemHref = makeProfileLink(quote.author, 'post', itemUrip.rkey) - const itemTitle = `Post by ${quote.author.handle}` - - const richText = React.useMemo(() => { - if ( - !bsky.dangerousIsType( - quote.record, - AppBskyFeedPost.isRecord, - ) - ) - return undefined - const {text, facets} = quote.record - return text.trim() - ? new RichTextAPI({text: text, facets: facets}) - : undefined - }, [quote.record]) - - const embed = React.useMemo(() => { - const e = quote.embed - - if (allowNestedQuotes) { - return e - } else { - if ( - AppBskyEmbedImages.isView(e) || - AppBskyEmbedExternal.isView(e) || - AppBskyEmbedVideo.isView(e) - ) { - return e - } else if ( - AppBskyEmbedRecordWithMedia.isView(e) && - (AppBskyEmbedImages.isView(e.media) || - AppBskyEmbedExternal.isView(e.media) || - AppBskyEmbedVideo.isView(e.media)) - ) { - return e.media - } - } - }, [quote.embed, allowNestedQuotes]) - - const onBeforePress = React.useCallback(() => { - precacheProfile(queryClient, quote.author) - onOpen?.() - }, [queryClient, quote.author, onOpen]) - - const [hover, setHover] = React.useState(false) - return ( - { - setHover(true) - }} - onPointerLeave={() => { - setHover(false) - }}> - - - - - - - {moderation ? ( - - ) : null} - {richText ? ( - - ) : null} - {embed && } - - - - ) -} - -export function QuoteX({onRemove}: {onRemove: () => void}) { - const {_} = useLingui() - return ( - - - - ) -} - -export function LazyQuoteEmbed({uri}: {uri: string}) { - const {data} = useResolveLinkQuery(uri) - const moderationOpts = useModerationOpts() - if (!data || data.type !== 'record' || data.kind !== 'post') { - return null - } - const moderation = moderationOpts - ? moderatePost(data.view, moderationOpts) - : undefined - return -} - -function viewRecordToPostView( - viewRecord: AppBskyEmbedRecord.ViewRecord, -): AppBskyFeedDefs.PostView { - const {value, embeds, ...rest} = viewRecord - return { - ...rest, - $type: 'app.bsky.feed.defs#postView', - record: value, - embed: embeds?.[0], - } -} - -const styles = StyleSheet.create({ - errorContainer: { - flexDirection: 'row', - alignItems: 'center', - gap: 4, - borderRadius: 8, - marginTop: 8, - paddingVertical: 14, - paddingHorizontal: 14, - borderWidth: StyleSheet.hairlineWidth, - }, - alert: { - marginBottom: 6, - }, -}) diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx deleted file mode 100644 index 4cf71f9486..0000000000 --- a/src/view/com/util/post-embeds/index.tsx +++ /dev/null @@ -1,327 +0,0 @@ -import React from 'react' -import { - InteractionManager, - type StyleProp, - StyleSheet, - View, - type ViewStyle, -} from 'react-native' -import { - type AnimatedRef, - measure, - type MeasuredDimensions, - runOnJS, - runOnUI, -} from 'react-native-reanimated' -import {Image} from 'expo-image' -import { - AppBskyEmbedExternal, - AppBskyEmbedImages, - AppBskyEmbedRecord, - AppBskyEmbedRecordWithMedia, - AppBskyEmbedVideo, - AppBskyFeedDefs, - AppBskyGraphDefs, - moderateFeedGenerator, - moderateUserList, - type ModerationDecision, -} from '@atproto/api' - -import {usePalette} from '#/lib/hooks/usePalette' -import {useLightboxControls} from '#/state/lightbox' -import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {FeedSourceCard} from '#/view/com/feeds/FeedSourceCard' -import {atoms as a, useTheme} from '#/alf' -import * as ListCard from '#/components/ListCard' -import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard' -import {ContentHider} from '../../../../components/moderation/ContentHider' -import {type Dimensions} from '../../lightbox/ImageViewing/@types' -import {AutoSizedImage} from '../images/AutoSizedImage' -import {ImageLayoutGrid} from '../images/ImageLayoutGrid' -import {ExternalLinkEmbed} from './ExternalLinkEmbed' -import {MaybeQuoteEmbed} from './QuoteEmbed' -import {PostEmbedViewContext, QuoteEmbedViewContext} from './types' -import {VideoEmbed} from './VideoEmbed' - -export * from './types' - -type Embed = - | AppBskyEmbedRecord.View - | AppBskyEmbedImages.View - | AppBskyEmbedVideo.View - | AppBskyEmbedExternal.View - | AppBskyEmbedRecordWithMedia.View - | {$type: string; [k: string]: unknown} - -export function PostEmbeds({ - embed, - moderation, - onOpen, - style, - allowNestedQuotes, - viewContext, -}: { - embed?: Embed - moderation?: ModerationDecision - onOpen?: () => void - style?: StyleProp - allowNestedQuotes?: boolean - viewContext?: PostEmbedViewContext -}) { - const {openLightbox} = useLightboxControls() - - // quote post with media - // = - if (AppBskyEmbedRecordWithMedia.isView(embed)) { - return ( - - - - - ) - } - - if (AppBskyEmbedRecord.isView(embed)) { - // custom feed embed (i.e. generator view) - if (AppBskyFeedDefs.isGeneratorView(embed.record)) { - return ( - - - - ) - } - - // list embed - if (AppBskyGraphDefs.isListView(embed.record)) { - return ( - - - - ) - } - - // starter pack embed - if (AppBskyGraphDefs.isStarterPackViewBasic(embed.record)) { - return ( - - - - ) - } - - // quote post - // = - return ( - - ) - } - - // image embed - // = - if (AppBskyEmbedImages.isView(embed)) { - const {images} = embed - - if (images.length > 0) { - const items = embed.images.map(img => ({ - uri: img.fullsize, - thumbUri: img.thumb, - alt: img.alt, - dimensions: img.aspectRatio ?? null, - })) - const _openLightbox = ( - index: number, - thumbRects: (MeasuredDimensions | null)[], - fetchedDims: (Dimensions | null)[], - ) => { - openLightbox({ - images: items.map((item, i) => ({ - ...item, - thumbRect: thumbRects[i] ?? null, - thumbDimensions: fetchedDims[i] ?? null, - type: 'image', - })), - index, - }) - } - const onPress = ( - index: number, - refs: AnimatedRef[], - fetchedDims: (Dimensions | null)[], - ) => { - runOnUI(() => { - 'worklet' - const rects: (MeasuredDimensions | null)[] = [] - for (const r of refs) { - rects.push(measure(r)) - } - runOnJS(_openLightbox)(index, rects, fetchedDims) - })() - } - const onPressIn = (_: number) => { - InteractionManager.runAfterInteractions(() => { - Image.prefetch(items.map(i => i.uri)) - }) - } - - if (images.length === 1) { - const image = images[0] - return ( - - - - onPress(0, [containerRef], [dims]) - } - onPressIn={() => onPressIn(0)} - hideBadge={ - viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia - } - /> - - - ) - } - - return ( - - - - - - ) - } - } - - // external link embed - // = - if (AppBskyEmbedExternal.isView(embed)) { - const link = embed.external - return ( - - - - ) - } - - // video embed - // = - if (AppBskyEmbedVideo.isView(embed)) { - return ( - - - - ) - } - - return -} - -export function MaybeFeedCard({view}: {view: AppBskyFeedDefs.GeneratorView}) { - const pal = usePalette('default') - const moderationOpts = useModerationOpts() - const moderation = React.useMemo(() => { - return moderationOpts - ? moderateFeedGenerator(view, moderationOpts) - : undefined - }, [view, moderationOpts]) - - return ( - - - - ) -} - -export function MaybeListCard({view}: {view: AppBskyGraphDefs.ListView}) { - const moderationOpts = useModerationOpts() - const moderation = React.useMemo(() => { - return moderationOpts ? moderateUserList(view, moderationOpts) : undefined - }, [view, moderationOpts]) - const t = useTheme() - - return ( - - - - - - ) -} - -const styles = StyleSheet.create({ - altContainer: { - backgroundColor: 'rgba(0, 0, 0, 0.75)', - borderRadius: 6, - paddingHorizontal: 6, - paddingVertical: 3, - position: 'absolute', - right: 6, - bottom: 6, - }, - alt: { - color: 'white', - fontSize: 7, - fontWeight: '600', - }, - customFeedOuter: { - borderWidth: StyleSheet.hairlineWidth, - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 12, - }, -}) diff --git a/src/view/com/util/post-embeds/types.ts b/src/view/com/util/post-embeds/types.ts deleted file mode 100644 index 08e9032768..0000000000 --- a/src/view/com/util/post-embeds/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -export enum PostEmbedViewContext { - ThreadHighlighted = 'ThreadHighlighted', - Feed = 'Feed', - FeedEmbedRecordWithMedia = 'FeedEmbedRecordWithMedia', -} - -export enum QuoteEmbedViewContext { - FeedEmbedRecordWithMedia = PostEmbedViewContext.FeedEmbedRecordWithMedia, -} From d2de074dc4cac54a694ef2fa5af037a1d5de1cf3 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 13 Jun 2025 13:37:47 -0500 Subject: [PATCH 6/7] Disable default stack traces that are causing issues (#8487) --- src/logger/sentry/setup/index.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/logger/sentry/setup/index.ts b/src/logger/sentry/setup/index.ts index 3819211f3c..f05a7fc833 100644 --- a/src/logger/sentry/setup/index.ts +++ b/src/logger/sentry/setup/index.ts @@ -38,4 +38,12 @@ init({ */ `Network request failed`, ], + /** + * Does not affect traces of error events or other logs, just disables + * automatically attaching stack traces to events. This helps us group events + * and prevents explosions of separate issues. + * + * @see https://docs.sentry.io/platforms/react-native/configuration/options/#attach-stacktrace + */ + attachStacktrace: false, }) From 7cd607f523b715bdea4b01d9203610e764b4fbe3 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 13 Jun 2025 15:11:42 -0500 Subject: [PATCH 7/7] Bump API SDK to fix `and/or` mute words bug (#8488) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f490f108f0..691a6e94d0 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "icons:optimize": "svgo -f ./assets/icons" }, "dependencies": { - "@atproto/api": "^0.15.14", + "@atproto/api": "^0.15.15", "@bitdrift/react-native": "^0.6.8", "@braintree/sanitize-url": "^6.0.2", "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet", diff --git a/yarn.lock b/yarn.lock index d9dbc18063..fb126ae730 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,10 +63,10 @@ "@atproto/xrpc" "^0.7.0" "@atproto/xrpc-server" "^0.7.18" -"@atproto/api@^0.15.14": - version "0.15.14" - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.15.14.tgz#41ff6ce2e7603119a005b7b5ce8e64551ec84879" - integrity sha512-FHEMAdscG+r2OFcZUIzPyTDpwzRAyinRsIIaTcuqe0MgZWF4CEGNAKPos0IbecBzMxTOzUHE18dQDKhoXMdgvg== +"@atproto/api@^0.15.15": + version "0.15.15" + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.15.15.tgz#a506a1a26f3dfef9adb77234f451c0e784b071e7" + integrity sha512-Wn8jv76pCvffnkNj68w0CGZ3PT4DJGM8DUZnYq9kEW2im6jbRBYI0yYrHNhSiE92A5Ox0HjL2jMhalsI2p9VlQ== dependencies: "@atproto/common-web" "^0.4.2" "@atproto/lexicon" "^0.4.11"