Merge remote-tracking branch 'origin/main' into dm-composer
* origin/main: (32 commits) Don't call getProfiles if there are no actors (#10196) Update browserslist (#10195) Fix layout shift when liking a post on Android (#10190) Revert PostThread parent chunk size for native (#10193) Nightly source-language update Bump expo-paste-input (#10187) Add some more clarity to the RQ docs (#10120) [APP-2038] fix feeds not refreshing (#10167) [APP-1999] Fix ReportDialog Android hang by narrowing dependency arrays (#10171) Minor hotkeys nit (#10184) Fixes Hotkeys causing native to crash immediately on start (#10180) Nightly source-language update Fix OOM crash - disable tanstack extension integration (#10169) Create global keyboard shortcut handler (#10145) Warn when useState get/set names mismatch (#10166) Fix bottom bar badge text padding (#10162) Scale profile badge icons with font size (#10161) Add `no-extraneous-dependencies` and `no-nodejs-modules` eslint rules (#10151) Fix `useResolveUriQuery` (#10163) Nightly source-language update ...
This commit is contained in:
@@ -431,16 +431,30 @@ yarn intl:compile # Compile translations for runtime
|
||||
// src/state/queries/profile.ts
|
||||
import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
// Query key pattern
|
||||
const RQKEY_ROOT = 'profile'
|
||||
export const RQKEY = (did: string) => [RQKEY_ROOT, did]
|
||||
import {createQueryKey} from '#/state/queries/util'
|
||||
|
||||
// Query hook
|
||||
/*
|
||||
* Query key name should match the query hook name for consistency
|
||||
*/
|
||||
const profileQueryKeyRoot = 'profile'
|
||||
|
||||
/*
|
||||
* Use object params and createQueryKey helper for better readability and to
|
||||
* avoid bugs with parameter order or types.
|
||||
*/
|
||||
export const createProfileQueryKey = (args: {did: string}) =>
|
||||
createQueryKey(profileQueryKeyRoot, args)
|
||||
|
||||
/*
|
||||
* Query hook should be named use[Name]Query, where [Name] describes the data
|
||||
* being fetched. This is not a strict requirement, but it's a helpful
|
||||
* convention for discoverability
|
||||
*/
|
||||
export function useProfileQuery({did}: {did: string}) {
|
||||
const agent = useAgent()
|
||||
|
||||
return useQuery({
|
||||
queryKey: RQKEY(did),
|
||||
queryKey: createProfileQueryKey({did}),
|
||||
queryFn: async () => {
|
||||
const res = await agent.getProfile({actor: did})
|
||||
return res.data
|
||||
@@ -450,8 +464,12 @@ export function useProfileQuery({did}: {did: string}) {
|
||||
})
|
||||
}
|
||||
|
||||
// Mutation hook
|
||||
export function useUpdateProfile() {
|
||||
/*
|
||||
* Mutation hook should match the name of the query hook, but with "Mutation"
|
||||
* suffix. This is not a strict requirement, but it's a helpful convention for
|
||||
* discoverability and consistency.
|
||||
*/
|
||||
export function useProfileMutation() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
@@ -459,7 +477,9 @@ export function useUpdateProfile() {
|
||||
// Update logic
|
||||
},
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({queryKey: RQKEY(variables.did)})
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: createProfileQueryKey({did: variables.did}),
|
||||
})
|
||||
},
|
||||
onError: (error) => {
|
||||
if (isNetworkError(error)) {
|
||||
@@ -473,6 +493,24 @@ export function useUpdateProfile() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* If cache mutation is needed, include specific interfaces for the specific
|
||||
* mutations you require adjacent to the source queries. Naming should be
|
||||
* descriptive of the mutation's purpose, e.g. use[Name]CacheMutation. This is
|
||||
* not a strict requirement, but it's a helpful convention for discoverability
|
||||
* and consistency.
|
||||
*/
|
||||
export function useProfileCacheMutation() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return (data: Partial<Profile>) => {
|
||||
queryClient.setQueryData(createProfileQueryKey({did: data.did}), oldData => {
|
||||
if (!oldData) return oldData
|
||||
return {...oldData, ...data}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Stale Time Constants** (from `src/state/queries/index.ts`):
|
||||
@@ -491,7 +529,7 @@ export function useDraftsQuery() {
|
||||
const agent = useAgent()
|
||||
|
||||
return useInfiniteQuery({
|
||||
queryKey: ['drafts'],
|
||||
queryKey: createQueryKey('drafts'),
|
||||
queryFn: async ({pageParam}) => {
|
||||
const res = await agent.app.bsky.draft.getDrafts({cursor: pageParam})
|
||||
return res.data
|
||||
@@ -504,6 +542,19 @@ export function useDraftsQuery() {
|
||||
|
||||
To get all items from pages: `data?.pages.flatMap(page => page.items) ?? []`
|
||||
|
||||
**Persisted Queries**
|
||||
|
||||
To persist query data across app restarts, `createQueryKey` supports a third
|
||||
parameter called `options`, which has a `persistedVersion` property. When this
|
||||
property is set to a number, the query will be persisted.
|
||||
|
||||
When this property is updated (e.g. incremented), the persisted data will be cleared and replaced with the new data from the query function. This is useful for cases where the shape of the data has changed and old persisted data would no longer be valid.
|
||||
|
||||
```tsx
|
||||
export const createProfileQueryKey = (args: {did: string}) =>
|
||||
createQueryKey(profileQueryKeyRoot, args, {persistedVersion: 1})
|
||||
```
|
||||
|
||||
### Preferences (React Context)
|
||||
|
||||
```tsx
|
||||
|
||||
+14
-1
@@ -47,7 +47,6 @@ export default defineConfig(
|
||||
js.configs.recommended,
|
||||
tseslint.configs.recommendedTypeChecked,
|
||||
reactHooks.configs.flat.recommended,
|
||||
// @ts-expect-error https://github.com/un-ts/eslint-plugin-import-x/issues/439
|
||||
importX.flatConfigs.recommended,
|
||||
importX.flatConfigs.typescript,
|
||||
importX.flatConfigs['react-native'],
|
||||
@@ -62,6 +61,7 @@ export default defineConfig(
|
||||
'react-native': reactNative,
|
||||
'react-native-a11y': reactNativeA11y,
|
||||
'simple-import-sort': simpleImportSort,
|
||||
// @ts-expect-error - not sure why
|
||||
lingui,
|
||||
'react-compiler': reactCompiler,
|
||||
'bsky-internal': bskyInternal,
|
||||
@@ -127,6 +127,7 @@ export default defineConfig(
|
||||
*/
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
'react/hook-use-state': 'warn',
|
||||
'react/no-unescaped-entities': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'react-native/no-inline-styles': 'off',
|
||||
@@ -189,6 +190,18 @@ export default defineConfig(
|
||||
*/
|
||||
ignore: ['^#\/locale\/locales\/.+\/messages'],
|
||||
}],
|
||||
'import-x/no-extraneous-dependencies': ['error', {
|
||||
'whitelist': [
|
||||
// test files only
|
||||
'@jest/globals',
|
||||
// we only use a really simple util from this, and we know it will be present
|
||||
'expo-modules-core',
|
||||
// this is a dep for @atproto/api, but we absolutely need them in sync, so just
|
||||
// rely on the transient version
|
||||
'@atproto/common-web',
|
||||
]
|
||||
}],
|
||||
'import-x/no-nodejs-modules': 'error',
|
||||
|
||||
/**
|
||||
* TypeScript-specific rules
|
||||
|
||||
@@ -29,6 +29,7 @@ function getTagName(node) {
|
||||
return reversedIdentifiers.reverse().join('.')
|
||||
}
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
|
||||
@@ -3,6 +3,7 @@ const BANNED_IMPORTS = [
|
||||
'@fortawesome/free-solid-svg-icons',
|
||||
]
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
|
||||
@@ -10,6 +10,7 @@ const BANNED_IMPORT_PREFIXES = [
|
||||
'view/',
|
||||
]
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
|
||||
@@ -9,6 +9,7 @@ jest.mock('@react-native-async-storage/async-storage', () =>
|
||||
require('@react-native-async-storage/async-storage/jest/async-storage-mock'),
|
||||
)
|
||||
jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter', () => {
|
||||
// eslint-disable-next-line import-x/no-nodejs-modules
|
||||
const {EventEmitter} = require('events')
|
||||
return {
|
||||
__esModule: true,
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import {GestureHandlerRootView} from 'react-native-gesture-handler'
|
||||
import {SafeAreaProvider} from 'react-native-safe-area-context'
|
||||
import {render} from '@testing-library/react-native'
|
||||
|
||||
import {ThemeProvider} from '../src/lib/ThemeContext'
|
||||
import {type RootStoreModel, RootStoreProvider} from '../src/state'
|
||||
|
||||
const customRender = (ui: any, rootStore: RootStoreModel) =>
|
||||
render(
|
||||
<GestureHandlerRootView style={{flex: 1}}>
|
||||
<RootStoreProvider value={rootStore}>
|
||||
<ThemeProvider theme="light">
|
||||
<SafeAreaProvider>{ui}</SafeAreaProvider>
|
||||
</ThemeProvider>
|
||||
</RootStoreProvider>
|
||||
</GestureHandlerRootView>,
|
||||
)
|
||||
|
||||
// re-export everything
|
||||
export * from '@testing-library/react-native'
|
||||
|
||||
// override render method
|
||||
export {customRender as render}
|
||||
+9
-9
@@ -81,7 +81,7 @@
|
||||
"icons:optimize": "svgo -f ./assets/icons"
|
||||
},
|
||||
"dependencies": {
|
||||
"@atproto/api": "^0.19.3",
|
||||
"@atproto/api": "^0.19.5",
|
||||
"@bitdrift/react-native": "^0.6.8",
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
"@bsky.app/alf": "^0.1.7",
|
||||
@@ -111,7 +111,6 @@
|
||||
"@ipld/dag-cbor": "^9.2.0",
|
||||
"@lingui/core": "^5.9.2",
|
||||
"@lingui/react": "^5.9.2",
|
||||
"@mattermost/react-native-paste-input": "mattermost/react-native-paste-input",
|
||||
"@miblanchard/react-native-slider": "^2.6.0",
|
||||
"@mozzius/expo-dynamic-app-icon": "^1.8.0",
|
||||
"@react-native-async-storage/async-storage": "2.2.0",
|
||||
@@ -119,9 +118,9 @@
|
||||
"@react-navigation/native": "^7.1.33",
|
||||
"@react-navigation/native-stack": "^7.14.4",
|
||||
"@sentry/react-native": "~6.20.0",
|
||||
"@tanstack/query-async-storage-persister": "^5.25.0",
|
||||
"@tanstack/react-query": "5.25.0",
|
||||
"@tanstack/react-query-persist-client": "^5.25.0",
|
||||
"@tanstack/query-async-storage-persister": "^5.96.2",
|
||||
"@tanstack/react-query": "^5.96.2",
|
||||
"@tanstack/react-query-persist-client": "^5.96.2",
|
||||
"@tiptap/core": "^2.9.1",
|
||||
"@tiptap/extension-document": "^2.9.1",
|
||||
"@tiptap/extension-hard-break": "^2.9.1",
|
||||
@@ -169,6 +168,7 @@
|
||||
"expo-location": "~19.0.8",
|
||||
"expo-media-library": "~18.2.1",
|
||||
"expo-notifications": "~0.32.16",
|
||||
"expo-paste-input": "^0.1.12",
|
||||
"expo-privacy-sensitive": "^0.1.0",
|
||||
"expo-screen-orientation": "~9.0.8",
|
||||
"expo-sharing": "~14.0.8",
|
||||
@@ -202,6 +202,7 @@
|
||||
"react": "19.1.0",
|
||||
"react-compiler-runtime": "^19.1.0-rc.1",
|
||||
"react-dom": "19.1.0",
|
||||
"react-hotkeys-hook": "5.2.4",
|
||||
"react-image-crop": "^11.0.7",
|
||||
"react-is": "19",
|
||||
"react-keyed-flatten-children": "^5.0.0",
|
||||
@@ -249,7 +250,6 @@
|
||||
"@lingui/cli": "^5.9.2",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
|
||||
"@react-native/babel-preset": "0.81.5",
|
||||
"@react-native/eslint-config": "^0.81.5",
|
||||
"@react-native/typescript-config": "^0.81.5",
|
||||
"@sentry/webpack-plugin": "^3.2.2",
|
||||
"@testing-library/react-native": "^13.2.0",
|
||||
@@ -267,8 +267,8 @@
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-bsky-internal": "link:./eslint",
|
||||
"eslint-plugin-import-x": "^4.16.1",
|
||||
"eslint-plugin-lingui": "^0.11.0",
|
||||
"eslint-plugin-import-x": "^4.16.2",
|
||||
"eslint-plugin-lingui": "^0.12.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
@@ -290,7 +290,7 @@
|
||||
"svgo": "^3.3.2",
|
||||
"ts-plugin-sort-import-suggestions": "^1.0.4",
|
||||
"typescript": "^6.0.2",
|
||||
"typescript-eslint": "^8.57.2",
|
||||
"typescript-eslint": "^8.58.0",
|
||||
"webpack-bundle-analyzer": "^4.10.1"
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
diff --git a/node_modules/@mattermost/react-native-paste-input/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputManager.kt b/node_modules/@mattermost/react-native-paste-input/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputManager.kt
|
||||
index 4ed2307..ede1181 100644
|
||||
--- a/node_modules/@mattermost/react-native-paste-input/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputManager.kt
|
||||
+++ b/node_modules/@mattermost/react-native-paste-input/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputManager.kt
|
||||
@@ -54,7 +54,7 @@ class PasteTextInputManager(context: ReactApplicationContext) : ReactTextInputMa
|
||||
}
|
||||
|
||||
override fun getExportedCustomBubblingEventTypeConstants(): MutableMap<String, Any> {
|
||||
- val map = super.getExportedCustomBubblingEventTypeConstants()!!
|
||||
+ val map = super.getExportedCustomBubblingEventTypeConstants().toMutableMap()
|
||||
map["onPaste"] = MapBuilder.of(
|
||||
"phasedRegistrationNames",
|
||||
MapBuilder.of("bubbled", "onPaste")
|
||||
@@ -1,264 +0,0 @@
|
||||
diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m b/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m
|
||||
index e916023..5049c33 100644
|
||||
--- a/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m
|
||||
+++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m
|
||||
@@ -4,6 +4,7 @@
|
||||
//
|
||||
// Created by Elias Nahum on 04-11-20.
|
||||
// Copyright © 2020 Facebook. All rights reserved.
|
||||
+// Updated to remove parent’s default text view
|
||||
//
|
||||
|
||||
#import "PasteInputView.h"
|
||||
@@ -12,49 +13,78 @@
|
||||
|
||||
@implementation PasteInputView
|
||||
{
|
||||
- PasteInputTextView *_backedTextInputView;
|
||||
+ // We'll store the custom text view in this ivar
|
||||
+ PasteInputTextView *_customBackedTextView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
+ // Must call the super’s designated initializer
|
||||
if (self = [super initWithBridge:bridge]) {
|
||||
- _backedTextInputView = [[PasteInputTextView alloc] initWithFrame:self.bounds];
|
||||
- _backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
- _backedTextInputView.textInputDelegate = self;
|
||||
+ // 1. The parent (RCTMultilineTextInputView) has already created
|
||||
+ // its own _backedTextInputView = [RCTUITextView new] in super init.
|
||||
+ // We can remove that subview:
|
||||
|
||||
- [self addSubview:_backedTextInputView];
|
||||
- }
|
||||
+ id<RCTBackedTextInputViewProtocol> parentInputView = super.backedTextInputView;
|
||||
+ if ([parentInputView isKindOfClass:[UIView class]]) {
|
||||
+ UIView *parentSubview = (UIView *)parentInputView;
|
||||
+ if (parentSubview.superview == self) {
|
||||
+ [parentSubview removeFromSuperview];
|
||||
+ }
|
||||
+ }
|
||||
|
||||
+ // 2. Now create our custom PasteInputTextView
|
||||
+ _customBackedTextView = [[PasteInputTextView alloc] initWithFrame:self.bounds];
|
||||
+ _customBackedTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
+ _customBackedTextView.textInputDelegate = self;
|
||||
+
|
||||
+ // Optional: disable inline predictions for iOS 17+
|
||||
+ if (@available(iOS 17.0, *)) {
|
||||
+ _customBackedTextView.inlinePredictionType = UITextInlinePredictionTypeNo;
|
||||
+ }
|
||||
+
|
||||
+ // 3. Add your custom text view as the only subview
|
||||
+ [self addSubview:_customBackedTextView];
|
||||
+ }
|
||||
return self;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * Override the parent's accessor so that anywhere in RN that calls
|
||||
+ * `self.backedTextInputView` will get the custom PasteInputTextView.
|
||||
+ */
|
||||
- (id<RCTBackedTextInputViewProtocol>)backedTextInputView
|
||||
{
|
||||
- return _backedTextInputView;
|
||||
+ return _customBackedTextView;
|
||||
}
|
||||
|
||||
-- (void)setDisableCopyPaste:(BOOL)disableCopyPaste {
|
||||
- _backedTextInputView.disableCopyPaste = disableCopyPaste;
|
||||
+#pragma mark - Setters for React Props
|
||||
+
|
||||
+- (void)setDisableCopyPaste:(BOOL)disableCopyPaste
|
||||
+{
|
||||
+ _customBackedTextView.disableCopyPaste = disableCopyPaste;
|
||||
}
|
||||
|
||||
-- (void)setOnPaste:(RCTDirectEventBlock)onPaste {
|
||||
- _backedTextInputView.onPaste = onPaste;
|
||||
+- (void)setOnPaste:(RCTDirectEventBlock)onPaste
|
||||
+{
|
||||
+ _customBackedTextView.onPaste = onPaste;
|
||||
}
|
||||
|
||||
-- (void)setSmartPunctuation:(NSString *)smartPunctuation {
|
||||
- if ([smartPunctuation isEqualToString:@"enable"]) {
|
||||
- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeYes];
|
||||
- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeYes];
|
||||
- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeYes];
|
||||
- } else if ([smartPunctuation isEqualToString:@"disable"]) {
|
||||
- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeNo];
|
||||
- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeNo];
|
||||
- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeNo];
|
||||
- } else {
|
||||
- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeDefault];
|
||||
- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeDefault];
|
||||
- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeDefault];
|
||||
- }
|
||||
+- (void)setSmartPunctuation:(NSString *)smartPunctuation
|
||||
+{
|
||||
+ if ([smartPunctuation isEqualToString:@"enable"]) {
|
||||
+ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeYes];
|
||||
+ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeYes];
|
||||
+ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeYes];
|
||||
+ } else if ([smartPunctuation isEqualToString:@"disable"]) {
|
||||
+ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeNo];
|
||||
+ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeNo];
|
||||
+ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeNo];
|
||||
+ } else {
|
||||
+ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeDefault];
|
||||
+ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeDefault];
|
||||
+ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeDefault];
|
||||
+ }
|
||||
}
|
||||
|
||||
#pragma mark - UIScrollViewDelegate
|
||||
@@ -62,7 +92,6 @@ - (void)setSmartPunctuation:(NSString *)smartPunctuation {
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||||
{
|
||||
RCTDirectEventBlock onScroll = self.onScroll;
|
||||
-
|
||||
if (onScroll) {
|
||||
CGPoint contentOffset = scrollView.contentOffset;
|
||||
CGSize contentSize = scrollView.contentSize;
|
||||
@@ -71,22 +100,22 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||||
|
||||
onScroll(@{
|
||||
@"contentOffset": @{
|
||||
- @"x": @(contentOffset.x),
|
||||
- @"y": @(contentOffset.y)
|
||||
+ @"x": @(contentOffset.x),
|
||||
+ @"y": @(contentOffset.y)
|
||||
},
|
||||
@"contentInset": @{
|
||||
- @"top": @(contentInset.top),
|
||||
- @"left": @(contentInset.left),
|
||||
- @"bottom": @(contentInset.bottom),
|
||||
- @"right": @(contentInset.right)
|
||||
+ @"top": @(contentInset.top),
|
||||
+ @"left": @(contentInset.left),
|
||||
+ @"bottom": @(contentInset.bottom),
|
||||
+ @"right": @(contentInset.right)
|
||||
},
|
||||
@"contentSize": @{
|
||||
- @"width": @(contentSize.width),
|
||||
- @"height": @(contentSize.height)
|
||||
+ @"width": @(contentSize.width),
|
||||
+ @"height": @(contentSize.height)
|
||||
},
|
||||
@"layoutMeasurement": @{
|
||||
- @"width": @(size.width),
|
||||
- @"height": @(size.height)
|
||||
+ @"width": @(size.width),
|
||||
+ @"height": @(size.height)
|
||||
},
|
||||
@"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<const PasteTextInputProps &>(*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<const PasteTextInputProps &>(*_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<const PasteTextInputProps &>(*_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 <react/renderer/components/iostextinput/conversions.h>
|
||||
#include <react/renderer/components/iostextinput/primitives.h>
|
||||
#include <react/renderer/components/text/BaseTextProps.h>
|
||||
+#include <react/renderer/components/textinput/BaseTextInputProps.h>
|
||||
#include <react/renderer/components/view/ViewProps.h>
|
||||
#include <react/renderer/core/Props.h>
|
||||
#include <react/renderer/core/PropsParserContext.h>
|
||||
@@ -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));
|
||||
}
|
||||
+15
-17
@@ -11,13 +11,11 @@ import {
|
||||
import * as ScreenOrientation from 'expo-screen-orientation'
|
||||
import * as SplashScreen from 'expo-splash-screen'
|
||||
import * as SystemUI from 'expo-system-ui'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import * as Sentry from '@sentry/react-native'
|
||||
|
||||
import {Provider as HideBottomBarBorderProvider} from '#/lib/hooks/useHideBottomBarBorder'
|
||||
import {QueryProvider} from '#/lib/react-query'
|
||||
import {s} from '#/lib/styles'
|
||||
import {ThemeProvider} from '#/lib/ThemeContext'
|
||||
import {Provider as TranslateOnDeviceProvider} from '#/lib/translation'
|
||||
import I18nProvider from '#/locale/i18nProvider'
|
||||
@@ -59,7 +57,7 @@ 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 {Shell} from '#/view/shell'
|
||||
import {ThemeProvider as Alf} from '#/alf'
|
||||
import {atoms as a, ThemeProvider as Alf} from '#/alf'
|
||||
import {useColorModeTheme} from '#/alf/util/useColorModeTheme'
|
||||
import {Provider as ContextMenuProvider} from '#/components/ContextMenu'
|
||||
import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry'
|
||||
@@ -89,9 +87,9 @@ import {Splash} from '#/Splash'
|
||||
import {BottomSheetProvider} from '../modules/bottom-sheet'
|
||||
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
|
||||
|
||||
SplashScreen.preventAutoHideAsync()
|
||||
void SplashScreen.preventAutoHideAsync()
|
||||
if (IS_IOS) {
|
||||
SystemUI.setBackgroundColorAsync('black')
|
||||
void SystemUI.setBackgroundColorAsync('black')
|
||||
}
|
||||
if (IS_ANDROID) {
|
||||
// iOS is handled by the config plugin -sfn
|
||||
@@ -105,17 +103,17 @@ if (IS_ANDROID) {
|
||||
/**
|
||||
* Begin geolocation ASAP
|
||||
*/
|
||||
Geo.resolve()
|
||||
prefetchAgeAssuranceConfig()
|
||||
prefetchLiveEvents()
|
||||
prefetchAppConfig()
|
||||
void Geo.resolve()
|
||||
void prefetchAgeAssuranceConfig()
|
||||
void prefetchLiveEvents()
|
||||
void prefetchAppConfig()
|
||||
|
||||
function InnerApp() {
|
||||
const [isReady, setIsReady] = useState(false)
|
||||
const {currentAccount} = useSession()
|
||||
const {resumeSession} = useSessionApi()
|
||||
const theme = useColorModeTheme()
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const hasCheckedReferrer = useStarterPackEntry()
|
||||
|
||||
// init
|
||||
@@ -134,16 +132,16 @@ function InnerApp() {
|
||||
}
|
||||
}
|
||||
const account = readLastActiveAccount()
|
||||
onLaunch(account)
|
||||
void onLaunch(account)
|
||||
}, [resumeSession])
|
||||
|
||||
useEffect(() => {
|
||||
return listenSessionDropped(() => {
|
||||
Toast.show(_(msg`Sorry! Your session expired. Please sign in again.`), {
|
||||
Toast.show(l`Sorry! Your session expired. Please sign in again.`, {
|
||||
type: 'info',
|
||||
})
|
||||
})
|
||||
}, [_])
|
||||
}, [l])
|
||||
|
||||
return (
|
||||
<Alf theme={theme}>
|
||||
@@ -176,7 +174,7 @@ function InnerApp() {
|
||||
<EmailVerificationProvider>
|
||||
<HideBottomBarBorderProvider>
|
||||
<GestureHandlerRootView
|
||||
style={s.h100pct}>
|
||||
style={a.h_full}>
|
||||
<GlobalGestureEventsProvider>
|
||||
<IntentDialogProvider>
|
||||
<TranslateOnDeviceProvider>
|
||||
@@ -220,8 +218,8 @@ function App() {
|
||||
const [isReady, setReady] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then(() =>
|
||||
setReady(true),
|
||||
void Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then(
|
||||
() => setReady(true),
|
||||
)
|
||||
}, [])
|
||||
|
||||
|
||||
+17
-15
@@ -5,10 +5,10 @@ import './style.css'
|
||||
import {Fragment, useEffect, useState} from 'react'
|
||||
import {KeyboardProvider as KeyboardControllerProvider} from 'react-native-keyboard-controller'
|
||||
import {SafeAreaProvider} from 'react-native-safe-area-context'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import * as Sentry from '@sentry/react-native'
|
||||
|
||||
import {Provider as HotkeysProvider} from '#/lib/hotkeys'
|
||||
import {QueryProvider} from '#/lib/react-query'
|
||||
import {ThemeProvider} from '#/lib/ThemeContext'
|
||||
import {Provider as TranslateOnDeviceProvider} from '#/lib/translation'
|
||||
@@ -82,17 +82,17 @@ import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottom
|
||||
/**
|
||||
* Begin geolocation ASAP
|
||||
*/
|
||||
Geo.resolve()
|
||||
prefetchAgeAssuranceConfig()
|
||||
prefetchLiveEvents()
|
||||
prefetchAppConfig()
|
||||
void Geo.resolve()
|
||||
void prefetchAgeAssuranceConfig()
|
||||
void prefetchLiveEvents()
|
||||
void prefetchAppConfig()
|
||||
|
||||
function InnerApp() {
|
||||
const [isReady, setIsReady] = useState(false)
|
||||
const {currentAccount} = useSession()
|
||||
const {resumeSession} = useSessionApi()
|
||||
const theme = useColorModeTheme()
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const hasCheckedReferrer = useStarterPackEntry()
|
||||
|
||||
// init
|
||||
@@ -105,22 +105,22 @@ function InnerApp() {
|
||||
await features.init
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(`session: resumeSession failed`, {message: e})
|
||||
logger.error('session: resumeSession failed', {message: e})
|
||||
} finally {
|
||||
setIsReady(true)
|
||||
}
|
||||
}
|
||||
const account = readLastActiveAccount()
|
||||
onLaunch(account)
|
||||
void onLaunch(account)
|
||||
}, [resumeSession])
|
||||
|
||||
useEffect(() => {
|
||||
return listenSessionDropped(() => {
|
||||
Toast.show(_(msg`Sorry! Your session expired. Please sign in again.`), {
|
||||
Toast.show(l`Sorry! Your session expired. Please sign in again.`, {
|
||||
type: 'info',
|
||||
})
|
||||
})
|
||||
}, [_])
|
||||
}, [l])
|
||||
|
||||
return (
|
||||
<Alf theme={theme}>
|
||||
@@ -156,8 +156,10 @@ function InnerApp() {
|
||||
<HideBottomBarBorderProvider>
|
||||
<IntentDialogProvider>
|
||||
<TranslateOnDeviceProvider>
|
||||
<Shell />
|
||||
<ToastOutlet />
|
||||
<HotkeysProvider>
|
||||
<Shell />
|
||||
<ToastOutlet />
|
||||
</HotkeysProvider>
|
||||
</TranslateOnDeviceProvider>
|
||||
</IntentDialogProvider>
|
||||
</HideBottomBarBorderProvider>
|
||||
@@ -195,8 +197,8 @@ function App() {
|
||||
const [isReady, setReady] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then(() =>
|
||||
setReady(true),
|
||||
void Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then(
|
||||
() => setReady(true),
|
||||
)
|
||||
}, [])
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import {Children} from 'react'
|
||||
import {type TextProps as RNTextProps} from 'react-native'
|
||||
import {type StyleProp, type TextStyle} from 'react-native'
|
||||
import {
|
||||
type StyleProp,
|
||||
type TextProps as RNTextProps,
|
||||
type TextStyle,
|
||||
} from 'react-native'
|
||||
import {UITextView} from 'react-native-uitextview'
|
||||
import createEmojiRegex from 'emoji-regex'
|
||||
|
||||
import {type Alf, applyFonts, atoms, flatten} from '#/alf'
|
||||
import {IS_NATIVE} from '#/env'
|
||||
import {IS_IOS} from '#/env'
|
||||
import {IS_IOS, IS_NATIVE} from '#/env'
|
||||
|
||||
/**
|
||||
* Ensures that `lineHeight` defaults to a relative value of `1`, or applies
|
||||
@@ -107,7 +109,8 @@ export function renderChildrenWithEmoji(
|
||||
})
|
||||
}
|
||||
|
||||
const SINGLE_EMOJI_RE = /^[\p{Emoji_Presentation}\p{Extended_Pictographic}]+$/u
|
||||
const SINGLE_EMOJI_RE =
|
||||
/^[\p{Emoji_Presentation}\p{Extended_Pictographic}\uFE0F\u200D]+$/u
|
||||
export function isOnlyEmoji(text: string) {
|
||||
return text.length <= 15 && SINGLE_EMOJI_RE.test(text)
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ const Context = createContext<AnalyticsBaseContextType>({
|
||||
},
|
||||
},
|
||||
})
|
||||
Context.displayName = 'AnalyticsContext'
|
||||
|
||||
/**
|
||||
* Ensures that deviceId is set and migrated from legacy storage. Handled on
|
||||
|
||||
@@ -560,32 +560,30 @@ export function ProfileGrid({
|
||||
<Text style={[a.text_sm, a.font_semi_bold, t.atoms.text]}>
|
||||
<Trans>Suggested for you</Trans>
|
||||
</Text>
|
||||
{!isProfileHeaderContext && (
|
||||
<Button
|
||||
label={l`See more suggested profiles`}
|
||||
onPress={() => {
|
||||
followDialogControl.open()
|
||||
ax.metric('suggestedUser:seeMore', {
|
||||
logContext,
|
||||
recId,
|
||||
})
|
||||
}}>
|
||||
{({hovered}) => (
|
||||
<Text
|
||||
style={[
|
||||
a.text_sm,
|
||||
{color: t.palette.primary_500},
|
||||
hovered &&
|
||||
web({
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: t.palette.primary_500,
|
||||
}),
|
||||
]}>
|
||||
<Trans>See more</Trans>
|
||||
</Text>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
label={l`See more suggested profiles`}
|
||||
onPress={() => {
|
||||
followDialogControl.open()
|
||||
ax.metric('suggestedUser:seeMore', {
|
||||
logContext,
|
||||
recId,
|
||||
})
|
||||
}}>
|
||||
{({hovered}) => (
|
||||
<Text
|
||||
style={[
|
||||
a.text_sm,
|
||||
{color: t.palette.primary_500},
|
||||
hovered &&
|
||||
web({
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: t.palette.primary_500,
|
||||
}),
|
||||
]}>
|
||||
<Trans>See more</Trans>
|
||||
</Text>
|
||||
)}
|
||||
</Button>
|
||||
</View>
|
||||
<FollowDialogWithoutGuide control={followDialogControl} />
|
||||
<LayoutAnimationConfig skipExiting skipEntering>
|
||||
@@ -605,16 +603,14 @@ export function ProfileGrid({
|
||||
decelerationRate="fast">
|
||||
{content}
|
||||
|
||||
{!isProfileHeaderContext && (
|
||||
<SeeMoreSuggestedProfilesCard
|
||||
onPress={() => {
|
||||
followDialogControl.open()
|
||||
ax.metric('suggestedUser:seeMore', {
|
||||
logContext,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<SeeMoreSuggestedProfilesCard
|
||||
onPress={() => {
|
||||
followDialogControl.open()
|
||||
ax.metric('suggestedUser:seeMore', {
|
||||
logContext,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</ScrollView>
|
||||
</BlockDrawerGesture>
|
||||
)}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute'
|
||||
import {Pause_Filled_Corner0_Rounded as PauseIcon} from '#/components/icons/Pause'
|
||||
import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play'
|
||||
import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker'
|
||||
import {KeepAwake} from '#/components/KeepAwake'
|
||||
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
||||
import {useVideoMuteState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext'
|
||||
import {GifPresentationControls} from '../GifPresentationControls'
|
||||
@@ -112,6 +113,7 @@ export function VideoEmbedInnerNative({
|
||||
/>
|
||||
)}
|
||||
<MediaInsetBorder />
|
||||
<KeepAwake enabled={isPlaying} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -136,6 +136,8 @@ export const BookmarkButton = memo(function BookmarkButton({
|
||||
<PostControlButton
|
||||
testID="postBookmarkBtn"
|
||||
big={big}
|
||||
active={isBookmarked}
|
||||
activeColor={t.palette.primary_500}
|
||||
label={
|
||||
isBookmarked
|
||||
? _(msg`Remove from saved posts`)
|
||||
@@ -143,10 +145,7 @@ export const BookmarkButton = memo(function BookmarkButton({
|
||||
}
|
||||
onPress={onHandlePress}
|
||||
hitSlop={hitSlop}>
|
||||
<PostControlButtonIcon
|
||||
fill={isBookmarked ? t.palette.primary_500 : undefined}
|
||||
icon={isBookmarked ? BookmarkFilled : Bookmark}
|
||||
/>
|
||||
<PostControlButtonIcon icon={isBookmarked ? BookmarkFilled : Bookmark} />
|
||||
</PostControlButton>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -130,8 +130,11 @@ export function PostControlButtonText({style, ...props}: TextProps) {
|
||||
<Text
|
||||
style={[
|
||||
color,
|
||||
a.user_select_none,
|
||||
big ? a.text_md : a.text_sm,
|
||||
active && a.font_semi_bold,
|
||||
// prevent layout shift on android
|
||||
{includeFontPadding: false, textAlignVertical: 'center'},
|
||||
style,
|
||||
]}
|
||||
{...props}
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
ProgressGuideAction,
|
||||
useProgressGuideControls,
|
||||
} from '#/state/shell/progress-guide'
|
||||
import {atoms as a, useBreakpoints} from '#/alf'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Reply as Bubble} from '#/components/icons/Reply'
|
||||
import {useFormatPostStatCount} from '#/components/PostControls/util'
|
||||
import * as Skele from '#/components/Skeleton'
|
||||
@@ -74,6 +74,7 @@ let PostControls = ({
|
||||
forceGoogleTranslate?: boolean
|
||||
}): React.ReactNode => {
|
||||
const ax = useAnalytics()
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
const {openComposer} = useOpenComposer()
|
||||
const {feedDescriptor} = useFeedFeedbackContext()
|
||||
@@ -270,6 +271,8 @@ let PostControls = ({
|
||||
<PostControlButton
|
||||
testID="likeBtn"
|
||||
big={big}
|
||||
active={Boolean(post.viewer?.like)}
|
||||
activeColor={t.palette.pink}
|
||||
onPress={() => requireAuth(() => onPressToggleLike())}
|
||||
label={
|
||||
post.viewer?.like
|
||||
@@ -296,10 +299,14 @@ let PostControls = ({
|
||||
hasBeenToggled={hasLikeIconBeenToggled}
|
||||
/>
|
||||
<CountWheel
|
||||
likeCount={post.likeCount ?? 0}
|
||||
big={big}
|
||||
isLiked={Boolean(post.viewer?.like)}
|
||||
count={post.likeCount ?? 0}
|
||||
isToggled={Boolean(post.viewer?.like)}
|
||||
hasBeenToggled={hasLikeIconBeenToggled}
|
||||
renderCount={({count}) => (
|
||||
<PostControlButtonText>
|
||||
{formatPostStatCount(count)}
|
||||
</PostControlButtonText>
|
||||
)}
|
||||
/>
|
||||
</PostControlButton>
|
||||
</View>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {View} from 'react-native'
|
||||
import {useWindowDimensions, View} from 'react-native'
|
||||
|
||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {atoms as a, type ViewStyleProp} from '#/alf'
|
||||
import {atoms as a, useAlf, type ViewStyleProp} from '#/alf'
|
||||
import {BotBadge, BotBadgeButton, isBotAccount} from '#/components/BotBadge'
|
||||
import {useSimpleVerificationState} from '#/components/verification'
|
||||
import {VerificationCheck} from '#/components/verification/VerificationCheck'
|
||||
@@ -38,12 +38,21 @@ export function ProfileBadges({
|
||||
}) {
|
||||
const shadowed = useProfileShadow(profile)
|
||||
const verification = useSimpleVerificationState({profile})
|
||||
const {fontScale: nativeScaleMultiplier} = useWindowDimensions()
|
||||
const {
|
||||
fonts: {scaleMultiplier: alfScaleMultiplier},
|
||||
} = useAlf()
|
||||
|
||||
// if nothing to show, don't render the container at all
|
||||
if (!verification.showBadge && !isBotAccount(shadowed)) return null
|
||||
|
||||
const isOnTheSmallSide = size === 'xs' || size === 'sm'
|
||||
|
||||
const verificationIconWidth =
|
||||
verificationIconSizes[size] * nativeScaleMultiplier * alfScaleMultiplier
|
||||
const botIconWidth =
|
||||
botIconSizes[size] * nativeScaleMultiplier * alfScaleMultiplier
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
@@ -56,19 +65,19 @@ export function ProfileBadges({
|
||||
<>
|
||||
<VerificationCheckButton
|
||||
profile={shadowed}
|
||||
width={verificationIconSizes[size]}
|
||||
width={verificationIconWidth}
|
||||
/>
|
||||
<BotBadgeButton profile={shadowed} width={botIconSizes[size]} />
|
||||
<BotBadgeButton profile={shadowed} width={botIconWidth} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{verification.showBadge && (
|
||||
<VerificationCheck
|
||||
verifier={verification.role === 'verifier'}
|
||||
width={verificationIconSizes[size]}
|
||||
width={verificationIconWidth}
|
||||
/>
|
||||
)}
|
||||
<BotBadge profile={shadowed} width={botIconSizes[size]} />
|
||||
<BotBadge profile={shadowed} width={botIconWidth} />
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
|
||||
@@ -3,8 +3,7 @@ import {View} from 'react-native'
|
||||
import {Select as RadixSelect} from 'radix-ui'
|
||||
|
||||
import {useA11y} from '#/state/a11y'
|
||||
import {flatten, useTheme, web} from '#/alf'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {atoms as a, flatten, useTheme, web} from '#/alf'
|
||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||
import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check'
|
||||
import {
|
||||
@@ -109,7 +108,7 @@ export function Trigger({children, label}: TriggerProps) {
|
||||
borderRadius: 10,
|
||||
maxWidth: 400,
|
||||
outline: 0,
|
||||
borderWidth: 2,
|
||||
borderWidth: 1,
|
||||
borderStyle: 'solid',
|
||||
borderColor: focused
|
||||
? t.palette.primary_500
|
||||
|
||||
@@ -46,6 +46,7 @@ export const PostsList = forwardRef<SectionRef, ProfilesListProps>(
|
||||
return (
|
||||
<View>
|
||||
<PostFeed
|
||||
enabled
|
||||
feed={feed}
|
||||
pollInterval={60e3}
|
||||
scrollElRef={scrollElRef}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {View} from 'react-native'
|
||||
|
||||
import {atoms as a, useTheme, type ViewStyleProp} from '#/alf'
|
||||
import {atoms as a, useTheme, type ViewStyleProp, web as webOnly} from '#/alf'
|
||||
import {IS_NATIVE, IS_WEB, IS_WEB_TOUCH_DEVICE} from '#/env'
|
||||
|
||||
export function SubtleHover({
|
||||
@@ -33,6 +33,10 @@ export function SubtleHover({
|
||||
a.transition_opacity,
|
||||
t.atoms.bg_contrast_50,
|
||||
style,
|
||||
// Force Safari to composite the overlay on its own GPU layer.
|
||||
// This fixes a layout shift that happens due to different subpixel
|
||||
// rounding when the overlay is composited on hover.
|
||||
webOnly({willChange: 'opacity'}),
|
||||
{opacity: hover ? opacity : 0},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {XRPCError} from '@atproto/xrpc'
|
||||
import {XRPCError} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useEffect} from 'react'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
|
||||
const events = new EventEmitter<{
|
||||
emailVerified: void
|
||||
|
||||
@@ -63,7 +63,7 @@ export function DateFieldButton({
|
||||
paddingLeft: 14,
|
||||
paddingRight: 14,
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 2,
|
||||
borderWidth: 1,
|
||||
},
|
||||
native({
|
||||
paddingTop: 10,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import {forwardRef} from 'react'
|
||||
import {useEffect, useRef} from 'react'
|
||||
import {type TextInput, View} from 'react-native'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {HITSLOP_10} from '#/lib/constants'
|
||||
import {listenFocusSearch} from '#/state/events'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button, ButtonIcon} from '#/components/Button'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
@@ -10,73 +11,89 @@ import {MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlassIcon} from '#/
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
|
||||
import {IS_NATIVE} from '#/env'
|
||||
|
||||
type SearchInputProps = Omit<TextField.InputProps, 'label'> & {
|
||||
type Props = Omit<TextField.InputProps, 'label'> & {
|
||||
label?: TextField.InputProps['label']
|
||||
/**
|
||||
* Called when the user presses the (X) button
|
||||
*/
|
||||
onClearText?: () => void
|
||||
hotkey?: boolean
|
||||
ref?: React.RefObject<TextInput | null>
|
||||
}
|
||||
|
||||
export const SearchInput = forwardRef<TextInput, SearchInputProps>(
|
||||
function SearchInput({value, label, onClearText, ...rest}, ref) {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
const showClear = value && value.length > 0
|
||||
export function SearchInput({
|
||||
value,
|
||||
label,
|
||||
onClearText,
|
||||
hotkey,
|
||||
ref,
|
||||
...rest
|
||||
}: Props) {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
const showClear = value && value.length > 0
|
||||
const internalRef = useRef<TextInput>(null)
|
||||
const inputRef = ref ?? internalRef
|
||||
|
||||
return (
|
||||
<View style={[a.w_full, a.relative]}>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={MagnifyingGlassIcon} />
|
||||
<TextField.Input
|
||||
inputRef={ref}
|
||||
label={label || l`Search`}
|
||||
value={value}
|
||||
placeholder={l`Search`}
|
||||
returnKeyType="search"
|
||||
keyboardAppearance={t.scheme}
|
||||
selectTextOnFocus={IS_NATIVE}
|
||||
autoFocus={false}
|
||||
accessibilityRole="search"
|
||||
autoCorrect={false}
|
||||
autoComplete="off"
|
||||
autoCapitalize="none"
|
||||
style={[
|
||||
showClear
|
||||
? {
|
||||
paddingRight: 24,
|
||||
}
|
||||
: {},
|
||||
]}
|
||||
{...rest}
|
||||
/>
|
||||
</TextField.Root>
|
||||
useEffect(() => {
|
||||
if (!hotkey) return
|
||||
return listenFocusSearch(() => {
|
||||
inputRef.current?.focus()
|
||||
})
|
||||
}, [hotkey, inputRef])
|
||||
|
||||
{showClear && (
|
||||
<View
|
||||
style={[
|
||||
a.absolute,
|
||||
a.z_20,
|
||||
a.my_auto,
|
||||
a.inset_0,
|
||||
a.justify_center,
|
||||
a.pr_sm,
|
||||
{left: 'auto'},
|
||||
]}>
|
||||
<Button
|
||||
testID="searchTextInputClearBtn"
|
||||
onPress={onClearText}
|
||||
label={l`Clear search query`}
|
||||
hitSlop={HITSLOP_10}
|
||||
size="tiny"
|
||||
shape="round"
|
||||
variant="ghost"
|
||||
color="secondary">
|
||||
<ButtonIcon icon={X} size="xs" />
|
||||
</Button>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
},
|
||||
)
|
||||
return (
|
||||
<View style={[a.w_full, a.relative]}>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={MagnifyingGlassIcon} />
|
||||
<TextField.Input
|
||||
inputRef={inputRef}
|
||||
label={label || l`Search`}
|
||||
value={value}
|
||||
placeholder={l`Search`}
|
||||
returnKeyType="search"
|
||||
keyboardAppearance={t.scheme}
|
||||
selectTextOnFocus={IS_NATIVE}
|
||||
autoFocus={false}
|
||||
accessibilityRole="search"
|
||||
autoCorrect={false}
|
||||
autoComplete="off"
|
||||
autoCapitalize="none"
|
||||
style={[
|
||||
showClear
|
||||
? {
|
||||
paddingRight: 24,
|
||||
}
|
||||
: {},
|
||||
]}
|
||||
{...rest}
|
||||
/>
|
||||
</TextField.Root>
|
||||
|
||||
{showClear && (
|
||||
<View
|
||||
style={[
|
||||
a.absolute,
|
||||
a.z_20,
|
||||
a.my_auto,
|
||||
a.inset_0,
|
||||
a.justify_center,
|
||||
a.pr_sm,
|
||||
{left: 'auto'},
|
||||
]}>
|
||||
<Button
|
||||
testID="searchTextInputClearBtn"
|
||||
onPress={onClearText}
|
||||
label={l`Clear search query`}
|
||||
hitSlop={HITSLOP_10}
|
||||
size="tiny"
|
||||
shape="round"
|
||||
variant="ghost"
|
||||
color="secondary">
|
||||
<ButtonIcon icon={X} size="xs" />
|
||||
</Button>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type ComAtprotoLabelDefs, ToolsOzoneReportDefs} from '@atproto/api'
|
||||
import {XRPCError} from '@atproto/xrpc'
|
||||
import {XRPCError} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
@@ -184,7 +184,7 @@ function Inner(props: ReportDialogProps) {
|
||||
)
|
||||
})
|
||||
}, [
|
||||
props,
|
||||
props.subject,
|
||||
allLabelers,
|
||||
state.selectedOption,
|
||||
isBskyOnlyReason,
|
||||
@@ -241,7 +241,17 @@ function Inner(props: ReportDialogProps) {
|
||||
} finally {
|
||||
setPending(false)
|
||||
}
|
||||
}, [_, submitReport, state, dispatch, props, setPending, setSuccess])
|
||||
}, [
|
||||
_,
|
||||
submitReport,
|
||||
state,
|
||||
dispatch,
|
||||
props.subject,
|
||||
props.control,
|
||||
props.onAfterSubmit,
|
||||
setPending,
|
||||
setSuccess,
|
||||
])
|
||||
|
||||
useCallOnce(() => {
|
||||
ax.metric('reportDialog:open', {
|
||||
|
||||
Vendored
+8
@@ -10,6 +10,10 @@ const iOSMajorVersion =
|
||||
Platform.OS === 'ios' && typeof Platform.Version === 'string'
|
||||
? parseInt(Platform.Version.split('.')[0], 10)
|
||||
: 0
|
||||
const androidPlatformVersion =
|
||||
Platform.OS === 'android' && typeof Platform.Version === 'number'
|
||||
? Platform.Version
|
||||
: 0
|
||||
|
||||
/**
|
||||
* The semver version of the app, specified in our `package.json`.file. On
|
||||
@@ -49,3 +53,7 @@ export const IS_WEB_FIREFOX: boolean = false
|
||||
export const IS_HIGH_DPI: boolean = true
|
||||
// ideally we'd use isLiquidGlassAvailable() from expo-glass-effect but checking iOS version is good enough for now
|
||||
export const IS_LIQUID_GLASS: boolean = iOSMajorVersion >= 26
|
||||
// So we can avoid attempting on-device translation when we know it's unsupported.
|
||||
export const IS_TRANSLATION_SUPPORTED: boolean =
|
||||
(IS_IOS && iOSMajorVersion >= 18) ||
|
||||
(IS_ANDROID && androidPlatformVersion > 22)
|
||||
|
||||
Vendored
+1
@@ -48,3 +48,4 @@ export const IS_HIGH_DPI: boolean = window.matchMedia(
|
||||
'(min-resolution: 2dppx)',
|
||||
).matches
|
||||
export const IS_LIQUID_GLASS: boolean = false
|
||||
export const IS_TRANSLATION_SUPPORTED: boolean = false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
|
||||
import {networkRetry} from '#/lib/async/retry'
|
||||
import {
|
||||
|
||||
@@ -8,10 +8,7 @@ import Animated, {
|
||||
} from 'react-native-reanimated'
|
||||
|
||||
import {decideShouldRoll} from '#/lib/custom-animations/util'
|
||||
import {s} from '#/lib/styles'
|
||||
import {Text} from '#/view/com/util/text/Text'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {useFormatPostStatCount} from '#/components/PostControls/util'
|
||||
import {atoms as a} from '#/alf'
|
||||
|
||||
const animationConfig = {
|
||||
duration: 400,
|
||||
@@ -87,89 +84,66 @@ function ExitingDown() {
|
||||
}
|
||||
|
||||
export function CountWheel({
|
||||
likeCount,
|
||||
big,
|
||||
isLiked,
|
||||
count,
|
||||
isToggled,
|
||||
hasBeenToggled,
|
||||
renderCount,
|
||||
}: {
|
||||
likeCount: number
|
||||
big?: boolean
|
||||
isLiked: boolean
|
||||
count: number
|
||||
isToggled: boolean
|
||||
hasBeenToggled: boolean
|
||||
renderCount: (props: {count: number}) => React.ReactNode
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const shouldAnimate = !useReducedMotion() && hasBeenToggled
|
||||
const shouldRoll = decideShouldRoll(isLiked, likeCount)
|
||||
const shouldRoll = decideShouldRoll(isToggled, count)
|
||||
|
||||
// Incrementing the key will cause the `Animated.View` to re-render, with the newly selected entering/exiting
|
||||
// animation
|
||||
// The initial entering/exiting animations will get skipped, since these will happen on screen mounts and would
|
||||
// be unnecessary
|
||||
const [key, setKey] = useState(0)
|
||||
const [prevCount, setPrevCount] = useState(likeCount)
|
||||
const prevIsLiked = useRef(isLiked)
|
||||
const formatPostStatCount = useFormatPostStatCount()
|
||||
const formattedCount = formatPostStatCount(likeCount)
|
||||
const formattedPrevCount = formatPostStatCount(prevCount)
|
||||
const [prevCount, setPrevCount] = useState(count)
|
||||
const prevIsToggled = useRef(isToggled)
|
||||
|
||||
useEffect(() => {
|
||||
if (isLiked === prevIsLiked.current) {
|
||||
if (isToggled === prevIsToggled.current) {
|
||||
return
|
||||
}
|
||||
|
||||
const newPrevCount = isLiked ? likeCount - 1 : likeCount + 1
|
||||
const newPrevCount = isToggled ? count - 1 : count + 1
|
||||
setKey(prev => prev + 1)
|
||||
setPrevCount(newPrevCount)
|
||||
prevIsLiked.current = isLiked
|
||||
}, [isLiked, likeCount])
|
||||
prevIsToggled.current = isToggled
|
||||
}, [isToggled, count])
|
||||
|
||||
const enteringAnimation =
|
||||
shouldAnimate && shouldRoll
|
||||
? isLiked
|
||||
? isToggled
|
||||
? EnteringUp
|
||||
: EnteringDown
|
||||
: undefined
|
||||
const exitingAnimation =
|
||||
shouldAnimate && shouldRoll
|
||||
? isLiked
|
||||
? isToggled
|
||||
? ExitingUp
|
||||
: ExitingDown
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<LayoutAnimationConfig skipEntering skipExiting>
|
||||
{likeCount > 0 ? (
|
||||
{count > 0 ? (
|
||||
<View style={[a.justify_center]}>
|
||||
<Animated.View entering={enteringAnimation} key={key}>
|
||||
<Text
|
||||
testID="likeCount"
|
||||
style={[
|
||||
big ? a.text_md : a.text_sm,
|
||||
a.user_select_none,
|
||||
isLiked
|
||||
? [a.font_semi_bold, s.likeColor]
|
||||
: {color: t.palette.contrast_500},
|
||||
]}>
|
||||
{formattedCount}
|
||||
</Text>
|
||||
{renderCount({count})}
|
||||
</Animated.View>
|
||||
{shouldAnimate && (likeCount > 1 || !isLiked) ? (
|
||||
{shouldAnimate && (count > 1 || !isToggled) ? (
|
||||
<Animated.View
|
||||
entering={exitingAnimation}
|
||||
// Add 2 to the key so there are never duplicates
|
||||
key={key + 2}
|
||||
style={[a.absolute, {width: 50, opacity: 0}]}
|
||||
aria-disabled={true}>
|
||||
<Text
|
||||
style={[
|
||||
big ? a.text_md : a.text_sm,
|
||||
a.user_select_none,
|
||||
isLiked
|
||||
? [a.font_semi_bold, s.likeColor]
|
||||
: {color: t.palette.contrast_500},
|
||||
]}>
|
||||
{formattedPrevCount}
|
||||
</Text>
|
||||
{renderCount({count: prevCount})}
|
||||
</Animated.View>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
@@ -3,10 +3,6 @@ import {View} from 'react-native'
|
||||
import {useReducedMotion} from 'react-native-reanimated'
|
||||
|
||||
import {decideShouldRoll} from '#/lib/custom-animations/util'
|
||||
import {s} from '#/lib/styles'
|
||||
import {Text} from '#/view/com/util/text/Text'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {useFormatPostStatCount} from '#/components/PostControls/util'
|
||||
|
||||
const animationConfig = {
|
||||
duration: 400,
|
||||
@@ -35,50 +31,46 @@ const exitingDownKeyframe = [
|
||||
]
|
||||
|
||||
export function CountWheel({
|
||||
likeCount,
|
||||
big,
|
||||
isLiked,
|
||||
count,
|
||||
isToggled,
|
||||
hasBeenToggled,
|
||||
renderCount,
|
||||
}: {
|
||||
likeCount: number
|
||||
big?: boolean
|
||||
isLiked: boolean
|
||||
count: number
|
||||
isToggled: boolean
|
||||
hasBeenToggled: boolean
|
||||
renderCount: (props: {count: number}) => React.ReactNode
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const shouldAnimate = !useReducedMotion() && hasBeenToggled
|
||||
const shouldRoll = decideShouldRoll(isLiked, likeCount)
|
||||
const shouldRoll = decideShouldRoll(isToggled, count)
|
||||
|
||||
const countView = useRef<HTMLDivElement>(null)
|
||||
const prevCountView = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [prevCount, setPrevCount] = useState(likeCount)
|
||||
const prevIsLiked = useRef(isLiked)
|
||||
const formatPostStatCount = useFormatPostStatCount()
|
||||
const formattedCount = formatPostStatCount(likeCount)
|
||||
const formattedPrevCount = formatPostStatCount(prevCount)
|
||||
const [prevCount, setPrevCount] = useState(count)
|
||||
const prevIsToggled = useRef(isToggled)
|
||||
|
||||
useEffect(() => {
|
||||
if (isLiked === prevIsLiked.current) {
|
||||
if (isToggled === prevIsToggled.current) {
|
||||
return
|
||||
}
|
||||
|
||||
const newPrevCount = isLiked ? likeCount - 1 : likeCount + 1
|
||||
const newPrevCount = isToggled ? count - 1 : count + 1
|
||||
if (shouldAnimate && shouldRoll) {
|
||||
countView.current?.animate?.(
|
||||
isLiked ? enteringUpKeyframe : enteringDownKeyframe,
|
||||
isToggled ? enteringUpKeyframe : enteringDownKeyframe,
|
||||
animationConfig,
|
||||
)
|
||||
prevCountView.current?.animate?.(
|
||||
isLiked ? exitingUpKeyframe : exitingDownKeyframe,
|
||||
isToggled ? exitingUpKeyframe : exitingDownKeyframe,
|
||||
animationConfig,
|
||||
)
|
||||
setPrevCount(newPrevCount)
|
||||
}
|
||||
prevIsLiked.current = isLiked
|
||||
}, [isLiked, likeCount, shouldAnimate, shouldRoll])
|
||||
prevIsToggled.current = isToggled
|
||||
}, [isToggled, count, shouldAnimate, shouldRoll])
|
||||
|
||||
if (likeCount < 1) {
|
||||
if (count < 1) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -87,34 +79,15 @@ export function CountWheel({
|
||||
<View
|
||||
// @ts-expect-error is div
|
||||
ref={countView}>
|
||||
<Text
|
||||
testID="likeCount"
|
||||
style={[
|
||||
big ? a.text_md : a.text_sm,
|
||||
a.user_select_none,
|
||||
isLiked
|
||||
? [a.font_semi_bold, s.likeColor]
|
||||
: {color: t.palette.contrast_500},
|
||||
]}>
|
||||
{formattedCount}
|
||||
</Text>
|
||||
{renderCount({count})}
|
||||
</View>
|
||||
{shouldAnimate && (likeCount > 1 || !isLiked) ? (
|
||||
{shouldAnimate && (count > 1 || !isToggled) ? (
|
||||
<View
|
||||
style={{position: 'absolute', opacity: 0}}
|
||||
aria-disabled={true}
|
||||
// @ts-expect-error is div
|
||||
ref={prevCountView}>
|
||||
<Text
|
||||
style={[
|
||||
big ? a.text_md : a.text_sm,
|
||||
a.user_select_none,
|
||||
isLiked
|
||||
? [a.font_semi_bold, s.likeColor]
|
||||
: {color: t.palette.contrast_500},
|
||||
]}>
|
||||
{formattedPrevCount}
|
||||
</Text>
|
||||
{renderCount({count: prevCount})}
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
@@ -5,7 +5,6 @@ import Animated, {
|
||||
useReducedMotion,
|
||||
} from 'react-native-reanimated'
|
||||
|
||||
import {s} from '#/lib/styles'
|
||||
import {useTheme} from '#/alf'
|
||||
import {
|
||||
Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled,
|
||||
@@ -86,7 +85,7 @@ export function AnimatedLikeIcon({
|
||||
{isLiked ? (
|
||||
<Animated.View
|
||||
entering={shouldAnimate ? keyframe.duration(300) : undefined}>
|
||||
<HeartIconFilled style={s.likeColor} width={size} />
|
||||
<HeartIconFilled style={{color: t.palette.pink}} width={size} />
|
||||
</Animated.View>
|
||||
) : (
|
||||
<HeartIconOutline
|
||||
@@ -100,7 +99,7 @@ export function AnimatedLikeIcon({
|
||||
entering={circle1Keyframe.duration(300)}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
backgroundColor: s.likeColor.color,
|
||||
backgroundColor: t.palette.pink,
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: size,
|
||||
|
||||
@@ -2,7 +2,6 @@ import {useEffect, useRef} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {useReducedMotion} from 'react-native-reanimated'
|
||||
|
||||
import {s} from '#/lib/styles'
|
||||
import {useTheme} from '#/alf'
|
||||
import {
|
||||
Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled,
|
||||
@@ -74,7 +73,7 @@ export function AnimatedLikeIcon({
|
||||
{isLiked ? (
|
||||
// @ts-expect-error is div
|
||||
<View ref={likeIconRef}>
|
||||
<HeartIconFilled style={s.likeColor} width={size} />
|
||||
<HeartIconFilled style={{color: t.palette.pink}} width={size} />
|
||||
</View>
|
||||
) : (
|
||||
<HeartIconOutline
|
||||
@@ -87,7 +86,7 @@ export function AnimatedLikeIcon({
|
||||
ref={circle1Ref}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
backgroundColor: s.likeColor.color,
|
||||
backgroundColor: t.palette.pink,
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: size,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useMemo} from 'react'
|
||||
import {useNavigation} from '@react-navigation/core'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {useDedupe} from '#/lib/hooks/useDedupe'
|
||||
import {type NavigationProp} from '#/lib/routes/types'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useEffect, useMemo, useState} from 'react'
|
||||
import {type EventArg, useNavigation} from '@react-navigation/core'
|
||||
import {type EventArg, useNavigation} from '@react-navigation/native'
|
||||
|
||||
if ('scrollRestoration' in history) {
|
||||
// Tell the brower not to mess with the scroll.
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export function Provider({children}: {children: React.ReactNode}) {
|
||||
return children
|
||||
}
|
||||
|
||||
export function useHotkeysContext() {
|
||||
return {
|
||||
enableScope: () => {},
|
||||
disableScope: () => {},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import {
|
||||
HotkeysProvider,
|
||||
useHotkeys,
|
||||
useHotkeysContext,
|
||||
} from 'react-hotkeys-hook'
|
||||
|
||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {emitFocusSearch} from '#/state/events'
|
||||
import {useSession} from '#/state/session'
|
||||
|
||||
enum Hotkeys {
|
||||
OPEN_COMPOSER = 'n',
|
||||
FOCUS_SEARCH = 'slash',
|
||||
}
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<unknown>) {
|
||||
return (
|
||||
<HotkeysProvider initiallyActiveScopes={['global']}>
|
||||
<KeyboardShortcuts>{children}</KeyboardShortcuts>
|
||||
</HotkeysProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export {useHotkeysContext}
|
||||
|
||||
function KeyboardShortcuts({children}: React.PropsWithChildren<unknown>) {
|
||||
useKeyboardShortcuts()
|
||||
return children
|
||||
}
|
||||
|
||||
function useKeyboardShortcuts() {
|
||||
const {openComposer} = useOpenComposer()
|
||||
const {hasSession} = useSession()
|
||||
const {t: l} = useLingui()
|
||||
|
||||
const shouldIgnore = (requiresSession: boolean = false) => {
|
||||
if (requiresSession && !hasSession) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const handleKey = (
|
||||
callback: () => void,
|
||||
options?: {requiresSession?: boolean},
|
||||
) => {
|
||||
if (shouldIgnore(options?.requiresSession)) {
|
||||
return
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
useHotkeys(
|
||||
Hotkeys.OPEN_COMPOSER,
|
||||
() =>
|
||||
handleKey(
|
||||
() => {
|
||||
openComposer({logContext: 'Other'})
|
||||
},
|
||||
{
|
||||
requiresSession: true,
|
||||
},
|
||||
),
|
||||
{scopes: ['global'], description: l`Compose new post`},
|
||||
[openComposer],
|
||||
)
|
||||
|
||||
useHotkeys(Hotkeys.FOCUS_SEARCH, () => handleKey(emitFocusSearch), {
|
||||
scopes: ['global'],
|
||||
preventDefault: true,
|
||||
description: l`Focus the search field`,
|
||||
})
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
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 {POST_IMG_MAX} from '#/lib/constants'
|
||||
import {logger} from '#/logger'
|
||||
@@ -322,7 +321,12 @@ export async function saveBytesToDisk(
|
||||
bytes: Uint8Array,
|
||||
type: string,
|
||||
) {
|
||||
const encoded = Buffer.from(bytes).toString('base64')
|
||||
// ideally we'd use `bytes.toBase64()`, but that's only baseline newly available
|
||||
let binary = ''
|
||||
for (const byte of bytes) {
|
||||
binary += String.fromCharCode(byte)
|
||||
}
|
||||
const encoded = btoa(binary)
|
||||
return await saveToDevice(filename, encoded, type)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
|
||||
import {createPersistedQueryStorage} from '#/lib/persisted-query-storage'
|
||||
import {listenNetworkConfirmed, listenNetworkLost} from '#/state/events'
|
||||
import {PERSISTED_QUERY_ROOT} from '#/state/queries'
|
||||
import {isQueryPersisted} from '#/state/queries/util'
|
||||
import * as env from '#/env'
|
||||
import {IS_NATIVE, IS_WEB} from '#/env'
|
||||
|
||||
@@ -137,8 +137,7 @@ const dehydrateOptions: PersistQueryClientProviderProps['persistOptions']['dehyd
|
||||
{
|
||||
shouldDehydrateMutation: (_: any) => false,
|
||||
shouldDehydrateQuery: query => {
|
||||
const root = String(query.queryKey[0])
|
||||
return root === PERSISTED_QUERY_ROOT
|
||||
return isQueryPersisted(query.queryKey)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -190,7 +189,10 @@ function QueryProviderInner({
|
||||
})
|
||||
useEffect(() => {
|
||||
if (IS_WEB) {
|
||||
window.__TANSTACK_QUERY_CLIENT__ = queryClient
|
||||
// WARNING, BROKEN
|
||||
// something since v5.32.0 causes OOMs. not important
|
||||
// so disable for now
|
||||
// window.__TANSTACK_QUERY_CLIENT__ = queryClient
|
||||
}
|
||||
}, [queryClient])
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {XRPCError} from '@atproto/xrpc'
|
||||
import {XRPCError} from '@atproto/api'
|
||||
import {t} from '@lingui/core/macro'
|
||||
|
||||
export function cleanError(str: any): string {
|
||||
|
||||
+1
-124
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
Dimensions,
|
||||
type StyleProp,
|
||||
StyleSheet,
|
||||
type TextStyle,
|
||||
} from 'react-native'
|
||||
import {type StyleProp, StyleSheet, type TextStyle} from 'react-native'
|
||||
|
||||
import {IS_WEB} from '#/env'
|
||||
import {type Theme, type TypographyVariant} from './ThemeContext'
|
||||
@@ -61,14 +56,6 @@ export const colors = {
|
||||
green5: '#082b03',
|
||||
|
||||
unreadNotifBg: '#ebf6ff',
|
||||
brandBlue: '#0066FF',
|
||||
like: '#ec4899',
|
||||
}
|
||||
|
||||
export const gradients = {
|
||||
blueLight: {start: '#5A71FA', end: colors.blue3}, // buttons
|
||||
blue: {start: '#5E55FB', end: colors.blue3}, // fab
|
||||
blueDark: {start: '#5F45E0', end: colors.blue3}, // avis, banner
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,57 +65,6 @@ export const s = StyleSheet.create({
|
||||
// helpers
|
||||
footerSpacer: {height: 100},
|
||||
contentContainer: {paddingBottom: 200},
|
||||
contentContainerExtra: {paddingBottom: 300},
|
||||
border0: {borderWidth: 0},
|
||||
border1: {borderWidth: 1},
|
||||
borderTop1: {borderTopWidth: 1},
|
||||
borderRight1: {borderRightWidth: 1},
|
||||
borderBottom1: {borderBottomWidth: 1},
|
||||
borderLeft1: {borderLeftWidth: 1},
|
||||
hidden: {display: 'none'},
|
||||
dimmed: {opacity: 0.5},
|
||||
|
||||
// font weights
|
||||
fw600: {fontWeight: '600'},
|
||||
bold: {fontWeight: '600'},
|
||||
fw500: {fontWeight: '600'},
|
||||
semiBold: {fontWeight: '600'},
|
||||
fw400: {fontWeight: '400'},
|
||||
normal: {fontWeight: '400'},
|
||||
fw300: {fontWeight: '400'},
|
||||
light: {fontWeight: '400'},
|
||||
|
||||
// text decoration
|
||||
underline: {textDecorationLine: 'underline'},
|
||||
|
||||
// font variants
|
||||
tabularNum: {fontVariant: ['tabular-nums']},
|
||||
|
||||
// font sizes
|
||||
f9: {fontSize: 9},
|
||||
f10: {fontSize: 10},
|
||||
f11: {fontSize: 11},
|
||||
f12: {fontSize: 12},
|
||||
f13: {fontSize: 13},
|
||||
f14: {fontSize: 14},
|
||||
f15: {fontSize: 15},
|
||||
f16: {fontSize: 16},
|
||||
f17: {fontSize: 17},
|
||||
f18: {fontSize: 18},
|
||||
|
||||
// line heights
|
||||
['lh13-1']: {lineHeight: 13},
|
||||
['lh13-1.3']: {lineHeight: 16.9}, // 1.3 of 13px
|
||||
['lh14-1']: {lineHeight: 14},
|
||||
['lh14-1.3']: {lineHeight: 18.2}, // 1.3 of 14px
|
||||
['lh15-1']: {lineHeight: 15},
|
||||
['lh15-1.3']: {lineHeight: 19.5}, // 1.3 of 15px
|
||||
['lh16-1']: {lineHeight: 16},
|
||||
['lh16-1.3']: {lineHeight: 20.8}, // 1.3 of 16px
|
||||
['lh17-1']: {lineHeight: 17},
|
||||
['lh17-1.3']: {lineHeight: 22.1}, // 1.3 of 17px
|
||||
['lh18-1']: {lineHeight: 18},
|
||||
['lh18-1.3']: {lineHeight: 23.4}, // 1.3 of 18px
|
||||
|
||||
// margins
|
||||
mr2: {marginRight: 2},
|
||||
@@ -171,74 +107,15 @@ export const s = StyleSheet.create({
|
||||
pb20: {paddingBottom: 20},
|
||||
px5: {paddingHorizontal: 5},
|
||||
|
||||
// flex
|
||||
flexRow: {flexDirection: 'row'},
|
||||
flexCol: {flexDirection: 'column'},
|
||||
flex1: {flex: 1},
|
||||
flexGrow1: {flexGrow: 1},
|
||||
alignCenter: {alignItems: 'center'},
|
||||
alignBaseline: {alignItems: 'baseline'},
|
||||
justifyCenter: {justifyContent: 'center'},
|
||||
|
||||
// position
|
||||
absolute: {position: 'absolute'},
|
||||
|
||||
// dimensions
|
||||
w100pct: {width: '100%'},
|
||||
h100pct: {height: '100%'},
|
||||
hContentRegion: IS_WEB ? {minHeight: '100%'} : {height: '100%'},
|
||||
window: {
|
||||
width: Dimensions.get('window').width,
|
||||
height: Dimensions.get('window').height,
|
||||
},
|
||||
|
||||
// text align
|
||||
textLeft: {textAlign: 'left'},
|
||||
textCenter: {textAlign: 'center'},
|
||||
textRight: {textAlign: 'right'},
|
||||
|
||||
// colors
|
||||
white: {color: colors.white},
|
||||
black: {color: colors.black},
|
||||
|
||||
gray1: {color: colors.gray1},
|
||||
gray2: {color: colors.gray2},
|
||||
gray3: {color: colors.gray3},
|
||||
gray4: {color: colors.gray4},
|
||||
gray5: {color: colors.gray5},
|
||||
|
||||
blue1: {color: colors.blue1},
|
||||
blue2: {color: colors.blue2},
|
||||
blue3: {color: colors.blue3},
|
||||
blue4: {color: colors.blue4},
|
||||
blue5: {color: colors.blue5},
|
||||
|
||||
red1: {color: colors.red1},
|
||||
red2: {color: colors.red2},
|
||||
red3: {color: colors.red3},
|
||||
red4: {color: colors.red4},
|
||||
red5: {color: colors.red5},
|
||||
|
||||
pink1: {color: colors.pink1},
|
||||
pink2: {color: colors.pink2},
|
||||
pink3: {color: colors.pink3},
|
||||
pink4: {color: colors.pink4},
|
||||
pink5: {color: colors.pink5},
|
||||
|
||||
purple1: {color: colors.purple1},
|
||||
purple2: {color: colors.purple2},
|
||||
purple3: {color: colors.purple3},
|
||||
purple4: {color: colors.purple4},
|
||||
purple5: {color: colors.purple5},
|
||||
|
||||
green1: {color: colors.green1},
|
||||
green2: {color: colors.green2},
|
||||
green3: {color: colors.green3},
|
||||
green4: {color: colors.green4},
|
||||
green5: {color: colors.green5},
|
||||
|
||||
brandBlue: {color: colors.brandBlue},
|
||||
likeColor: {color: colors.like},
|
||||
})
|
||||
|
||||
export function lh(
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import {useCallback, useContext, useEffect, useMemo, useState} from 'react'
|
||||
import {LayoutAnimation, Platform} from 'react-native'
|
||||
import {getLocales} from 'expo-localization'
|
||||
import {
|
||||
isTranslationSupported,
|
||||
onTranslateTask,
|
||||
} from '@bsky.app/expo-translate-text'
|
||||
import {onTranslateTask} from '@bsky.app/expo-translate-text'
|
||||
import {type TranslationTaskResult} from '@bsky.app/expo-translate-text/build/ExpoTranslateText.types'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
|
||||
import {useGoogleTranslate} from '#/lib/hooks/useGoogleTranslate'
|
||||
import {codeToLanguageName} from '#/locale/helpers'
|
||||
import {logger} from '#/logger'
|
||||
import {useLanguagePrefs} from '#/state/preferences'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_ANDROID, IS_IOS} from '#/env'
|
||||
import {IS_ANDROID, IS_IOS, IS_TRANSLATION_SUPPORTED} from '#/env'
|
||||
import {Context} from './context'
|
||||
import {
|
||||
type ContextType,
|
||||
@@ -25,6 +24,11 @@ import {guessLanguage} from './utils'
|
||||
export * from './types'
|
||||
export * from './utils'
|
||||
|
||||
const E_SAME_AS_SOURCE_LANGUAGE =
|
||||
'Translation result is the same as the source text.'
|
||||
const E_EMPTY_RESULT = 'Translation result is empty.'
|
||||
const E_INVALID_SOURCE_LANGUAGE = 'Invalid source language'
|
||||
|
||||
/**
|
||||
* Attempts on-device translation via @bsky.app/expo-translate-text.
|
||||
* Uses a lazy import to avoid crashing if the native module isn't linked into
|
||||
@@ -80,11 +84,11 @@ async function attemptTranslation(
|
||||
typeof result.translatedTexts === 'string' ? result.translatedTexts : ''
|
||||
|
||||
if (translatedText === input) {
|
||||
throw new Error('Translation result is the same as the source text.')
|
||||
throw new Error(E_SAME_AS_SOURCE_LANGUAGE)
|
||||
}
|
||||
|
||||
if (translatedText === '') {
|
||||
throw new Error('Translation result is empty.')
|
||||
throw new Error(E_EMPTY_RESULT)
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -159,6 +163,7 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
|
||||
>({})
|
||||
const [refCounts, setRefCounts] = useState<Record<string, number>>({})
|
||||
const ax = useAnalytics()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const {t: l} = useLingui()
|
||||
const googleTranslate = useGoogleTranslate()
|
||||
|
||||
@@ -235,7 +240,7 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
|
||||
googleTranslate: shouldForceGoogleTranslate,
|
||||
})
|
||||
|
||||
if (shouldForceGoogleTranslate || !isTranslationSupported()) {
|
||||
if (shouldForceGoogleTranslate || !IS_TRANSLATION_SUPPORTED) {
|
||||
await googleTranslate(
|
||||
text,
|
||||
expectedTargetLanguage,
|
||||
@@ -280,7 +285,8 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
|
||||
postLanguages: possibleSourceLanguages,
|
||||
},
|
||||
}))
|
||||
} catch (e) {
|
||||
} catch (err) {
|
||||
const e = err as Error
|
||||
logger.error('Failed to translate text on device', {safeMessage: e})
|
||||
// On-device translation failed (language pack missing or user
|
||||
// dismissed the download prompt).
|
||||
@@ -295,6 +301,21 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
|
||||
textLength: text.length,
|
||||
})
|
||||
let errorMessage = l`Device failed to translate :(`
|
||||
if (e.message === E_SAME_AS_SOURCE_LANGUAGE) {
|
||||
errorMessage = l`Translation to the same language is unavailable on your device.`
|
||||
}
|
||||
if (e.message === E_EMPTY_RESULT) {
|
||||
errorMessage = l`No translation received from your device.`
|
||||
}
|
||||
if (
|
||||
expectedSourceLanguage &&
|
||||
e.message.includes(E_INVALID_SOURCE_LANGUAGE)
|
||||
) {
|
||||
errorMessage = l`${codeToLanguageName(
|
||||
expectedSourceLanguage,
|
||||
langPrefs.appLanguage,
|
||||
)} is not supported by your device.`
|
||||
}
|
||||
if (!IS_ANDROID) {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
|
||||
}
|
||||
@@ -304,7 +325,7 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
|
||||
}))
|
||||
}
|
||||
},
|
||||
[ax, googleTranslate, l],
|
||||
[ax, googleTranslate, l, langPrefs.appLanguage],
|
||||
)
|
||||
|
||||
const ctx = useMemo(
|
||||
|
||||
@@ -182,6 +182,11 @@ msgstr ""
|
||||
msgid "{0} is not available"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: codeToLanguageName( expectedSourceLanguage, langPrefs.appLanguage, )
|
||||
#: src/lib/translation/index.tsx:314
|
||||
msgid "{0} is not supported by your device."
|
||||
msgstr "{0} is not supported by your device."
|
||||
|
||||
#. placeholder {0}: formatCount(i18n, JOINED_THIS_WEEK)
|
||||
#: src/screens/StarterPack/StarterPackLandingScreen.tsx:232
|
||||
msgid "{0} joined this week"
|
||||
@@ -852,8 +857,8 @@ msgstr ""
|
||||
#: src/view/com/composer/GifAltText.tsx:76
|
||||
#: src/view/com/composer/GifAltText.tsx:150
|
||||
#: src/view/com/composer/GifAltText.tsx:217
|
||||
#: src/view/com/composer/photos/Gallery.tsx:194
|
||||
#: src/view/com/composer/photos/Gallery.tsx:241
|
||||
#: src/view/com/composer/photos/Gallery.tsx:196
|
||||
#: src/view/com/composer/photos/Gallery.tsx:243
|
||||
#: src/view/com/composer/photos/ImageAltTextDialog.tsx:95
|
||||
#: src/view/com/composer/photos/ImageAltTextDialog.tsx:103
|
||||
msgid "Add alt text"
|
||||
@@ -905,8 +910,8 @@ msgstr ""
|
||||
msgid "Add media to post"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:522
|
||||
#: src/components/moderation/ReportDialog/index.tsx:526
|
||||
#: src/components/moderation/ReportDialog/index.tsx:532
|
||||
#: src/components/moderation/ReportDialog/index.tsx:536
|
||||
msgid "Add more details (optional)"
|
||||
msgstr ""
|
||||
|
||||
@@ -997,7 +1002,7 @@ msgstr ""
|
||||
msgid "Additional details (limit 1000 characters)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:540
|
||||
#: src/components/moderation/ReportDialog/index.tsx:550
|
||||
msgid "Additional details (limit 300 characters)"
|
||||
msgstr ""
|
||||
|
||||
@@ -1157,7 +1162,7 @@ msgstr ""
|
||||
#: src/components/images/Gallery.tsx:120
|
||||
#: src/components/Post/Embed/VideoEmbed/GifPresentationControls.tsx:94
|
||||
#: src/view/com/composer/GifAltText.tsx:100
|
||||
#: src/view/com/composer/photos/Gallery.tsx:212
|
||||
#: src/view/com/composer/photos/Gallery.tsx:214
|
||||
msgid "ALT"
|
||||
msgstr ""
|
||||
|
||||
@@ -1176,7 +1181,7 @@ msgid "Alt Text"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/composer/GifAltText.tsx:105
|
||||
#: src/view/com/composer/photos/Gallery.tsx:124
|
||||
#: src/view/com/composer/photos/Gallery.tsx:125
|
||||
msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone."
|
||||
msgstr ""
|
||||
|
||||
@@ -1827,20 +1832,20 @@ msgstr ""
|
||||
msgid "Browse custom feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:631
|
||||
#: src/components/FeedInterstitials.tsx:627
|
||||
msgid "Browse more accounts"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:761
|
||||
#: src/components/FeedInterstitials.tsx:757
|
||||
msgid "Browse more feeds on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:742
|
||||
#: src/components/FeedInterstitials.tsx:745
|
||||
#: src/components/FeedInterstitials.tsx:738
|
||||
#: src/components/FeedInterstitials.tsx:741
|
||||
msgid "Browse more suggestions"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:770
|
||||
#: src/components/FeedInterstitials.tsx:766
|
||||
msgid "Browse more suggestions on the Explore page"
|
||||
msgstr ""
|
||||
|
||||
@@ -1882,7 +1887,7 @@ msgstr ""
|
||||
#. placeholder {0}: sanitizeHandle(item.feed.creator.handle, '@')
|
||||
#. placeholder {0}: sanitizeHandle(labeler.creator.handle, '@')
|
||||
#: src/components/LabelingServiceCard/index.tsx:62
|
||||
#: src/components/moderation/ReportDialog/index.tsx:843
|
||||
#: src/components/moderation/ReportDialog/index.tsx:853
|
||||
#: src/screens/Search/components/StarterPackCard.tsx:107
|
||||
#: src/screens/Search/Explore.tsx:969
|
||||
msgid "By {0}"
|
||||
@@ -1948,7 +1953,7 @@ msgstr ""
|
||||
#: src/screens/Deactivated.tsx:150
|
||||
#: src/screens/Profile/Header/EditProfileDialog.tsx:215
|
||||
#: src/screens/Profile/Header/EditProfileDialog.tsx:223
|
||||
#: src/screens/Search/Shell.tsx:396
|
||||
#: src/screens/Search/Shell.tsx:397
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:42
|
||||
#: src/screens/Settings/AppIconSettings/index.tsx:228
|
||||
#: src/screens/Settings/components/ChangeHandleDialog.tsx:80
|
||||
@@ -1974,7 +1979,7 @@ msgstr ""
|
||||
msgid "Cancel reactivation and sign out"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Shell.tsx:387
|
||||
#: src/screens/Search/Shell.tsx:388
|
||||
msgid "Cancel search"
|
||||
msgstr ""
|
||||
|
||||
@@ -2023,7 +2028,7 @@ msgstr ""
|
||||
msgid "Change Handle"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:439
|
||||
#: src/components/moderation/ReportDialog/index.tsx:449
|
||||
msgid "Change moderation service"
|
||||
msgstr ""
|
||||
|
||||
@@ -2036,11 +2041,11 @@ msgstr ""
|
||||
msgid "Change password dialog"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:304
|
||||
#: src/components/moderation/ReportDialog/index.tsx:314
|
||||
msgid "Change report category"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:384
|
||||
#: src/components/moderation/ReportDialog/index.tsx:394
|
||||
msgid "Change report reason"
|
||||
msgstr ""
|
||||
|
||||
@@ -2213,7 +2218,7 @@ msgstr ""
|
||||
msgid "Clear image cache"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/forms/SearchInput.tsx:69
|
||||
#: src/components/forms/SearchInput.tsx:87
|
||||
msgid "Clear search query"
|
||||
msgstr ""
|
||||
|
||||
@@ -2333,7 +2338,7 @@ msgstr ""
|
||||
msgid "Close dialog"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/index.web.tsx:131
|
||||
#: src/view/shell/index.web.tsx:129
|
||||
msgid "Close drawer menu"
|
||||
msgstr ""
|
||||
|
||||
@@ -2425,6 +2430,7 @@ msgstr ""
|
||||
msgid "Complete the challenge"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/hotkeys/index.tsx:66
|
||||
#: src/view/com/feeds/ComposerPrompt.tsx:147
|
||||
#: src/view/shell/desktop/LeftNav.tsx:575
|
||||
msgid "Compose new post"
|
||||
@@ -2913,8 +2919,8 @@ msgstr ""
|
||||
|
||||
#. Accessibility label for button to create a moderation report for the selected option
|
||||
#. placeholder {0}: option.title
|
||||
#: src/components/moderation/ReportDialog/index.tsx:703
|
||||
#: src/components/moderation/ReportDialog/index.tsx:749
|
||||
#: src/components/moderation/ReportDialog/index.tsx:713
|
||||
#: src/components/moderation/ReportDialog/index.tsx:759
|
||||
msgid "Create report for {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -3159,7 +3165,7 @@ msgstr ""
|
||||
msgid "Developer options"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/translation/index.tsx:297
|
||||
#: src/lib/translation/index.tsx:303
|
||||
msgid "Device failed to translate :("
|
||||
msgstr "Device failed to translate :("
|
||||
|
||||
@@ -3489,7 +3495,7 @@ msgstr ""
|
||||
|
||||
#: src/view/com/composer/photos/EditImageDialog.web.tsx:86
|
||||
#: src/view/com/composer/photos/EditImageDialog.web.tsx:90
|
||||
#: src/view/com/composer/photos/Gallery.tsx:219
|
||||
#: src/view/com/composer/photos/Gallery.tsx:221
|
||||
msgid "Edit image"
|
||||
msgstr ""
|
||||
|
||||
@@ -3760,7 +3766,7 @@ msgstr ""
|
||||
msgid "Enter your username and password"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:148
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:150
|
||||
msgid "Enters full screen"
|
||||
msgstr ""
|
||||
|
||||
@@ -4201,7 +4207,7 @@ msgstr ""
|
||||
#. placeholder {0}: sanitizeHandle(feed.creatorHandle, '@')
|
||||
#. placeholder {0}: sanitizeHandle(view.creator.handle, '@')
|
||||
#: src/components/FeedCard.tsx:170
|
||||
#: src/state/queries/feed.ts:121
|
||||
#: src/state/queries/feed.ts:118
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:151
|
||||
msgid "Feed by {0}"
|
||||
msgstr ""
|
||||
@@ -4344,7 +4350,7 @@ msgstr ""
|
||||
msgid "Find people to follow"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Shell.tsx:529
|
||||
#: src/screens/Search/Shell.tsx:530
|
||||
msgid "Find posts, users, and feeds on Bluesky"
|
||||
msgstr ""
|
||||
|
||||
@@ -4388,6 +4394,10 @@ msgstr ""
|
||||
msgid "Focus code input"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/hotkeys/index.tsx:73
|
||||
msgid "Focus the search field"
|
||||
msgstr "Focus the search field"
|
||||
|
||||
#. User is not following this account, click to follow
|
||||
#: src/components/ProfileCard.tsx:546
|
||||
#: src/components/ProfileHoverCard/index.web.tsx:495
|
||||
@@ -5933,7 +5943,7 @@ msgstr ""
|
||||
msgid "Load new notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Profile/ProfileFeed/index.tsx:203
|
||||
#: src/screens/Profile/ProfileFeed/index.tsx:204
|
||||
#: src/screens/Profile/Sections/Feed.tsx:117
|
||||
#: src/screens/ProfileList/FeedSection.tsx:113
|
||||
#: src/view/com/feeds/FeedPage.tsx:170
|
||||
@@ -6236,7 +6246,7 @@ msgstr ""
|
||||
msgid "Music"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:169
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:171
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx:97
|
||||
msgctxt "video"
|
||||
msgid "Mute"
|
||||
@@ -6383,8 +6393,8 @@ msgstr ""
|
||||
msgid "Navigates to your profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:335
|
||||
#: src/components/moderation/ReportDialog/index.tsx:352
|
||||
#: src/components/moderation/ReportDialog/index.tsx:345
|
||||
#: src/components/moderation/ReportDialog/index.tsx:362
|
||||
msgid "Need to report a copyright violation, legal request, or regulatory compliance issue?"
|
||||
msgstr ""
|
||||
|
||||
@@ -6470,7 +6480,7 @@ msgstr ""
|
||||
msgid "New password"
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Profile/ProfileFeed/index.tsx:220
|
||||
#: src/screens/Profile/ProfileFeed/index.tsx:221
|
||||
#: src/screens/ProfileList/index.tsx:251
|
||||
#: src/screens/ProfileList/index.tsx:300
|
||||
#: src/view/screens/Feeds.tsx:553
|
||||
@@ -6480,13 +6490,9 @@ msgid "New post"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedPage.tsx:181
|
||||
msgctxt "action"
|
||||
msgid "New post"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/LeftNav.tsx:583
|
||||
msgctxt "action"
|
||||
msgid "New Post"
|
||||
msgid "New post"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/notifications/NotificationFeedItem.tsx:545
|
||||
@@ -6712,6 +6718,10 @@ msgstr ""
|
||||
msgid "No thanks"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/translation/index.tsx:308
|
||||
msgid "No translation received from your device."
|
||||
msgstr "No translation received from your device."
|
||||
|
||||
#: src/view/screens/Profile.tsx:500
|
||||
msgid "No video posts yet"
|
||||
msgstr ""
|
||||
@@ -7262,7 +7272,7 @@ msgstr ""
|
||||
msgid "Password updated!"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:153
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:155
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx:395
|
||||
msgid "Pause"
|
||||
msgstr ""
|
||||
@@ -7377,7 +7387,7 @@ msgstr ""
|
||||
msgid "Pinned to your feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:153
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:155
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx:396
|
||||
msgid "Play"
|
||||
msgstr ""
|
||||
@@ -7404,7 +7414,7 @@ msgstr ""
|
||||
msgid "Plays or pauses the GIF"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:154
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:156
|
||||
msgid "Plays or pauses the video"
|
||||
msgstr ""
|
||||
|
||||
@@ -8121,7 +8131,7 @@ msgstr ""
|
||||
msgid "Remove from your feeds?"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/composer/photos/Gallery.tsx:228
|
||||
#: src/view/com/composer/photos/Gallery.tsx:230
|
||||
msgid "Remove image"
|
||||
msgstr ""
|
||||
|
||||
@@ -8329,7 +8339,7 @@ msgid "Report conversation"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:98
|
||||
#: src/components/moderation/ReportDialog/index.tsx:255
|
||||
#: src/components/moderation/ReportDialog/index.tsx:265
|
||||
msgid "Report dialog"
|
||||
msgstr ""
|
||||
|
||||
@@ -8544,7 +8554,7 @@ msgstr ""
|
||||
#: src/components/dms/MessageItem.tsx:322
|
||||
#: src/components/Error.tsx:66
|
||||
#: src/components/Lists.tsx:115
|
||||
#: src/components/moderation/ReportDialog/index.tsx:289
|
||||
#: src/components/moderation/ReportDialog/index.tsx:299
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:56
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:59
|
||||
#: src/components/StarterPack/ProfileStarterPacks.tsx:377
|
||||
@@ -8566,7 +8576,7 @@ msgstr ""
|
||||
msgid "Retry"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:286
|
||||
#: src/components/moderation/ReportDialog/index.tsx:296
|
||||
#: src/view/screens/Storybook/Admonitions.tsx:61
|
||||
msgid "Retry loading report options"
|
||||
msgstr ""
|
||||
@@ -8722,10 +8732,10 @@ msgid "Scroll to top"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/dialogs/SearchablePeopleList.tsx:515
|
||||
#: src/components/forms/SearchInput.tsx:33
|
||||
#: src/components/forms/SearchInput.tsx:35
|
||||
#: src/components/forms/SearchInput.tsx:51
|
||||
#: src/components/forms/SearchInput.tsx:53
|
||||
#: src/screens/Search/Shell.tsx:354
|
||||
#: src/screens/Search/Shell.tsx:517
|
||||
#: src/screens/Search/Shell.tsx:518
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:199
|
||||
msgid "Search"
|
||||
msgstr ""
|
||||
@@ -8759,14 +8769,14 @@ msgstr ""
|
||||
msgid "Search for \"{interestsDisplayName}\" (active)"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/Search.tsx:130
|
||||
msgid "Search for \"{query}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/components/AutocompleteResults.tsx:47
|
||||
msgid "Search for \"{searchText}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/Search.tsx:129
|
||||
msgid "Search for “{tQuery}”"
|
||||
msgstr "Search for “{tQuery}”"
|
||||
|
||||
#: src/screens/StarterPack/Wizard/index.tsx:552
|
||||
msgid "Search for feeds that you want to suggest to others."
|
||||
msgstr ""
|
||||
@@ -8864,12 +8874,12 @@ msgstr ""
|
||||
msgid "See jobs at Bluesky"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:584
|
||||
#: src/components/FeedInterstitials.tsx:645
|
||||
#: src/components/FeedInterstitials.tsx:583
|
||||
#: src/components/FeedInterstitials.tsx:641
|
||||
msgid "See more"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:565
|
||||
#: src/components/FeedInterstitials.tsx:564
|
||||
msgid "See more suggested profiles"
|
||||
msgstr ""
|
||||
|
||||
@@ -8906,7 +8916,7 @@ msgstr ""
|
||||
msgid "Select a color"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:374
|
||||
#: src/components/moderation/ReportDialog/index.tsx:384
|
||||
msgid "Select a reason"
|
||||
msgstr ""
|
||||
|
||||
@@ -8991,7 +9001,7 @@ msgstr ""
|
||||
msgid "Select languages"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:424
|
||||
#: src/components/moderation/ReportDialog/index.tsx:434
|
||||
msgid "Select moderation service"
|
||||
msgstr ""
|
||||
|
||||
@@ -9090,7 +9100,7 @@ msgstr ""
|
||||
msgid "Send post to..."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:814
|
||||
#: src/components/moderation/ReportDialog/index.tsx:824
|
||||
msgid "Send report to {title}"
|
||||
msgstr ""
|
||||
|
||||
@@ -9565,7 +9575,7 @@ msgstr ""
|
||||
msgid "Some of your verifications are invalid."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/FeedInterstitials.tsx:724
|
||||
#: src/components/FeedInterstitials.tsx:720
|
||||
msgid "Some other feeds you might like"
|
||||
msgstr ""
|
||||
|
||||
@@ -9593,7 +9603,7 @@ msgid "Something went wrong"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/ageAssurance/AgeAssuranceInitDialog.tsx:139
|
||||
#: src/components/moderation/ReportDialog/index.tsx:281
|
||||
#: src/components/moderation/ReportDialog/index.tsx:291
|
||||
#: src/screens/Deactivated.tsx:86
|
||||
#: src/screens/Settings/components/DeactivateAccountDialog.tsx:59
|
||||
#: src/view/screens/Storybook/Admonitions.tsx:56
|
||||
@@ -9625,7 +9635,7 @@ msgstr ""
|
||||
msgid "Sorry, we're unable to load account suggestions at this time."
|
||||
msgstr ""
|
||||
|
||||
#: src/App.native.tsx:142
|
||||
#: src/App.native.tsx:141
|
||||
#: src/App.web.tsx:119
|
||||
msgid "Sorry! Your session expired. Please sign in again."
|
||||
msgstr ""
|
||||
@@ -9778,9 +9788,9 @@ msgstr ""
|
||||
msgid "Submit Appeal"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:502
|
||||
#: src/components/moderation/ReportDialog/index.tsx:563
|
||||
#: src/components/moderation/ReportDialog/index.tsx:570
|
||||
#: src/components/moderation/ReportDialog/index.tsx:512
|
||||
#: src/components/moderation/ReportDialog/index.tsx:573
|
||||
#: src/components/moderation/ReportDialog/index.tsx:580
|
||||
msgid "Submit report"
|
||||
msgstr ""
|
||||
|
||||
@@ -10175,7 +10185,7 @@ msgid "There was an issue fetching notifications. Tap here to try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/screens/Search/Explore.tsx:1025
|
||||
#: src/view/com/posts/PostFeed.tsx:763
|
||||
#: src/view/com/posts/PostFeed.tsx:773
|
||||
msgid "There was an issue fetching posts. Tap here to try again."
|
||||
msgstr ""
|
||||
|
||||
@@ -10609,7 +10619,7 @@ msgstr ""
|
||||
msgid "Toggle to enable or disable adult content"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:171
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:173
|
||||
msgid "Toggles the sound"
|
||||
msgstr ""
|
||||
|
||||
@@ -10664,6 +10674,10 @@ msgstr "Translating"
|
||||
msgid "Translating…"
|
||||
msgstr "Translating…"
|
||||
|
||||
#: src/lib/translation/index.tsx:305
|
||||
msgid "Translation to the same language is unavailable on your device."
|
||||
msgstr "Translation to the same language is unavailable on your device."
|
||||
|
||||
#: src/screens/Settings/ThreadPreferences.tsx:87
|
||||
#: src/screens/Settings/ThreadPreferences.tsx:92
|
||||
msgid "Tree view"
|
||||
@@ -10843,7 +10857,7 @@ msgstr ""
|
||||
msgid "Unfollows the user"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/moderation/ReportDialog/index.tsx:486
|
||||
#: src/components/moderation/ReportDialog/index.tsx:496
|
||||
msgid "Unfortunately, none of your subscribed labelers supports this report type."
|
||||
msgstr ""
|
||||
|
||||
@@ -10882,7 +10896,7 @@ msgstr ""
|
||||
msgid "Unmute"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:168
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:170
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx:96
|
||||
msgctxt "video"
|
||||
msgid "Unmute"
|
||||
@@ -10981,6 +10995,10 @@ msgstr ""
|
||||
msgid "Unsubscribed from list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/composer/text-input/TextInput.tsx:131
|
||||
msgid "Unsupported clipboard content"
|
||||
msgstr "Unsupported clipboard content"
|
||||
|
||||
#: src/view/com/composer/Composer.tsx:1369
|
||||
msgid "Unsupported video type: {mimeType}"
|
||||
msgstr ""
|
||||
@@ -11136,7 +11154,7 @@ msgid "User list by {0}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: sanitizeHandle(view.creator.handle, '@')
|
||||
#: src/state/queries/feed.ts:162
|
||||
#: src/state/queries/feed.ts:159
|
||||
msgid "User List by {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -11295,8 +11313,8 @@ msgstr ""
|
||||
msgid "Version {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:86
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:147
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:87
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:149
|
||||
msgid "Video"
|
||||
msgstr ""
|
||||
|
||||
@@ -11344,7 +11362,7 @@ msgid "Video uploaded"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: embed.alt
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:86
|
||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:87
|
||||
msgid "Video: {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -12522,7 +12540,7 @@ msgid "Your reply was sent"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: state.selectedLabeler?.creator.displayName
|
||||
#: src/components/moderation/ReportDialog/index.tsx:513
|
||||
#: src/components/moderation/ReportDialog/index.tsx:523
|
||||
msgid "Your report will be sent to <0>{0}</0>."
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -52,8 +52,9 @@ import {atoms as a, native, platform, useBreakpoints, web} from '#/alf'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {ListFooter} from '#/components/Lists'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_NATIVE} from '#/env'
|
||||
|
||||
const PARENT_CHUNK_SIZE = 20
|
||||
const PARENT_CHUNK_SIZE = IS_NATIVE ? 5 : 20
|
||||
const CHILDREN_CHUNK_SIZE = 50
|
||||
|
||||
export function PostThread({uri}: {uri: string}) {
|
||||
|
||||
@@ -185,6 +185,7 @@ export function ProfileFeedScreenInner({
|
||||
|
||||
<FeedFeedbackProvider value={feedFeedback}>
|
||||
<PostFeed
|
||||
enabled
|
||||
feed={feed}
|
||||
feedParams={feedParams}
|
||||
pollInterval={60e3}
|
||||
|
||||
@@ -380,6 +380,7 @@ export function SearchScreenShell({
|
||||
inputPlaceholder ?? l`Search for posts, users, or feeds`
|
||||
}
|
||||
hitSlop={{...HITSLOP_20, top: 0}}
|
||||
hotkey={true}
|
||||
/>
|
||||
</View>
|
||||
{showAutocomplete && (
|
||||
|
||||
Vendored
+1
-1
@@ -5,7 +5,7 @@ import {
|
||||
type AppBskyFeedDefs,
|
||||
} from '@atproto/api'
|
||||
import {type QueryClient} from '@tanstack/react-query'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
|
||||
import {batchedUpdates} from '#/lib/batchedUpdates'
|
||||
import {findAllPostsInQueryData as findAllPostsInBookmarksQueryData} from '#/state/queries/bookmarks/useBookmarksQuery'
|
||||
|
||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
import {useEffect, useMemo, useState} from 'react'
|
||||
import {type AppBskyActorDefs, type AppBskyNotificationDefs} from '@atproto/api'
|
||||
import {type QueryClient} from '@tanstack/react-query'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
|
||||
import {batchedUpdates} from '#/lib/batchedUpdates'
|
||||
import {findAllProfilesInQueryData as findAllProfilesInActivitySubscriptionsQueryData} from '#/state/queries/activity-subscriptions'
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
useState,
|
||||
} from 'react'
|
||||
|
||||
import {useHotkeysContext} from '#/lib/hotkeys'
|
||||
import {type DialogControlRefProps} from '#/components/Dialog'
|
||||
import {Provider as GlobalDialogsProvider} from '#/components/dialogs/Context'
|
||||
import {IS_WEB} from '#/env'
|
||||
@@ -62,6 +63,7 @@ export function useDialogFullyExpandedCountContext() {
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [fullyExpandedCount, setFullyExpandedCount] = useState(0)
|
||||
const {disableScope, enableScope} = useHotkeysContext()
|
||||
|
||||
const activeDialogs = useRef<
|
||||
Map<string, React.MutableRefObject<DialogControlRefProps>>
|
||||
@@ -77,18 +79,26 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
|
||||
return openDialogs.current.size > 0
|
||||
} else {
|
||||
BottomSheetNativeComponent.dismissAll()
|
||||
void BottomSheetNativeComponent.dismissAll()
|
||||
return false
|
||||
}
|
||||
}, [])
|
||||
|
||||
const setDialogIsOpen = useCallback((id: string, isOpen: boolean) => {
|
||||
if (isOpen) {
|
||||
openDialogs.current.add(id)
|
||||
} else {
|
||||
openDialogs.current.delete(id)
|
||||
}
|
||||
}, [])
|
||||
const setDialogIsOpen = useCallback(
|
||||
(id: string, isOpen: boolean) => {
|
||||
if (isOpen) {
|
||||
openDialogs.current.add(id)
|
||||
} else {
|
||||
openDialogs.current.delete(id)
|
||||
}
|
||||
if (openDialogs.current.size > 0) {
|
||||
disableScope('global')
|
||||
} else {
|
||||
enableScope('global')
|
||||
}
|
||||
},
|
||||
[disableScope, enableScope],
|
||||
)
|
||||
|
||||
const context = useMemo<IDialogContext>(
|
||||
() => ({
|
||||
|
||||
+9
-1
@@ -1,4 +1,4 @@
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
|
||||
type UnlistenFn = () => void
|
||||
|
||||
@@ -45,3 +45,11 @@ export function listenPostCreated(fn: () => void): UnlistenFn {
|
||||
emitter.on('post-created', fn)
|
||||
return () => emitter.off('post-created', fn)
|
||||
}
|
||||
|
||||
export function emitFocusSearch() {
|
||||
emitter.emit('focus-search')
|
||||
}
|
||||
export function listenFocusSearch(fn: () => void): UnlistenFn {
|
||||
emitter.on('focus-search', fn)
|
||||
return () => emitter.off('focus-search', fn)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
type GestureUpdateEvent,
|
||||
type PanGestureHandlerEventPayload,
|
||||
} from 'react-native-gesture-handler'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
|
||||
export type GlobalGestureEvents = {
|
||||
begin: GestureStateChangeEvent<PanGestureHandlerEventPayload>
|
||||
|
||||
+11
-1
@@ -1,7 +1,8 @@
|
||||
import {createContext, useContext, useMemo, useState} from 'react'
|
||||
import {createContext, useContext, useEffect, useMemo, useState} from 'react'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {useHotkeysContext} from '#/lib/hotkeys'
|
||||
import {type ImageSource} from '#/view/com/lightbox/ImageViewing/@types'
|
||||
|
||||
export type Lightbox = {
|
||||
@@ -28,6 +29,15 @@ LightboxControlContext.displayName = 'LightboxControlContext'
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [activeLightbox, setActiveLightbox] = useState<Lightbox | null>(null)
|
||||
const {disableScope, enableScope} = useHotkeysContext()
|
||||
|
||||
useEffect(() => {
|
||||
if (activeLightbox) {
|
||||
disableScope('global')
|
||||
} else {
|
||||
enableScope('global')
|
||||
}
|
||||
}, [activeLightbox, disableScope, enableScope])
|
||||
|
||||
const openLightbox = useNonReactiveCallback(
|
||||
(lightbox: Omit<Lightbox, 'id'>) => {
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
type ChatBskyConvoGetLog,
|
||||
type ChatBskyConvoSendMessage,
|
||||
} from '@atproto/api'
|
||||
import {XRPCError} from '@atproto/xrpc'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {XRPCError} from '@atproto/api'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
|
||||
import {networkRetry} from '#/lib/async/retry'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {type BskyAgent, type ChatBskyConvoGetLog} from '@atproto/api'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
|
||||
import {networkRetry} from '#/lib/async/retry'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {createContext, useContext, useMemo, useState} from 'react'
|
||||
import {createContext, useContext, useEffect, useMemo, useState} from 'react'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {useHotkeysContext} from '#/lib/hotkeys'
|
||||
|
||||
export interface UserAddRemoveListsModal {
|
||||
name: 'user-add-remove-lists'
|
||||
@@ -47,6 +48,15 @@ ModalControlContext.displayName = 'ModalControlContext'
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [activeModals, setActiveModals] = useState<Modal[]>([])
|
||||
const {disableScope, enableScope} = useHotkeysContext()
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModals.length > 0) {
|
||||
disableScope('global')
|
||||
} else {
|
||||
enableScope('global')
|
||||
}
|
||||
}, [activeModals.length, disableScope, enableScope])
|
||||
|
||||
const openModal = useNonReactiveCallback((modal: Modal) => {
|
||||
setActiveModals(modals => [...modals, modal])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
|
||||
import BroadcastChannel from '#/lib/broadcast'
|
||||
import {logger} from '#/logger'
|
||||
|
||||
+18
-11
@@ -22,13 +22,10 @@ import {
|
||||
import {DISCOVER_FEED_URI, DISCOVER_SAVED_FEED} from '#/lib/constants'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {
|
||||
PERSISTED_QUERY_GCTIME,
|
||||
PERSISTED_QUERY_ROOT,
|
||||
STALE,
|
||||
} from '#/state/queries'
|
||||
import {GCTIME, STALE} from '#/state/queries'
|
||||
import {RQKEY as listQueryKey} from '#/state/queries/list'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {createQueryKey} from '#/state/queries/util'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import {router} from '#/routes'
|
||||
import {useModerationOpts} from '../preferences/moderation-opts'
|
||||
@@ -416,10 +413,20 @@ const PWI_DISCOVER_FEED_STUB: SavedFeedSourceInfo = {
|
||||
contentMode: undefined,
|
||||
}
|
||||
|
||||
const createPinnedFeedInfosQueryKeyRoot = (
|
||||
const createPinnedFeedInfosQueryKey = (
|
||||
kind: 'pinned' | 'saved',
|
||||
feedUris: string[],
|
||||
) => [PERSISTED_QUERY_ROOT, 'feed-info', kind, feedUris]
|
||||
) =>
|
||||
createQueryKey(
|
||||
'feed-info',
|
||||
{
|
||||
kind,
|
||||
feedUris,
|
||||
},
|
||||
{
|
||||
persistedVersion: 1,
|
||||
},
|
||||
)
|
||||
|
||||
export function usePinnedFeedsInfos() {
|
||||
const {hasSession} = useSession()
|
||||
@@ -428,11 +435,11 @@ export function usePinnedFeedsInfos() {
|
||||
const pinnedItems = preferences?.savedFeeds.filter(feed => feed.pinned) ?? []
|
||||
|
||||
return useQuery({
|
||||
queryKey: createPinnedFeedInfosQueryKeyRoot(
|
||||
queryKey: createPinnedFeedInfosQueryKey(
|
||||
'pinned',
|
||||
pinnedItems.map(f => f.value),
|
||||
),
|
||||
gcTime: PERSISTED_QUERY_GCTIME,
|
||||
gcTime: GCTIME.INFINITY,
|
||||
staleTime: STALE.INFINITY,
|
||||
enabled: !isLoadingPrefs,
|
||||
queryFn: async () => {
|
||||
@@ -536,11 +543,11 @@ export function useSavedFeeds() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useQuery({
|
||||
queryKey: createPinnedFeedInfosQueryKeyRoot(
|
||||
queryKey: createPinnedFeedInfosQueryKey(
|
||||
'saved',
|
||||
savedItems.map(f => f.value),
|
||||
),
|
||||
gcTime: PERSISTED_QUERY_GCTIME,
|
||||
gcTime: GCTIME.INFINITY,
|
||||
staleTime: STALE.INFINITY,
|
||||
enabled: !isLoadingPrefs,
|
||||
placeholderData: previousData => {
|
||||
|
||||
@@ -19,22 +19,6 @@ export const STALE = {
|
||||
INFINITY: Infinity,
|
||||
}
|
||||
|
||||
/**
|
||||
* Root key for persisted queries.
|
||||
*
|
||||
* If the `querykey` of your query uses this at index 0, it will be
|
||||
* persisted automatically by the `PersistQueryClientProvider` in
|
||||
* `#/lib/react-query.tsx`.
|
||||
*
|
||||
* Be careful when using this, since it will change the query key and may
|
||||
* break any cases where we call `invalidateQueries` or `refetchQueries`
|
||||
* with the old key.
|
||||
*
|
||||
* Also, only use this for queries that are safe to persist between
|
||||
* app launches (like user preferences).
|
||||
*
|
||||
* Note that for queries that are persisted, it is recommended to extend
|
||||
* the `gcTime` to a longer duration, otherwise it'll get busted
|
||||
*/
|
||||
export const PERSISTED_QUERY_ROOT = 'PERSISTED'
|
||||
export const PERSISTED_QUERY_GCTIME = Infinity
|
||||
export const GCTIME = {
|
||||
INFINITY: Infinity,
|
||||
}
|
||||
|
||||
@@ -3,15 +3,12 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
import {z} from 'zod'
|
||||
|
||||
import {MAX_LABELERS} from '#/lib/constants'
|
||||
import {
|
||||
PERSISTED_QUERY_GCTIME,
|
||||
PERSISTED_QUERY_ROOT,
|
||||
STALE,
|
||||
} from '#/state/queries'
|
||||
import {GCTIME, STALE} from '#/state/queries'
|
||||
import {
|
||||
preferencesQueryKey,
|
||||
usePreferencesQuery,
|
||||
} from '#/state/queries/preferences'
|
||||
import {createQueryKey} from '#/state/queries/util'
|
||||
import {useAgent} from '#/state/session'
|
||||
|
||||
const labelerInfoQueryKeyRoot = 'labeler-info'
|
||||
@@ -26,11 +23,8 @@ export const labelersInfoQueryKey = (dids: string[]) => [
|
||||
dids.slice().sort(),
|
||||
]
|
||||
|
||||
const persistedLabelersDetailedInfoQueryKey = (dids: string[]) => [
|
||||
PERSISTED_QUERY_ROOT,
|
||||
'labelers-detailed-info',
|
||||
dids,
|
||||
]
|
||||
const createLabelersDetailedInfoQueryKey = (dids: string[]) =>
|
||||
createQueryKey('labelers-detailed-info', {dids}, {persistedVersion: 1})
|
||||
|
||||
export function useLabelerInfoQuery({
|
||||
did,
|
||||
@@ -69,8 +63,8 @@ export function useLabelersDetailedInfoQuery({dids}: {dids: string[]}) {
|
||||
const agent = useAgent()
|
||||
return useQuery({
|
||||
enabled: !!dids.length,
|
||||
queryKey: persistedLabelersDetailedInfoQueryKey(dids),
|
||||
gcTime: PERSISTED_QUERY_GCTIME,
|
||||
queryKey: createLabelersDetailedInfoQueryKey(dids),
|
||||
gcTime: GCTIME.INFINITY,
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryFn: async () => {
|
||||
const res = await agent.app.bsky.labeler.getServices({
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from 'react'
|
||||
import {AppState} from 'react-native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
|
||||
import BroadcastChannel from '#/lib/broadcast'
|
||||
import {resetBadgeCount} from '#/lib/notifications/notifications'
|
||||
|
||||
@@ -9,11 +9,7 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
import {PROD_DEFAULT_FEED} from '#/lib/constants'
|
||||
import {replaceEqualDeep} from '#/lib/functions'
|
||||
import {getAge} from '#/lib/strings/time'
|
||||
import {
|
||||
PERSISTED_QUERY_GCTIME,
|
||||
PERSISTED_QUERY_ROOT,
|
||||
STALE,
|
||||
} from '#/state/queries'
|
||||
import {GCTIME, STALE} from '#/state/queries'
|
||||
import {
|
||||
DEFAULT_HOME_FEED_PREFS,
|
||||
DEFAULT_LOGGED_OUT_PREFERENCES,
|
||||
@@ -23,6 +19,7 @@ import {
|
||||
type ThreadViewPreferences,
|
||||
type UsePreferencesQueryResponse,
|
||||
} from '#/state/queries/preferences/types'
|
||||
import {createQueryKey} from '#/state/queries/util'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {saveLabelers} from '#/state/session/agent-config'
|
||||
import {useAgeAssurance} from '#/ageAssurance'
|
||||
@@ -33,7 +30,11 @@ export * from '#/state/queries/preferences/const'
|
||||
export * from '#/state/queries/preferences/moderation'
|
||||
export * from '#/state/queries/preferences/types'
|
||||
|
||||
export const preferencesQueryKey = [PERSISTED_QUERY_ROOT, 'getPreferences']
|
||||
export const preferencesQueryKey = createQueryKey(
|
||||
'getPreferences',
|
||||
{},
|
||||
{persistedVersion: 1},
|
||||
)
|
||||
|
||||
export function usePreferencesQuery() {
|
||||
const agent = useAgent()
|
||||
@@ -44,7 +45,7 @@ export function usePreferencesQuery() {
|
||||
structuralSharing: replaceEqualDeep,
|
||||
refetchOnWindowFocus: true,
|
||||
queryKey: preferencesQueryKey,
|
||||
gcTime: PERSISTED_QUERY_GCTIME,
|
||||
gcTime: GCTIME.INFINITY,
|
||||
queryFn: async () => {
|
||||
if (!agent.did) {
|
||||
return DEFAULT_LOGGED_OUT_PREFERENCES
|
||||
|
||||
@@ -99,6 +99,7 @@ export function useProfilesQuery({
|
||||
}) {
|
||||
const agent = useAgent()
|
||||
return useQuery({
|
||||
enabled: handles.length > 0,
|
||||
staleTime: STALE.MINUTES.FIVE,
|
||||
queryKey: profilesQueryKey(handles),
|
||||
queryFn: async () => {
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import {AtUri} from '@atproto/api'
|
||||
import {
|
||||
type QueryClient,
|
||||
useQuery,
|
||||
type UseQueryResult,
|
||||
} from '@tanstack/react-query'
|
||||
import {AtUri, type BskyAgent} from '@atproto/api'
|
||||
import {type QueryClient, queryOptions, useQuery} from '@tanstack/react-query'
|
||||
|
||||
import {STALE} from '#/state/queries'
|
||||
import {useAgent} from '#/state/session'
|
||||
@@ -12,26 +8,12 @@ import {useUnstableProfileViewCache} from './profile'
|
||||
const RQKEY_ROOT = 'resolved-did'
|
||||
export const RQKEY = (didOrHandle: string) => [RQKEY_ROOT, didOrHandle]
|
||||
|
||||
type UriUseQueryResult = UseQueryResult<{did: string; uri: string}, Error>
|
||||
export function useResolveUriQuery(uri: string | undefined): UriUseQueryResult {
|
||||
const urip = new AtUri(uri || '')
|
||||
const res = useResolveDidQuery(urip.host)
|
||||
if (res.data) {
|
||||
// @ts-expect-error TODO new-sdk-migration
|
||||
urip.host = res.data
|
||||
return {
|
||||
...res,
|
||||
data: {did: urip.host, uri: urip.toString()},
|
||||
} as UriUseQueryResult
|
||||
}
|
||||
return res as UriUseQueryResult
|
||||
}
|
||||
|
||||
export function useResolveDidQuery(didOrHandle: string | undefined) {
|
||||
const agent = useAgent()
|
||||
const {getUnstableProfile} = useUnstableProfileViewCache()
|
||||
|
||||
return useQuery<string, Error>({
|
||||
const resolvedDidQueryOptions = (
|
||||
agent: BskyAgent,
|
||||
getUnstableProfile: (did: string) => {did: string} | undefined,
|
||||
didOrHandle: string | undefined,
|
||||
) =>
|
||||
queryOptions({
|
||||
staleTime: STALE.HOURS.ONE,
|
||||
queryKey: RQKEY(didOrHandle ?? ''),
|
||||
queryFn: async () => {
|
||||
@@ -50,6 +32,30 @@ export function useResolveDidQuery(didOrHandle: string | undefined) {
|
||||
},
|
||||
enabled: !!didOrHandle,
|
||||
})
|
||||
|
||||
export function useResolveUriQuery(uri: string | undefined) {
|
||||
const urip = new AtUri(uri || '')
|
||||
const host = urip.host
|
||||
|
||||
const agent = useAgent()
|
||||
const {getUnstableProfile} = useUnstableProfileViewCache()
|
||||
|
||||
return useQuery({
|
||||
...resolvedDidQueryOptions(agent, getUnstableProfile, host),
|
||||
select: did => ({
|
||||
did,
|
||||
uri: AtUri.make(did, urip.collection, urip.rkey).toString(),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
export function useResolveDidQuery(didOrHandle: string | undefined) {
|
||||
const agent = useAgent()
|
||||
const {getUnstableProfile} = useUnstableProfileViewCache()
|
||||
|
||||
return useQuery(
|
||||
resolvedDidQueryOptions(agent, getUnstableProfile, didOrHandle),
|
||||
)
|
||||
}
|
||||
|
||||
export function precacheResolvedUri(
|
||||
|
||||
@@ -14,6 +14,61 @@ import {
|
||||
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
export type StructuredQueryKey<T extends Record<string, unknown>> = readonly [
|
||||
string,
|
||||
T,
|
||||
{
|
||||
persistedVersion?: number
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* Helper method to ensure consistent query keys and key ordering
|
||||
*/
|
||||
export function createQueryKey<T extends Record<string, unknown>>(
|
||||
/**
|
||||
* The query key root. All queries must have a root.
|
||||
*/
|
||||
root: string,
|
||||
/**
|
||||
* Any arguments the query depends on, and if changed, should result in the query being refetched.
|
||||
*/
|
||||
args: T,
|
||||
options: {
|
||||
/**
|
||||
* If provided, this indicates that the query is persisted and the version
|
||||
* of the persisted query format.
|
||||
*
|
||||
* This is used to ensure that when we make breaking changes to the
|
||||
* persisted query format, we can increment the version and avoid trying to
|
||||
* read old persisted queries with the new format.
|
||||
*
|
||||
* If you're persisting your queries, you probably want to set `gcTime:
|
||||
* GCTIME.INFINITY` for this query, otherwise it'll get busted immediately
|
||||
* after being persisted.
|
||||
*/
|
||||
persistedVersion?: number
|
||||
} = {},
|
||||
): StructuredQueryKey<T> {
|
||||
return [root, args, options] as const
|
||||
}
|
||||
|
||||
export function isQueryPersisted(
|
||||
queryKey: QueryKey,
|
||||
): queryKey is StructuredQueryKey<Record<string, unknown>> {
|
||||
return (
|
||||
Array.isArray(queryKey) &&
|
||||
queryKey.length === 3 &&
|
||||
typeof queryKey[0] === 'string' &&
|
||||
typeof queryKey[1] === 'object' &&
|
||||
queryKey[1] !== null &&
|
||||
typeof queryKey[2] === 'object' &&
|
||||
queryKey[2] !== null &&
|
||||
'persistedVersion' in queryKey[2] &&
|
||||
typeof queryKey[2].persistedVersion === 'number'
|
||||
)
|
||||
}
|
||||
|
||||
export async function truncateAndInvalidate<T = any>(
|
||||
queryClient: QueryClient,
|
||||
queryKey: QueryKey,
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
import {useEffect} from 'react'
|
||||
|
||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {useDialogStateContext} from '#/state/dialogs'
|
||||
import {useLightbox} from '#/state/lightbox'
|
||||
import {useModals} from '#/state/modals'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useIsDrawerOpen} from '#/state/shell/drawer-open'
|
||||
|
||||
/**
|
||||
* Based on {@link https://github.com/jaywcjlove/hotkeys-js/blob/b0038773f3b902574f22af747f3bb003a850f1da/src/index.js#L51C1-L64C2}
|
||||
*/
|
||||
function shouldIgnore(event: KeyboardEvent) {
|
||||
const target: any = event.target || event.srcElement
|
||||
if (!target) return false
|
||||
const {tagName} = target
|
||||
if (!tagName) return false
|
||||
const isInput =
|
||||
tagName === 'INPUT' &&
|
||||
![
|
||||
'checkbox',
|
||||
'radio',
|
||||
'range',
|
||||
'button',
|
||||
'file',
|
||||
'reset',
|
||||
'submit',
|
||||
'color',
|
||||
].includes(target.type)
|
||||
// ignore: isContentEditable === 'true', <input> and <textarea> when readOnly state is false, <select>
|
||||
if (
|
||||
target.isContentEditable ||
|
||||
((isInput || tagName === 'TEXTAREA' || tagName === 'SELECT') &&
|
||||
!target.readOnly)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function useComposerKeyboardShortcut() {
|
||||
const {openComposer} = useOpenComposer()
|
||||
const {openDialogs} = useDialogStateContext()
|
||||
const {isModalActive} = useModals()
|
||||
const {activeLightbox} = useLightbox()
|
||||
const isDrawerOpen = useIsDrawerOpen()
|
||||
const {hasSession} = useSession()
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasSession) {
|
||||
return
|
||||
}
|
||||
|
||||
function handler(event: KeyboardEvent) {
|
||||
if (shouldIgnore(event)) return
|
||||
if (
|
||||
openDialogs?.current.size > 0 ||
|
||||
isModalActive ||
|
||||
activeLightbox ||
|
||||
isDrawerOpen
|
||||
)
|
||||
return
|
||||
if (event.key === 'n' || event.key === 'N') {
|
||||
openComposer({logContext: 'Other'})
|
||||
}
|
||||
}
|
||||
document.addEventListener('keydown', handler)
|
||||
return () => document.removeEventListener('keydown', handler)
|
||||
}, [
|
||||
openComposer,
|
||||
isModalActive,
|
||||
openDialogs,
|
||||
activeLightbox,
|
||||
isDrawerOpen,
|
||||
hasSession,
|
||||
])
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import {createContext, useContext, useState} from 'react'
|
||||
|
||||
import {useHotkeysContext} from '#/lib/hotkeys'
|
||||
|
||||
type StateContext = boolean
|
||||
type SetContext = (v: boolean) => void
|
||||
|
||||
@@ -10,10 +12,22 @@ setContext.displayName = 'DrawerOpenSetContext'
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [state, setState] = useState(false)
|
||||
const {disableScope, enableScope} = useHotkeysContext()
|
||||
|
||||
const setDrawerOpen = (open: boolean) => {
|
||||
if (open) {
|
||||
disableScope('global')
|
||||
} else {
|
||||
enableScope('global')
|
||||
}
|
||||
setState(open)
|
||||
}
|
||||
|
||||
return (
|
||||
<stateContext.Provider value={state}>
|
||||
<setContext.Provider value={setState}>{children}</setContext.Provider>
|
||||
<setContext.Provider value={setDrawerOpen}>
|
||||
{children}
|
||||
</setContext.Provider>
|
||||
</stateContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ export function GifAltTextDialogLoaded({
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<Admonition type="tip" style={[a.mt_sm]}>
|
||||
<Admonition type="info" style={[a.mt_sm]}>
|
||||
<Trans>
|
||||
Alt text describes images for blind and low-vision users, and helps
|
||||
give context to everyone.
|
||||
|
||||
@@ -120,12 +120,14 @@ const GalleryInner = ({images, containerInfo, dispatch}: GalleryInnerProps) => {
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
<Admonition type="tip" style={[a.mt_sm]}>
|
||||
<Trans>
|
||||
Alt text describes images for blind and low-vision users, and helps
|
||||
give context to everyone.
|
||||
</Trans>
|
||||
</Admonition>
|
||||
{images.some(image => !image.alt) && (
|
||||
<Admonition type="info" style={[a.mt_sm]}>
|
||||
<Trans>
|
||||
Alt text describes images for blind and low-vision users, and helps
|
||||
give context to everyone.
|
||||
</Trans>
|
||||
</Admonition>
|
||||
)}
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
|
||||
@@ -8,21 +8,17 @@ import {
|
||||
import {
|
||||
type NativeSyntheticEvent,
|
||||
Text as RNText,
|
||||
TextInput as RNTextInput,
|
||||
type TextInputSelectionChangeEventData,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {type PasteEventPayload, TextInputWrapper} from 'expo-paste-input'
|
||||
import {AppBskyRichtextFacet, RichText} from '@atproto/api'
|
||||
import PasteInput, {
|
||||
type PastedFile,
|
||||
type PasteInputRef,
|
||||
// @ts-expect-error no types when installing from github
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
} from '@mattermost/react-native-paste-input'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {POST_IMG_MAX} from '#/lib/constants'
|
||||
import {downloadAndResize} from '#/lib/media/manip'
|
||||
import {isUriImage} from '#/lib/media/util'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {getMentionAt, insertMentionAt} from '#/lib/strings/mention-manip'
|
||||
import {useTheme} from '#/lib/ThemeContext'
|
||||
import {
|
||||
@@ -51,8 +47,9 @@ export function TextInput({
|
||||
onError,
|
||||
...props
|
||||
}: TextInputProps) {
|
||||
const {t: l} = useLingui()
|
||||
const {theme: t, fonts} = useAlf()
|
||||
const textInput = useRef<PasteInputRef>(null)
|
||||
const textInput = useRef<RNTextInput>(null)
|
||||
const textInputSelection = useRef<Selection>({start: 0, end: 0})
|
||||
const theme = useTheme()
|
||||
const [autocompletePrefix, setAutocompletePrefix] = useState('')
|
||||
@@ -129,19 +126,21 @@ export function TextInput({
|
||||
)
|
||||
|
||||
const onPaste = useCallback(
|
||||
async (err: string | undefined, files: PastedFile[]) => {
|
||||
if (err) {
|
||||
return onError(cleanError(err))
|
||||
(payload: PasteEventPayload) => {
|
||||
if (payload.type === 'unsupported') {
|
||||
onError(l`Unsupported clipboard content`)
|
||||
return
|
||||
}
|
||||
|
||||
const uris = files.map(f => f.uri)
|
||||
const uri = uris.find(isUriImage)
|
||||
|
||||
if (uri) {
|
||||
onPhotoPasted(uri)
|
||||
if (payload.type === 'images') {
|
||||
for (const uri of payload.uris) {
|
||||
if (isUriImage(uri)) {
|
||||
onPhotoPasted(uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[onError, onPhotoPasted],
|
||||
[l, onError, onPhotoPasted],
|
||||
)
|
||||
|
||||
const onSelectionChange = useCallback(
|
||||
@@ -217,41 +216,42 @@ export function TextInput({
|
||||
|
||||
return (
|
||||
<View style={[a.flex_1, a.pl_md, hasRightPadding && a.pr_4xl]}>
|
||||
<PasteInput
|
||||
testID="composerTextInput"
|
||||
ref={textInput}
|
||||
onChangeText={onChangeText}
|
||||
onPaste={onPaste}
|
||||
onSelectionChange={onSelectionChange}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor={t.atoms.text_contrast_low.color}
|
||||
keyboardAppearance={theme.colorScheme}
|
||||
autoFocus={props.autoFocus !== undefined ? props.autoFocus : true}
|
||||
allowFontScaling
|
||||
multiline
|
||||
scrollEnabled={false}
|
||||
numberOfLines={2}
|
||||
// Note: should be the default value, but as of v1.104
|
||||
// it switched to "none" on Android
|
||||
autoCapitalize="sentences"
|
||||
{...props}
|
||||
style={[
|
||||
inputTextStyle,
|
||||
a.w_full,
|
||||
!autocompletePrefix && a.h_full,
|
||||
{
|
||||
textAlignVertical: 'top',
|
||||
minHeight: 60,
|
||||
includeFontPadding: false,
|
||||
},
|
||||
{
|
||||
borderWidth: 1,
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
props.style,
|
||||
]}>
|
||||
{textDecorated}
|
||||
</PasteInput>
|
||||
<TextInputWrapper onPaste={onPaste}>
|
||||
<RNTextInput
|
||||
testID="composerTextInput"
|
||||
ref={textInput}
|
||||
onChangeText={onChangeText}
|
||||
onSelectionChange={onSelectionChange}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor={t.atoms.text_contrast_low.color}
|
||||
keyboardAppearance={theme.colorScheme}
|
||||
autoFocus={props.autoFocus !== undefined ? props.autoFocus : true}
|
||||
allowFontScaling
|
||||
multiline
|
||||
scrollEnabled={false}
|
||||
numberOfLines={2}
|
||||
// Note: should be the default value, but as of v1.104
|
||||
// it switched to "none" on Android
|
||||
autoCapitalize="sentences"
|
||||
{...props}
|
||||
style={[
|
||||
inputTextStyle,
|
||||
a.w_full,
|
||||
!autocompletePrefix && a.h_full,
|
||||
{
|
||||
textAlignVertical: 'top',
|
||||
minHeight: 60,
|
||||
includeFontPadding: false,
|
||||
},
|
||||
{
|
||||
borderWidth: 1,
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
props.style,
|
||||
]}>
|
||||
{textDecorated}
|
||||
</RNTextInput>
|
||||
</TextInputWrapper>
|
||||
<Autocomplete
|
||||
prefix={autocompletePrefix}
|
||||
onSelect={onSelectAutocompleteItem}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
|
||||
export const textInputWebEmitter = new EventEmitter()
|
||||
|
||||
@@ -582,7 +582,7 @@ function LightboxFooter({
|
||||
{altText ? (
|
||||
<View accessibilityRole="button" style={styles.footerText}>
|
||||
<Text
|
||||
style={[s.gray3]}
|
||||
style={{color: colors.gray3}}
|
||||
numberOfLines={isAltExpanded ? undefined : 3}
|
||||
selectable
|
||||
onPress={() => {
|
||||
|
||||
@@ -202,7 +202,7 @@ function ListItem({
|
||||
<View style={styles.listItemContent}>
|
||||
<Text
|
||||
type="lg"
|
||||
style={[s.bold, pal.text]}
|
||||
style={[{fontWeight: '600'}, pal.text]}
|
||||
numberOfLines={1}
|
||||
lineHeight={1.2}>
|
||||
{sanitizeDisplayName(list.name)}
|
||||
|
||||
@@ -272,7 +272,7 @@ let NotificationFeedItem = ({
|
||||
<HeartIconFilled
|
||||
size="xl"
|
||||
style={[
|
||||
s.likeColor,
|
||||
{color: t.palette.pink},
|
||||
// {position: 'relative', top: -4}
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
RQKEY,
|
||||
usePostFeedQuery,
|
||||
} from '#/state/queries/post-feed'
|
||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useProgressGuide} from '#/state/shell/progress-guide'
|
||||
import {useSelectedFeed} from '#/state/shell/selected-feed'
|
||||
@@ -700,13 +701,22 @@ let PostFeed = ({
|
||||
})
|
||||
setIsPTRing(true)
|
||||
try {
|
||||
await refetch()
|
||||
await truncateAndInvalidate(queryClient, RQKEY(feed, feedParams))
|
||||
onHasNew?.(false)
|
||||
} catch (err) {
|
||||
logger.error('Failed to refresh posts feed', {message: err})
|
||||
}
|
||||
setIsPTRing(false)
|
||||
}, [ax, refetch, setIsPTRing, onHasNew, feed, feedType, enabled])
|
||||
}, [
|
||||
ax,
|
||||
queryClient,
|
||||
setIsPTRing,
|
||||
onHasNew,
|
||||
feed,
|
||||
feedParams,
|
||||
feedType,
|
||||
enabled,
|
||||
])
|
||||
|
||||
const onEndReached = useCallback(async () => {
|
||||
if (isFetching || !hasNextPage || isError) return
|
||||
|
||||
@@ -60,7 +60,7 @@ export function PostLoadingPlaceholder({
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<View style={[s.flex1]}>
|
||||
<View style={[a.flex_1]}>
|
||||
<LoadingPlaceholder width={100} height={6} style={{marginBottom: 10}} />
|
||||
<LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} />
|
||||
<LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} />
|
||||
@@ -238,7 +238,7 @@ export function FeedLoadingPlaceholder({
|
||||
height={36}
|
||||
style={[styles.avatar, {borderRadius: 8}]}
|
||||
/>
|
||||
<View style={[s.flex1]}>
|
||||
<View style={[a.flex_1]}>
|
||||
<LoadingPlaceholder width={100} height={8} style={[s.mt5, s.mb10]} />
|
||||
<LoadingPlaceholder width={120} height={8} />
|
||||
</View>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
withSpring,
|
||||
} from 'react-native-reanimated'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
|
||||
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||
import {useMinimalShellMode} from '#/state/shell'
|
||||
|
||||
@@ -174,7 +174,7 @@ function NotifsView() {
|
||||
}
|
||||
return (
|
||||
<View style={s.p10}>
|
||||
<View style={s.flexRow}>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<Button onPress={triggerPush} label="Trigger Push" />
|
||||
<Button onPress={triggerToast} label="Trigger Toast" />
|
||||
<Button onPress={triggerToast2} label="Trigger Toast 2" />
|
||||
@@ -187,7 +187,7 @@ function PaletteView({palette}: {palette: PaletteColorName}) {
|
||||
const defaultPal = usePalette('default')
|
||||
const pal = usePalette(palette)
|
||||
return (
|
||||
<View style={[pal.view, pal.border, s.p10, s.mb5, s.border1]}>
|
||||
<View style={[pal.view, pal.border, s.p10, s.mb5, {borderWidth: 1}]}>
|
||||
<Text style={[pal.text]}>{palette} colors</Text>
|
||||
<Text style={[pal.textLight]}>Light text</Text>
|
||||
<Text style={[pal.link]}>Link text</Text>
|
||||
@@ -343,15 +343,15 @@ function ButtonsView() {
|
||||
const buttonStyles = {marginRight: 5}
|
||||
return (
|
||||
<View style={[defaultPal.view]}>
|
||||
<View style={[s.flexRow, s.mb5]}>
|
||||
<View style={[{flexDirection: 'row'}, s.mb5]}>
|
||||
<Button type="primary" label="Primary solid" style={buttonStyles} />
|
||||
<Button type="secondary" label="Secondary solid" style={buttonStyles} />
|
||||
</View>
|
||||
<View style={[s.flexRow, s.mb5]}>
|
||||
<View style={[{flexDirection: 'row'}, s.mb5]}>
|
||||
<Button type="default" label="Default solid" style={buttonStyles} />
|
||||
<Button type="inverted" label="Inverted solid" style={buttonStyles} />
|
||||
</View>
|
||||
<View style={s.flexRow}>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<Button
|
||||
type="primary-outline"
|
||||
label="Primary outline"
|
||||
@@ -363,7 +363,7 @@ function ButtonsView() {
|
||||
style={buttonStyles}
|
||||
/>
|
||||
</View>
|
||||
<View style={s.flexRow}>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<Button
|
||||
type="primary-light"
|
||||
label="Primary light"
|
||||
@@ -375,7 +375,7 @@ function ButtonsView() {
|
||||
style={buttonStyles}
|
||||
/>
|
||||
</View>
|
||||
<View style={s.flexRow}>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<Button
|
||||
type="default-light"
|
||||
label="Default light"
|
||||
|
||||
@@ -400,10 +400,16 @@ function Btn({
|
||||
a.rounded_full,
|
||||
{backgroundColor: t.palette.primary_500},
|
||||
]}>
|
||||
<Text style={styles.notificationCountLabel}>{notificationCount}</Text>
|
||||
<Text
|
||||
style={styles.notificationCountLabel}
|
||||
maxFontSizeMultiplier={1.5}>
|
||||
{notificationCount}
|
||||
</Text>
|
||||
</View>
|
||||
) : hasNew ? (
|
||||
<View style={[styles.hasNewBadge, a.rounded_full]} />
|
||||
<View
|
||||
style={[styles.hasNewBadge, {backgroundColor: t.palette.primary_500}]}
|
||||
/>
|
||||
) : null}
|
||||
</PressableScale>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {StyleSheet} from 'react-native'
|
||||
|
||||
import {colors} from '#/lib/styles'
|
||||
import {atoms as a} from '#/alf'
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
@@ -24,8 +23,9 @@ export const styles = StyleSheet.create({
|
||||
position: 'absolute',
|
||||
left: '52%',
|
||||
top: 8,
|
||||
paddingHorizontal: 4,
|
||||
paddingBottom: 1,
|
||||
paddingHorizontal: 5,
|
||||
paddingTop: 1,
|
||||
paddingBottom: 2,
|
||||
borderRadius: 6,
|
||||
zIndex: 1,
|
||||
},
|
||||
@@ -37,8 +37,9 @@ export const styles = StyleSheet.create({
|
||||
notificationCountLabel: {
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
color: colors.white,
|
||||
color: 'white',
|
||||
fontVariant: ['tabular-nums'],
|
||||
includeFontPadding: false,
|
||||
},
|
||||
hasNewBadge: {
|
||||
position: 'absolute',
|
||||
@@ -47,8 +48,7 @@ export const styles = StyleSheet.create({
|
||||
top: 10,
|
||||
width: 8,
|
||||
height: 8,
|
||||
backgroundColor: colors.blue3,
|
||||
borderRadius: 6,
|
||||
borderRadius: 4,
|
||||
zIndex: 1,
|
||||
},
|
||||
ctrlIcon: {
|
||||
|
||||
@@ -313,7 +313,9 @@ const NavItem: React.FC<{
|
||||
<Text style={styles.notificationCountLabel}>{notificationCount}</Text>
|
||||
</View>
|
||||
) : hasNew ? (
|
||||
<View style={styles.hasNewBadge} />
|
||||
<View
|
||||
style={[styles.hasNewBadge, {backgroundColor: t.palette.primary_500}]}
|
||||
/>
|
||||
) : null}
|
||||
</Link>
|
||||
)
|
||||
|
||||
@@ -580,7 +580,7 @@ function ComposeBtn() {
|
||||
style={[a.rounded_full]}>
|
||||
<ButtonIcon icon={EditBig} position="left" />
|
||||
<ButtonText>
|
||||
<Trans context="action">New Post</Trans>
|
||||
<Trans context="action">New post</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
@@ -3,7 +3,7 @@ import {View} from 'react-native'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/core'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
|
||||
import {useKawaiiMode} from '#/state/preferences/kawaii'
|
||||
|
||||
@@ -9,7 +9,6 @@ import {useIntentHandler} from '#/lib/hooks/useIntentHandler'
|
||||
import {type NavigationProp} from '#/lib/routes/types'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell'
|
||||
import {useComposerKeyboardShortcut} from '#/state/shell/composer/useComposerKeyboardShortcut'
|
||||
import {useCloseAllActiveElements} from '#/state/util'
|
||||
import {Lightbox} from '#/view/com/lightbox/Lightbox'
|
||||
import {ModalsContainer} from '#/view/com/modals/Modal'
|
||||
@@ -36,7 +35,7 @@ import {NoAccessScreen} from '#/ageAssurance/components/NoAccessScreen'
|
||||
import {RedirectOverlay} from '#/ageAssurance/components/RedirectOverlay'
|
||||
import {PassiveAnalytics} from '#/analytics/PassiveAnalytics'
|
||||
import {FlatNavigator, RoutesContainer} from '#/Navigation'
|
||||
import {Composer} from './Composer.web'
|
||||
import {Composer} from './Composer'
|
||||
import {DrawerContent} from './Drawer'
|
||||
|
||||
function ShellInner() {
|
||||
@@ -45,7 +44,6 @@ function ShellInner() {
|
||||
const {state: policyUpdateState} = usePolicyUpdateContext()
|
||||
const welcomeModalControl = useWelcomeModal()
|
||||
|
||||
useComposerKeyboardShortcut()
|
||||
useIntentHandler()
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
"@jridgewell/gen-mapping" "^0.3.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@atproto/api@^0.19.3":
|
||||
version "0.19.6"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.19.6.tgz#c8fae3d792fe429c900ac0ba2609d60b9a89e28b"
|
||||
integrity sha512-8L5dZvGaclB52b8msjtgDNx3uLWUY4PELA7KbFyAWBFVasCceE1txdrscCqDCLN+Fff9+Sm07OIjnjHYJXdETA==
|
||||
"@atproto/api@^0.19.5":
|
||||
version "0.19.5"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.19.5.tgz#6388e5d6d3a1693fe04b5f37c705682bac8601d3"
|
||||
integrity sha512-u6R5TecYJDO8l8QFN09AMuJASYnUkJ4HhYE5hg4/dha/z14a+OAil2/dli/208uM5AHPFLtlnB8kIK9XU5GgQQ==
|
||||
dependencies:
|
||||
"@atproto/common-web" "^0.4.19"
|
||||
"@atproto/lexicon" "^0.6.2"
|
||||
"@atproto/syntax" "^0.5.3"
|
||||
"@atproto/syntax" "^0.5.2"
|
||||
"@atproto/xrpc" "^0.7.7"
|
||||
await-lock "^2.2.2"
|
||||
multiformats "^9.9.0"
|
||||
@@ -73,20 +73,13 @@
|
||||
multiformats "^9.9.0"
|
||||
zod "^3.23.8"
|
||||
|
||||
"@atproto/syntax@^0.5.0", "@atproto/syntax@^0.5.1":
|
||||
"@atproto/syntax@^0.5.0", "@atproto/syntax@^0.5.1", "@atproto/syntax@^0.5.2":
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.5.2.tgz#d4b32c9feb421ceeb5ade1fa80bc42764d51e52e"
|
||||
integrity sha512-W41szOnkppoHr0iCUrzL8gy3OD6qmDyp1UvUgmTx2oFQfgbudpz51T/gznesiCcqiUT5obfHdx4PJ+WdlEOE7Q==
|
||||
dependencies:
|
||||
tslib "^2.8.1"
|
||||
|
||||
"@atproto/syntax@^0.5.3":
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.5.3.tgz#4331d01f63fe56c374dcf95d4432a22b62271a17"
|
||||
integrity sha512-gzhlHOJHm5KXdCc17fXi1fXM81ccs5jJfNgCui84ay9JGvczxegpYHNqdMlv+iBuhtBzFIjgx6ChjRxN/kO8kQ==
|
||||
dependencies:
|
||||
tslib "^2.8.1"
|
||||
|
||||
"@atproto/xrpc@^0.7.7":
|
||||
version "0.7.7"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.7.7.tgz#c0e3106c854cb9bc7d3129de2f31b8256eb0ed11"
|
||||
@@ -304,15 +297,6 @@
|
||||
json5 "^2.2.3"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/eslint-parser@^7.25.1":
|
||||
version "7.28.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.28.6.tgz#6a294a4add732ebe7ded8a8d2792dd03dd81dc3f"
|
||||
integrity sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==
|
||||
dependencies:
|
||||
"@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1"
|
||||
eslint-visitor-keys "^2.1.0"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/generator@^7.20.5":
|
||||
version "7.23.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e"
|
||||
@@ -2622,14 +2606,14 @@
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz#9bdad8176be7811ad148d1f8772359041f46c6c5"
|
||||
integrity sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==
|
||||
|
||||
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1":
|
||||
"@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1":
|
||||
version "4.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595"
|
||||
integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==
|
||||
dependencies:
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
|
||||
"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.12.2":
|
||||
"@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.12.2":
|
||||
version "4.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b"
|
||||
integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==
|
||||
@@ -3920,12 +3904,6 @@
|
||||
"@babel/runtime" "^7.20.13"
|
||||
"@lingui/core" "5.9.2"
|
||||
|
||||
"@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.3"
|
||||
|
||||
"@messageformat/parser@^5.0.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.1.0.tgz#05e4851c782d633ad735791dd0a68ee65d2a7201"
|
||||
@@ -3955,13 +3933,6 @@
|
||||
"@emnapi/runtime" "^1.4.3"
|
||||
"@tybys/wasm-util" "^0.10.0"
|
||||
|
||||
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
|
||||
version "5.1.1-v1"
|
||||
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"
|
||||
integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==
|
||||
dependencies:
|
||||
eslint-scope "5.1.1"
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
@@ -4800,29 +4771,6 @@
|
||||
serve-static "^1.16.2"
|
||||
ws "^6.2.3"
|
||||
|
||||
"@react-native/eslint-config@^0.81.5":
|
||||
version "0.81.6"
|
||||
resolved "https://registry.yarnpkg.com/@react-native/eslint-config/-/eslint-config-0.81.6.tgz#6911df60f250f6462b3b7361190d276bb09c0432"
|
||||
integrity sha512-Uur91Ss9L8W7omBAqnNe80GNUNn5qQXYuP0xmiKmytGk0ttG9QkcOma4aS4nR3m8O7v0kFA7U3+AD5an526Jsg==
|
||||
dependencies:
|
||||
"@babel/core" "^7.25.2"
|
||||
"@babel/eslint-parser" "^7.25.1"
|
||||
"@react-native/eslint-plugin" "0.81.6"
|
||||
"@typescript-eslint/eslint-plugin" "^7.1.1"
|
||||
"@typescript-eslint/parser" "^7.1.1"
|
||||
eslint-config-prettier "^8.5.0"
|
||||
eslint-plugin-eslint-comments "^3.2.0"
|
||||
eslint-plugin-ft-flow "^2.0.1"
|
||||
eslint-plugin-jest "^27.9.0"
|
||||
eslint-plugin-react "^7.30.1"
|
||||
eslint-plugin-react-hooks "^5.2.0"
|
||||
eslint-plugin-react-native "^4.0.0"
|
||||
|
||||
"@react-native/eslint-plugin@0.81.6":
|
||||
version "0.81.6"
|
||||
resolved "https://registry.yarnpkg.com/@react-native/eslint-plugin/-/eslint-plugin-0.81.6.tgz#9c3831a1a3a1204f016ce53b68e99e9269c0115f"
|
||||
integrity sha512-2wLmnq6l2dualDoAj42P4miwReXt5i7rad31zenhYFBSUNt849y2sroP2I6Yjh0r+UqBdDlf6QN2W+Pdmi6pJw==
|
||||
|
||||
"@react-native/gradle-plugin@0.81.5":
|
||||
version "0.81.5"
|
||||
resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.81.5.tgz#a58830f38789f6254b64449a17fe57455b589d00"
|
||||
@@ -5164,7 +5112,7 @@
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^3.0.0"
|
||||
|
||||
"@tanstack/query-async-storage-persister@^5.25.0":
|
||||
"@tanstack/query-async-storage-persister@^5.96.2":
|
||||
version "5.96.2"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-async-storage-persister/-/query-async-storage-persister-5.96.2.tgz#29423b35f2d8c5f63afbf72475baa7799ffaf022"
|
||||
integrity sha512-lYJm+TwzOEUVkxCJapLSzRXPzmPpv7Vy3zSB1RXYQ6+vznEgXBqLjn+ZwBRvHpkRda9VXis64wv44rPIi9nCwg==
|
||||
@@ -5172,11 +5120,6 @@
|
||||
"@tanstack/query-core" "5.96.2"
|
||||
"@tanstack/query-persist-client-core" "5.96.2"
|
||||
|
||||
"@tanstack/query-core@5.25.0":
|
||||
version "5.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.25.0.tgz#e08ed0a9fad34c8005d1a282e57280031ac50cdc"
|
||||
integrity sha512-vlobHP64HTuSE68lWF1mEhwSRC5Q7gaT+a/m9S+ItuN+ruSOxe1rFnR9j0ACWQ314BPhBEVKfBQ6mHL0OWfdbQ==
|
||||
|
||||
"@tanstack/query-core@5.96.2":
|
||||
version "5.96.2"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.96.2.tgz#766dab253476afd0b27959b66abb606d8d2dd9f5"
|
||||
@@ -5189,19 +5132,19 @@
|
||||
dependencies:
|
||||
"@tanstack/query-core" "5.96.2"
|
||||
|
||||
"@tanstack/react-query-persist-client@^5.25.0":
|
||||
"@tanstack/react-query-persist-client@^5.96.2":
|
||||
version "5.96.2"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query-persist-client/-/react-query-persist-client-5.96.2.tgz#b47d62fc990a9fd38ddcf4a080d1300ae887e5a0"
|
||||
integrity sha512-smQ38oVPlnvkG+G7R60IAD9X6azJLRjHEd7twml9XBLYM31ncPDP0tUKy/Gv/4ItVmKTtjZ5VabXpVZxnaWSww==
|
||||
dependencies:
|
||||
"@tanstack/query-persist-client-core" "5.96.2"
|
||||
|
||||
"@tanstack/react-query@5.25.0":
|
||||
version "5.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.25.0.tgz#f4dac794cf10dd956aa56dbbdf67049a5ba2669d"
|
||||
integrity sha512-u+n5R7mLO7RmeiIonpaCRVXNRWtZEef/aVZ/XGWRPa7trBIvGtzlfo0Ah7ZtnTYfrKEVwnZ/tzRCBcoiqJ/tFw==
|
||||
"@tanstack/react-query@^5.96.2":
|
||||
version "5.96.2"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.96.2.tgz#a164abfb80eb5e7772bbcddfa7240f3fd8d0d7be"
|
||||
integrity sha512-sYyzzJT4G0g02azzJ8o55VFFV31XvFpdUpG+unxS0vSaYsJnSPKGoI6WdPwUucJL1wpgGfwfmntNX/Ub1uOViA==
|
||||
dependencies:
|
||||
"@tanstack/query-core" "5.25.0"
|
||||
"@tanstack/query-core" "5.96.2"
|
||||
|
||||
"@testing-library/react-native@^13.2.0":
|
||||
version "13.2.0"
|
||||
@@ -5615,11 +5558,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
||||
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
|
||||
|
||||
"@types/semver@^7.3.12":
|
||||
version "7.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528"
|
||||
integrity sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==
|
||||
|
||||
"@types/send@*":
|
||||
version "0.17.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.1.tgz#ed4932b8a2a805f1fe362a70f4e62d0ac994e301"
|
||||
@@ -5699,21 +5637,6 @@
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^7.1.1":
|
||||
version "7.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz#b16d3cf3ee76bf572fdf511e79c248bdec619ea3"
|
||||
integrity sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.10.0"
|
||||
"@typescript-eslint/scope-manager" "7.18.0"
|
||||
"@typescript-eslint/type-utils" "7.18.0"
|
||||
"@typescript-eslint/utils" "7.18.0"
|
||||
"@typescript-eslint/visitor-keys" "7.18.0"
|
||||
graphemer "^1.4.0"
|
||||
ignore "^5.3.1"
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/parser@8.58.0":
|
||||
version "8.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.58.0.tgz#da04ece1967b6c2fe8f10c3473dabf3825795ef7"
|
||||
@@ -5725,17 +5648,6 @@
|
||||
"@typescript-eslint/visitor-keys" "8.58.0"
|
||||
debug "^4.4.3"
|
||||
|
||||
"@typescript-eslint/parser@^7.1.1":
|
||||
version "7.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.18.0.tgz#83928d0f1b7f4afa974098c64b5ce6f9051f96a0"
|
||||
integrity sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "7.18.0"
|
||||
"@typescript-eslint/types" "7.18.0"
|
||||
"@typescript-eslint/typescript-estree" "7.18.0"
|
||||
"@typescript-eslint/visitor-keys" "7.18.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/project-service@8.58.0":
|
||||
version "8.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.58.0.tgz#66ceda0aabf7427aec3e2713fa43eb278dead2aa"
|
||||
@@ -5745,22 +5657,6 @@
|
||||
"@typescript-eslint/types" "^8.58.0"
|
||||
debug "^4.4.3"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c"
|
||||
integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
"@typescript-eslint/visitor-keys" "5.62.0"
|
||||
|
||||
"@typescript-eslint/scope-manager@7.18.0":
|
||||
version "7.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz#c928e7a9fc2c0b3ed92ab3112c614d6bd9951c83"
|
||||
integrity sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "7.18.0"
|
||||
"@typescript-eslint/visitor-keys" "7.18.0"
|
||||
|
||||
"@typescript-eslint/scope-manager@8.58.0":
|
||||
version "8.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz#e304142775e49a1b7ac3c8bf2536714447c72cab"
|
||||
@@ -5774,16 +5670,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz#c5a8edb21f31e0fdee565724e1b984171c559482"
|
||||
integrity sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==
|
||||
|
||||
"@typescript-eslint/type-utils@7.18.0":
|
||||
version "7.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz#2165ffaee00b1fbbdd2d40aa85232dab6998f53b"
|
||||
integrity sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "7.18.0"
|
||||
"@typescript-eslint/utils" "7.18.0"
|
||||
debug "^4.3.4"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/type-utils@8.58.0":
|
||||
version "8.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz#ce0e72cd967ffbbe8de322db6089bd4374be352f"
|
||||
@@ -5795,48 +5681,11 @@
|
||||
debug "^4.4.3"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/types@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
|
||||
integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
|
||||
|
||||
"@typescript-eslint/types@7.18.0":
|
||||
version "7.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.18.0.tgz#b90a57ccdea71797ffffa0321e744f379ec838c9"
|
||||
integrity sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==
|
||||
|
||||
"@typescript-eslint/types@8.58.0", "@typescript-eslint/types@^8.56.0", "@typescript-eslint/types@^8.58.0":
|
||||
version "8.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.58.0.tgz#e94ae7abdc1c6530e71183c1007b61fa93112a5a"
|
||||
integrity sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b"
|
||||
integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
"@typescript-eslint/visitor-keys" "5.62.0"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@7.18.0":
|
||||
version "7.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz#b5868d486c51ce8f312309ba79bdb9f331b37931"
|
||||
integrity sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "7.18.0"
|
||||
"@typescript-eslint/visitor-keys" "7.18.0"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
minimatch "^9.0.4"
|
||||
semver "^7.6.0"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@8.58.0":
|
||||
version "8.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz#ed233faa8e2f2a2e1357c3e7d553d6465a0ee59a"
|
||||
@@ -5852,17 +5701,7 @@
|
||||
tinyglobby "^0.2.15"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/utils@7.18.0":
|
||||
version "7.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.18.0.tgz#bca01cde77f95fc6a8d5b0dbcbfb3d6ca4be451f"
|
||||
integrity sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
"@typescript-eslint/scope-manager" "7.18.0"
|
||||
"@typescript-eslint/types" "7.18.0"
|
||||
"@typescript-eslint/typescript-estree" "7.18.0"
|
||||
|
||||
"@typescript-eslint/utils@8.58.0", "@typescript-eslint/utils@^8.0.0":
|
||||
"@typescript-eslint/utils@8.58.0", "@typescript-eslint/utils@^8.57.2":
|
||||
version "8.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.58.0.tgz#21a74a7963b0d288b719a4121c7dd555adaab3c3"
|
||||
integrity sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==
|
||||
@@ -5872,36 +5711,6 @@
|
||||
"@typescript-eslint/types" "8.58.0"
|
||||
"@typescript-eslint/typescript-estree" "8.58.0"
|
||||
|
||||
"@typescript-eslint/utils@^5.10.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86"
|
||||
integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.2.0"
|
||||
"@types/json-schema" "^7.0.9"
|
||||
"@types/semver" "^7.3.12"
|
||||
"@typescript-eslint/scope-manager" "5.62.0"
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
"@typescript-eslint/typescript-estree" "5.62.0"
|
||||
eslint-scope "^5.1.1"
|
||||
semver "^7.3.7"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e"
|
||||
integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@7.18.0":
|
||||
version "7.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz#0564629b6124d67607378d0f0332a0495b25e7d7"
|
||||
integrity sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "7.18.0"
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
|
||||
"@typescript-eslint/visitor-keys@8.58.0":
|
||||
version "8.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz#2abd55a4be70fd55967aceaba4330b9ba9f45189"
|
||||
@@ -6486,11 +6295,6 @@ array-union@^1.0.1:
|
||||
dependencies:
|
||||
array-uniq "^1.0.1"
|
||||
|
||||
array-union@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
|
||||
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
|
||||
|
||||
array-union@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/array-union/-/array-union-3.0.1.tgz#da52630d327f8b88cfbfb57728e2af5cd9b6b975"
|
||||
@@ -8739,11 +8543,6 @@ escodegen@^2.0.0:
|
||||
optionalDependencies:
|
||||
source-map "~0.6.1"
|
||||
|
||||
eslint-config-prettier@^8.5.0:
|
||||
version "8.10.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.2.tgz#0642e53625ebc62c31c24726b0f050df6bd97a2e"
|
||||
integrity sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==
|
||||
|
||||
eslint-import-context@^0.1.8, eslint-import-context@^0.1.9:
|
||||
version "0.1.9"
|
||||
resolved "https://registry.yarnpkg.com/eslint-import-context/-/eslint-import-context-0.1.9.tgz#967b0b2f0a90ef4b689125e088f790f0b7756dbe"
|
||||
@@ -8769,23 +8568,7 @@ eslint-import-resolver-typescript@^4.4.4:
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
eslint-plugin-eslint-comments@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz#9e1cd7b4413526abb313933071d7aba05ca12ffa"
|
||||
integrity sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==
|
||||
dependencies:
|
||||
escape-string-regexp "^1.0.5"
|
||||
ignore "^5.0.5"
|
||||
|
||||
eslint-plugin-ft-flow@^2.0.1:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-ft-flow/-/eslint-plugin-ft-flow-2.0.3.tgz#3b3c113c41902bcbacf0e22b536debcfc3c819e8"
|
||||
integrity sha512-Vbsd/b+LYA99jUbsL6viEUWShFaYQt2YQs3QN3f+aeszOhh2sgdcU0mjzDyD4yyBvMc8qy2uwvBBWfMzEX06tg==
|
||||
dependencies:
|
||||
lodash "^4.17.21"
|
||||
string-natural-compare "^3.0.1"
|
||||
|
||||
eslint-plugin-import-x@^4.16.1:
|
||||
eslint-plugin-import-x@^4.16.2:
|
||||
version "4.16.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.2.tgz#95d1f798795566712c87897317ef8433d101db29"
|
||||
integrity sha512-rM9K8UBHcWKpzQzStn1YRN2T5NvdeIfSVoKu/lKF41znQXHAUcBbYXe5wd6GNjZjTrP7viQ49n1D83x/2gYgIw==
|
||||
@@ -8801,20 +8584,13 @@ eslint-plugin-import-x@^4.16.1:
|
||||
stable-hash-x "^0.2.0"
|
||||
unrs-resolver "^1.9.2"
|
||||
|
||||
eslint-plugin-jest@^27.9.0:
|
||||
version "27.9.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz#7c98a33605e1d8b8442ace092b60e9919730000b"
|
||||
integrity sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==
|
||||
eslint-plugin-lingui@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-lingui/-/eslint-plugin-lingui-0.12.0.tgz#fca175ad3ed40538483bfcd50c091851fefc3e8b"
|
||||
integrity sha512-2+9P3thudGIBI10sDWYUIrGs3HIP09gv0XF98RDzZs34GAsAsGTUoSgrttKS1knAU5dwbrFKhNKu2LMrjRECag==
|
||||
dependencies:
|
||||
"@typescript-eslint/utils" "^5.10.0"
|
||||
|
||||
eslint-plugin-lingui@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-lingui/-/eslint-plugin-lingui-0.11.0.tgz#e33a4fe83698bb4cdfbfa816391fb79b68e6c026"
|
||||
integrity sha512-O2Ixoapt5fa4VKZJgXhVwb6BHnzByIUDNMfZOhHWGMYk40GfGCho4MUfspLVrHAFLimgBPKXtCcJ8GC4YNZmfg==
|
||||
dependencies:
|
||||
"@typescript-eslint/utils" "^8.0.0"
|
||||
micromatch "^4.0.0"
|
||||
"@typescript-eslint/utils" "^8.57.2"
|
||||
micromatch "^4.0.8"
|
||||
|
||||
eslint-plugin-react-compiler@^19.1.0-rc.2:
|
||||
version "19.1.0-rc.2"
|
||||
@@ -8828,11 +8604,6 @@ eslint-plugin-react-compiler@^19.1.0-rc.2:
|
||||
zod "^3.22.4"
|
||||
zod-validation-error "^3.0.3"
|
||||
|
||||
eslint-plugin-react-hooks@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz#1be0080901e6ac31ce7971beed3d3ec0a423d9e3"
|
||||
integrity sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==
|
||||
|
||||
eslint-plugin-react-hooks@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz#66e258db58ece50723ef20cc159f8aa908219169"
|
||||
@@ -8858,13 +8629,6 @@ eslint-plugin-react-native-globals@^0.1.1:
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz#ee1348bc2ceb912303ce6bdbd22e2f045ea86ea2"
|
||||
integrity sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==
|
||||
|
||||
eslint-plugin-react-native@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-native/-/eslint-plugin-react-native-4.1.0.tgz#5343acd3b2246bc1b857ac38be708f070d18809f"
|
||||
integrity sha512-QLo7rzTBOl43FvVqDdq5Ql9IoElIuTdjrz9SKAXCvULvBoRZ44JGSkx9z4999ZusCsb4rK3gjS8gOGyeYqZv2Q==
|
||||
dependencies:
|
||||
eslint-plugin-react-native-globals "^0.1.1"
|
||||
|
||||
eslint-plugin-react-native@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-native/-/eslint-plugin-react-native-5.0.0.tgz#2ee990ba4967c557183b31121578547fb5c02d5d"
|
||||
@@ -8872,7 +8636,7 @@ eslint-plugin-react-native@^5.0.0:
|
||||
dependencies:
|
||||
eslint-plugin-react-native-globals "^0.1.1"
|
||||
|
||||
eslint-plugin-react@^7.30.1, eslint-plugin-react@^7.37.5:
|
||||
eslint-plugin-react@^7.37.5:
|
||||
version "7.37.5"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz#2975511472bdda1b272b34d779335c9b0e877065"
|
||||
integrity sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==
|
||||
@@ -8901,7 +8665,7 @@ eslint-plugin-simple-import-sort@^12.1.1:
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz#e64bfdaf91c5b98a298619aa634a9f7aa43b709e"
|
||||
integrity sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==
|
||||
|
||||
eslint-scope@5.1.1, eslint-scope@^5.1.1:
|
||||
eslint-scope@5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
|
||||
integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
|
||||
@@ -8917,12 +8681,7 @@ eslint-scope@^8.4.0:
|
||||
esrecurse "^4.3.0"
|
||||
estraverse "^5.2.0"
|
||||
|
||||
eslint-visitor-keys@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
|
||||
integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
|
||||
|
||||
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3:
|
||||
eslint-visitor-keys@^3.4.3:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
|
||||
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
||||
@@ -9332,6 +9091,11 @@ expo-notifications@~0.32.16:
|
||||
expo-application "~7.0.8"
|
||||
expo-constants "~18.0.13"
|
||||
|
||||
expo-paste-input@^0.1.12:
|
||||
version "0.1.12"
|
||||
resolved "https://registry.yarnpkg.com/expo-paste-input/-/expo-paste-input-0.1.12.tgz#0295eb26caf738fc39019f4ecae48c22b126b67e"
|
||||
integrity sha512-iaOCgygmCYVbSj+gJOxr8P28y8Tf0X4UHtVVXRjEsARKf5gQUoVaRX63uzB+8h+g6vN710fSmu5vdxNP28bw8g==
|
||||
|
||||
expo-privacy-sensitive@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/expo-privacy-sensitive/-/expo-privacy-sensitive-0.1.0.tgz#2177d7a3cb8ed352df94c5806d012dfb7b48bc84"
|
||||
@@ -9512,17 +9276,6 @@ fast-glob@^3.2.7:
|
||||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.4"
|
||||
|
||||
fast-glob@^3.2.9:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
|
||||
integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
|
||||
dependencies:
|
||||
"@nodelib/fs.stat" "^2.0.2"
|
||||
"@nodelib/fs.walk" "^1.2.3"
|
||||
glob-parent "^5.1.2"
|
||||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.8"
|
||||
|
||||
fast-glob@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
|
||||
@@ -10080,18 +9833,6 @@ globalthis@^1.0.4:
|
||||
define-properties "^1.2.1"
|
||||
gopd "^1.0.1"
|
||||
|
||||
globby@^11.1.0:
|
||||
version "11.1.0"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
|
||||
integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
|
||||
dependencies:
|
||||
array-union "^2.1.0"
|
||||
dir-glob "^3.0.1"
|
||||
fast-glob "^3.2.9"
|
||||
ignore "^5.2.0"
|
||||
merge2 "^1.4.1"
|
||||
slash "^3.0.0"
|
||||
|
||||
globby@^12.0.2:
|
||||
version "12.2.0"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-12.2.0.tgz#2ab8046b4fba4ff6eede835b29f678f90e3d3c22"
|
||||
@@ -10132,11 +9873,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0,
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
|
||||
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
|
||||
|
||||
graphemer@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
|
||||
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
|
||||
|
||||
gzip-size@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
|
||||
@@ -10487,11 +10223,6 @@ ieee754@^1.1.13:
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
||||
ignore@^5.0.5:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
|
||||
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
|
||||
|
||||
ignore@^5.1.9, ignore@^5.2.0:
|
||||
version "5.2.4"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
|
||||
@@ -12501,7 +12232,7 @@ micromatch@4.0.5, micromatch@^4.0.2, micromatch@^4.0.4:
|
||||
braces "^3.0.2"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
micromatch@^4.0.0, micromatch@^4.0.7, micromatch@^4.0.8:
|
||||
micromatch@^4.0.7, micromatch@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
|
||||
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
|
||||
@@ -14130,6 +13861,11 @@ react-freeze@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/react-freeze/-/react-freeze-1.0.3.tgz#5e3ca90e682fed1d73a7cb50c2c7402b3e85618d"
|
||||
integrity sha512-ZnXwLQnGzrDpHBHiC56TXFXvmolPeMjTn1UOm610M4EXGzbEDR7oOIyS2ZiItgbs6eZc4oU/a0hpk8PrcKvv5g==
|
||||
|
||||
react-hotkeys-hook@5.2.4:
|
||||
version "5.2.4"
|
||||
resolved "https://registry.yarnpkg.com/react-hotkeys-hook/-/react-hotkeys-hook-5.2.4.tgz#45ad54d78823b2a929963d482aff98efad0530f2"
|
||||
integrity sha512-BgKg+A1+TawkYluh5Bo4cTmcgMN5L29uhJbDUQdHwPX+qgXRjIPYU5kIDHyxnAwCkCBiu9V5OpB2mpyeluVF2A==
|
||||
|
||||
react-image-crop@^11.0.7:
|
||||
version "11.0.7"
|
||||
resolved "https://registry.yarnpkg.com/react-image-crop/-/react-image-crop-11.0.7.tgz#25f3d37ccbb65a05d19d23b4740a5912835c741e"
|
||||
@@ -14932,16 +14668,16 @@ selfsigned@^2.1.1:
|
||||
dependencies:
|
||||
node-forge "^1"
|
||||
|
||||
semver@7.6.3, semver@^7.1.3:
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
|
||||
semver@^6.0.0, semver@^6.3.0, semver@^6.3.1:
|
||||
version "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:
|
||||
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.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"
|
||||
@@ -14949,11 +14685,6 @@ semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@~7.5.4:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^7.3.7:
|
||||
version "7.7.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a"
|
||||
integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==
|
||||
|
||||
semver@^7.6.0:
|
||||
version "7.6.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
|
||||
@@ -15507,11 +15238,6 @@ string-length@^5.0.1:
|
||||
char-regex "^2.0.0"
|
||||
strip-ansi "^7.0.1"
|
||||
|
||||
string-natural-compare@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
||||
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
@@ -16022,11 +15748,6 @@ tr46@~0.0.3:
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
|
||||
|
||||
ts-api-utils@^1.3.0:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064"
|
||||
integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==
|
||||
|
||||
ts-api-utils@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1"
|
||||
@@ -16042,11 +15763,6 @@ ts-plugin-sort-import-suggestions@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/ts-plugin-sort-import-suggestions/-/ts-plugin-sort-import-suggestions-1.0.4.tgz#d1ed6c235feb8c8bb8b34c625ea75b46e3e62925"
|
||||
integrity sha512-85n5lm2OQQ+b7aRNK9omU1gmjMNXRsgeLwojm5u4OSY5sVBkAHTcgMQPEeHMNlyyfFW0uXnwgqAU0pNfhD96Bw==
|
||||
|
||||
tslib@^1.8.1:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
|
||||
@@ -16057,13 +15773,6 @@ tslib@^2.8.0, tslib@^2.8.1:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||
|
||||
tsutils@^3.21.0:
|
||||
version "3.21.0"
|
||||
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
|
||||
integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
|
||||
dependencies:
|
||||
tslib "^1.8.1"
|
||||
|
||||
type-check@^0.4.0, type-check@~0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
|
||||
@@ -16188,7 +15897,7 @@ typed-array-length@^1.0.7:
|
||||
possible-typed-array-names "^1.0.0"
|
||||
reflect.getprototypeof "^1.0.6"
|
||||
|
||||
typescript-eslint@^8.57.2:
|
||||
typescript-eslint@^8.58.0:
|
||||
version "8.58.0"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.58.0.tgz#5758b1b68ae7ec05d756b98c63a1f6953a01172b"
|
||||
integrity sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==
|
||||
|
||||
Reference in New Issue
Block a user