Merge branch 'main' into app-1934
This commit is contained in:
+4
-4
@@ -109,7 +109,6 @@
|
|||||||
"@ipld/dag-cbor": "^9.2.0",
|
"@ipld/dag-cbor": "^9.2.0",
|
||||||
"@lingui/core": "^5.9.2",
|
"@lingui/core": "^5.9.2",
|
||||||
"@lingui/react": "^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",
|
"@miblanchard/react-native-slider": "^2.6.0",
|
||||||
"@mozzius/expo-dynamic-app-icon": "^1.8.0",
|
"@mozzius/expo-dynamic-app-icon": "^1.8.0",
|
||||||
"@react-native-async-storage/async-storage": "2.2.0",
|
"@react-native-async-storage/async-storage": "2.2.0",
|
||||||
@@ -117,9 +116,9 @@
|
|||||||
"@react-navigation/native": "^7.1.33",
|
"@react-navigation/native": "^7.1.33",
|
||||||
"@react-navigation/native-stack": "^7.14.4",
|
"@react-navigation/native-stack": "^7.14.4",
|
||||||
"@sentry/react-native": "~6.20.0",
|
"@sentry/react-native": "~6.20.0",
|
||||||
"@tanstack/query-async-storage-persister": "^5.25.0",
|
"@tanstack/query-async-storage-persister": "^5.95.2",
|
||||||
"@tanstack/react-query": "5.25.0",
|
"@tanstack/react-query": "^5.95.2",
|
||||||
"@tanstack/react-query-persist-client": "^5.25.0",
|
"@tanstack/react-query-persist-client": "^5.95.2",
|
||||||
"@tiptap/core": "^2.9.1",
|
"@tiptap/core": "^2.9.1",
|
||||||
"@tiptap/extension-document": "^2.9.1",
|
"@tiptap/extension-document": "^2.9.1",
|
||||||
"@tiptap/extension-hard-break": "^2.9.1",
|
"@tiptap/extension-hard-break": "^2.9.1",
|
||||||
@@ -167,6 +166,7 @@
|
|||||||
"expo-location": "~19.0.8",
|
"expo-location": "~19.0.8",
|
||||||
"expo-media-library": "~18.2.1",
|
"expo-media-library": "~18.2.1",
|
||||||
"expo-notifications": "~0.32.16",
|
"expo-notifications": "~0.32.16",
|
||||||
|
"expo-paste-input": "^0.1.10",
|
||||||
"expo-privacy-sensitive": "^0.1.0",
|
"expo-privacy-sensitive": "^0.1.0",
|
||||||
"expo-screen-orientation": "~9.0.8",
|
"expo-screen-orientation": "~9.0.8",
|
||||||
"expo-sharing": "~14.0.8",
|
"expo-sharing": "~14.0.8",
|
||||||
|
|||||||
@@ -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));
|
|
||||||
}
|
|
||||||
@@ -560,32 +560,30 @@ export function ProfileGrid({
|
|||||||
<Text style={[a.text_sm, a.font_semi_bold, t.atoms.text]}>
|
<Text style={[a.text_sm, a.font_semi_bold, t.atoms.text]}>
|
||||||
<Trans>Suggested for you</Trans>
|
<Trans>Suggested for you</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
{!isProfileHeaderContext && (
|
<Button
|
||||||
<Button
|
label={l`See more suggested profiles`}
|
||||||
label={l`See more suggested profiles`}
|
onPress={() => {
|
||||||
onPress={() => {
|
followDialogControl.open()
|
||||||
followDialogControl.open()
|
ax.metric('suggestedUser:seeMore', {
|
||||||
ax.metric('suggestedUser:seeMore', {
|
logContext,
|
||||||
logContext,
|
recId,
|
||||||
recId,
|
})
|
||||||
})
|
}}>
|
||||||
}}>
|
{({hovered}) => (
|
||||||
{({hovered}) => (
|
<Text
|
||||||
<Text
|
style={[
|
||||||
style={[
|
a.text_sm,
|
||||||
a.text_sm,
|
{color: t.palette.primary_500},
|
||||||
{color: t.palette.primary_500},
|
hovered &&
|
||||||
hovered &&
|
web({
|
||||||
web({
|
textDecorationLine: 'underline',
|
||||||
textDecorationLine: 'underline',
|
textDecorationColor: t.palette.primary_500,
|
||||||
textDecorationColor: t.palette.primary_500,
|
}),
|
||||||
}),
|
]}>
|
||||||
]}>
|
<Trans>See more</Trans>
|
||||||
<Trans>See more</Trans>
|
</Text>
|
||||||
</Text>
|
)}
|
||||||
)}
|
</Button>
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
<FollowDialogWithoutGuide control={followDialogControl} />
|
<FollowDialogWithoutGuide control={followDialogControl} />
|
||||||
<LayoutAnimationConfig skipExiting skipEntering>
|
<LayoutAnimationConfig skipExiting skipEntering>
|
||||||
@@ -605,16 +603,14 @@ export function ProfileGrid({
|
|||||||
decelerationRate="fast">
|
decelerationRate="fast">
|
||||||
{content}
|
{content}
|
||||||
|
|
||||||
{!isProfileHeaderContext && (
|
<SeeMoreSuggestedProfilesCard
|
||||||
<SeeMoreSuggestedProfilesCard
|
onPress={() => {
|
||||||
onPress={() => {
|
followDialogControl.open()
|
||||||
followDialogControl.open()
|
ax.metric('suggestedUser:seeMore', {
|
||||||
ax.metric('suggestedUser:seeMore', {
|
logContext,
|
||||||
logContext,
|
})
|
||||||
})
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</BlockDrawerGesture>
|
</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 {Pause_Filled_Corner0_Rounded as PauseIcon} from '#/components/icons/Pause'
|
||||||
import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play'
|
import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play'
|
||||||
import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker'
|
import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker'
|
||||||
|
import {KeepAwake} from '#/components/KeepAwake'
|
||||||
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
||||||
import {useVideoMuteState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext'
|
import {useVideoMuteState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext'
|
||||||
import {GifPresentationControls} from '../GifPresentationControls'
|
import {GifPresentationControls} from '../GifPresentationControls'
|
||||||
@@ -112,6 +113,7 @@ export function VideoEmbedInnerNative({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<MediaInsetBorder />
|
<MediaInsetBorder />
|
||||||
|
<KeepAwake enabled={isPlaying} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import {View} from 'react-native'
|
|||||||
import {Select as RadixSelect} from 'radix-ui'
|
import {Select as RadixSelect} from 'radix-ui'
|
||||||
|
|
||||||
import {useA11y} from '#/state/a11y'
|
import {useA11y} from '#/state/a11y'
|
||||||
import {flatten, useTheme, web} from '#/alf'
|
import {atoms as a, flatten, useTheme, web} from '#/alf'
|
||||||
import {atoms as a} from '#/alf'
|
|
||||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||||
import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check'
|
import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check'
|
||||||
import {
|
import {
|
||||||
@@ -109,7 +108,7 @@ export function Trigger({children, label}: TriggerProps) {
|
|||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
maxWidth: 400,
|
maxWidth: 400,
|
||||||
outline: 0,
|
outline: 0,
|
||||||
borderWidth: 2,
|
borderWidth: 1,
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderColor: focused
|
borderColor: focused
|
||||||
? t.palette.primary_500
|
? t.palette.primary_500
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {View} from 'react-native'
|
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'
|
import {IS_NATIVE, IS_WEB, IS_WEB_TOUCH_DEVICE} from '#/env'
|
||||||
|
|
||||||
export function SubtleHover({
|
export function SubtleHover({
|
||||||
@@ -33,6 +33,10 @@ export function SubtleHover({
|
|||||||
a.transition_opacity,
|
a.transition_opacity,
|
||||||
t.atoms.bg_contrast_50,
|
t.atoms.bg_contrast_50,
|
||||||
style,
|
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},
|
{opacity: hover ? opacity : 0},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export function DateFieldButton({
|
|||||||
paddingLeft: 14,
|
paddingLeft: 14,
|
||||||
paddingRight: 14,
|
paddingRight: 14,
|
||||||
borderColor: 'transparent',
|
borderColor: 'transparent',
|
||||||
borderWidth: 2,
|
borderWidth: 1,
|
||||||
},
|
},
|
||||||
native({
|
native({
|
||||||
paddingTop: 10,
|
paddingTop: 10,
|
||||||
|
|||||||
Vendored
+8
@@ -10,6 +10,10 @@ const iOSMajorVersion =
|
|||||||
Platform.OS === 'ios' && typeof Platform.Version === 'string'
|
Platform.OS === 'ios' && typeof Platform.Version === 'string'
|
||||||
? parseInt(Platform.Version.split('.')[0], 10)
|
? parseInt(Platform.Version.split('.')[0], 10)
|
||||||
: 0
|
: 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
|
* 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
|
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
|
// 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
|
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)',
|
'(min-resolution: 2dppx)',
|
||||||
).matches
|
).matches
|
||||||
export const IS_LIQUID_GLASS: boolean = false
|
export const IS_LIQUID_GLASS: boolean = false
|
||||||
|
export const IS_TRANSLATION_SUPPORTED: boolean = false
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
import {useCallback, useContext, useEffect, useMemo, useState} from 'react'
|
import {useCallback, useContext, useEffect, useMemo, useState} from 'react'
|
||||||
import {LayoutAnimation, Platform} from 'react-native'
|
import {LayoutAnimation, Platform} from 'react-native'
|
||||||
import {getLocales} from 'expo-localization'
|
import {getLocales} from 'expo-localization'
|
||||||
import {
|
import {onTranslateTask} from '@bsky.app/expo-translate-text'
|
||||||
isTranslationSupported,
|
|
||||||
onTranslateTask,
|
|
||||||
} from '@bsky.app/expo-translate-text'
|
|
||||||
import {type TranslationTaskResult} from '@bsky.app/expo-translate-text/build/ExpoTranslateText.types'
|
import {type TranslationTaskResult} from '@bsky.app/expo-translate-text/build/ExpoTranslateText.types'
|
||||||
import {useLingui} from '@lingui/react/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useFocusEffect} from '@react-navigation/native'
|
import {useFocusEffect} from '@react-navigation/native'
|
||||||
|
|
||||||
import {useGoogleTranslate} from '#/lib/hooks/useGoogleTranslate'
|
import {useGoogleTranslate} from '#/lib/hooks/useGoogleTranslate'
|
||||||
|
import {codeToLanguageName} from '#/locale/helpers'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
import {useLanguagePrefs} from '#/state/preferences'
|
||||||
import {useAnalytics} from '#/analytics'
|
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 {Context} from './context'
|
||||||
import {
|
import {
|
||||||
type ContextType,
|
type ContextType,
|
||||||
@@ -25,6 +24,11 @@ import {guessLanguage} from './utils'
|
|||||||
export * from './types'
|
export * from './types'
|
||||||
export * from './utils'
|
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.
|
* 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
|
* 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 : ''
|
typeof result.translatedTexts === 'string' ? result.translatedTexts : ''
|
||||||
|
|
||||||
if (translatedText === input) {
|
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 === '') {
|
if (translatedText === '') {
|
||||||
throw new Error('Translation result is empty.')
|
throw new Error(E_EMPTY_RESULT)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -159,6 +163,7 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
|
|||||||
>({})
|
>({})
|
||||||
const [refCounts, setRefCounts] = useState<Record<string, number>>({})
|
const [refCounts, setRefCounts] = useState<Record<string, number>>({})
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
|
const langPrefs = useLanguagePrefs()
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const googleTranslate = useGoogleTranslate()
|
const googleTranslate = useGoogleTranslate()
|
||||||
|
|
||||||
@@ -235,7 +240,7 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
|
|||||||
googleTranslate: shouldForceGoogleTranslate,
|
googleTranslate: shouldForceGoogleTranslate,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (shouldForceGoogleTranslate || !isTranslationSupported()) {
|
if (shouldForceGoogleTranslate || !IS_TRANSLATION_SUPPORTED) {
|
||||||
await googleTranslate(
|
await googleTranslate(
|
||||||
text,
|
text,
|
||||||
expectedTargetLanguage,
|
expectedTargetLanguage,
|
||||||
@@ -280,7 +285,8 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
|
|||||||
postLanguages: possibleSourceLanguages,
|
postLanguages: possibleSourceLanguages,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
|
const e = err as Error
|
||||||
logger.error('Failed to translate text on device', {safeMessage: e})
|
logger.error('Failed to translate text on device', {safeMessage: e})
|
||||||
// On-device translation failed (language pack missing or user
|
// On-device translation failed (language pack missing or user
|
||||||
// dismissed the download prompt).
|
// dismissed the download prompt).
|
||||||
@@ -295,6 +301,21 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
|
|||||||
textLength: text.length,
|
textLength: text.length,
|
||||||
})
|
})
|
||||||
let errorMessage = l`Device failed to translate :(`
|
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) {
|
if (!IS_ANDROID) {
|
||||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
|
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(
|
const ctx = useMemo(
|
||||||
|
|||||||
@@ -182,6 +182,11 @@ msgstr ""
|
|||||||
msgid "{0} is not available"
|
msgid "{0} is not available"
|
||||||
msgstr ""
|
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)
|
#. placeholder {0}: formatCount(i18n, JOINED_THIS_WEEK)
|
||||||
#: src/screens/StarterPack/StarterPackLandingScreen.tsx:232
|
#: src/screens/StarterPack/StarterPackLandingScreen.tsx:232
|
||||||
msgid "{0} joined this week"
|
msgid "{0} joined this week"
|
||||||
@@ -852,8 +857,8 @@ msgstr ""
|
|||||||
#: src/view/com/composer/GifAltText.tsx:76
|
#: src/view/com/composer/GifAltText.tsx:76
|
||||||
#: src/view/com/composer/GifAltText.tsx:150
|
#: src/view/com/composer/GifAltText.tsx:150
|
||||||
#: src/view/com/composer/GifAltText.tsx:217
|
#: src/view/com/composer/GifAltText.tsx:217
|
||||||
#: src/view/com/composer/photos/Gallery.tsx:194
|
#: src/view/com/composer/photos/Gallery.tsx:196
|
||||||
#: src/view/com/composer/photos/Gallery.tsx:241
|
#: src/view/com/composer/photos/Gallery.tsx:243
|
||||||
#: src/view/com/composer/photos/ImageAltTextDialog.tsx:95
|
#: src/view/com/composer/photos/ImageAltTextDialog.tsx:95
|
||||||
#: src/view/com/composer/photos/ImageAltTextDialog.tsx:103
|
#: src/view/com/composer/photos/ImageAltTextDialog.tsx:103
|
||||||
msgid "Add alt text"
|
msgid "Add alt text"
|
||||||
@@ -1157,7 +1162,7 @@ msgstr ""
|
|||||||
#: src/components/images/Gallery.tsx:120
|
#: src/components/images/Gallery.tsx:120
|
||||||
#: src/components/Post/Embed/VideoEmbed/GifPresentationControls.tsx:94
|
#: src/components/Post/Embed/VideoEmbed/GifPresentationControls.tsx:94
|
||||||
#: src/view/com/composer/GifAltText.tsx:100
|
#: 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"
|
msgid "ALT"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1176,7 +1181,7 @@ msgid "Alt Text"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/view/com/composer/GifAltText.tsx:105
|
#: 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."
|
msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1827,20 +1832,20 @@ msgstr ""
|
|||||||
msgid "Browse custom feeds"
|
msgid "Browse custom feeds"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/FeedInterstitials.tsx:631
|
#: src/components/FeedInterstitials.tsx:627
|
||||||
msgid "Browse more accounts"
|
msgid "Browse more accounts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/FeedInterstitials.tsx:761
|
#: src/components/FeedInterstitials.tsx:757
|
||||||
msgid "Browse more feeds on the Explore page"
|
msgid "Browse more feeds on the Explore page"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/FeedInterstitials.tsx:742
|
#: src/components/FeedInterstitials.tsx:738
|
||||||
#: src/components/FeedInterstitials.tsx:745
|
#: src/components/FeedInterstitials.tsx:741
|
||||||
msgid "Browse more suggestions"
|
msgid "Browse more suggestions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/FeedInterstitials.tsx:770
|
#: src/components/FeedInterstitials.tsx:766
|
||||||
msgid "Browse more suggestions on the Explore page"
|
msgid "Browse more suggestions on the Explore page"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3159,7 +3164,7 @@ msgstr ""
|
|||||||
msgid "Developer options"
|
msgid "Developer options"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/translation/index.tsx:297
|
#: src/lib/translation/index.tsx:303
|
||||||
msgid "Device failed to translate :("
|
msgid "Device failed to translate :("
|
||||||
msgstr "Device failed to translate :("
|
msgstr "Device failed to translate :("
|
||||||
|
|
||||||
@@ -3489,7 +3494,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/view/com/composer/photos/EditImageDialog.web.tsx:86
|
#: src/view/com/composer/photos/EditImageDialog.web.tsx:86
|
||||||
#: src/view/com/composer/photos/EditImageDialog.web.tsx:90
|
#: 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"
|
msgid "Edit image"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3760,7 +3765,7 @@ msgstr ""
|
|||||||
msgid "Enter your username and password"
|
msgid "Enter your username and password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:148
|
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:150
|
||||||
msgid "Enters full screen"
|
msgid "Enters full screen"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -6236,7 +6241,7 @@ msgstr ""
|
|||||||
msgid "Music"
|
msgid "Music"
|
||||||
msgstr ""
|
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
|
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx:97
|
||||||
msgctxt "video"
|
msgctxt "video"
|
||||||
msgid "Mute"
|
msgid "Mute"
|
||||||
@@ -6712,6 +6717,10 @@ msgstr ""
|
|||||||
msgid "No thanks"
|
msgid "No thanks"
|
||||||
msgstr ""
|
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
|
#: src/view/screens/Profile.tsx:500
|
||||||
msgid "No video posts yet"
|
msgid "No video posts yet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -7262,7 +7271,7 @@ msgstr ""
|
|||||||
msgid "Password updated!"
|
msgid "Password updated!"
|
||||||
msgstr ""
|
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
|
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx:395
|
||||||
msgid "Pause"
|
msgid "Pause"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -7377,7 +7386,7 @@ msgstr ""
|
|||||||
msgid "Pinned to your feeds"
|
msgid "Pinned to your feeds"
|
||||||
msgstr ""
|
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
|
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx:396
|
||||||
msgid "Play"
|
msgid "Play"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -7404,7 +7413,7 @@ msgstr ""
|
|||||||
msgid "Plays or pauses the GIF"
|
msgid "Plays or pauses the GIF"
|
||||||
msgstr ""
|
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"
|
msgid "Plays or pauses the video"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -8121,7 +8130,7 @@ msgstr ""
|
|||||||
msgid "Remove from your feeds?"
|
msgid "Remove from your feeds?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/view/com/composer/photos/Gallery.tsx:228
|
#: src/view/com/composer/photos/Gallery.tsx:230
|
||||||
msgid "Remove image"
|
msgid "Remove image"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -8759,14 +8768,14 @@ msgstr ""
|
|||||||
msgid "Search for \"{interestsDisplayName}\" (active)"
|
msgid "Search for \"{interestsDisplayName}\" (active)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/view/shell/desktop/Search.tsx:130
|
|
||||||
msgid "Search for \"{query}\""
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/screens/Search/components/AutocompleteResults.tsx:47
|
#: src/screens/Search/components/AutocompleteResults.tsx:47
|
||||||
msgid "Search for \"{searchText}\""
|
msgid "Search for \"{searchText}\""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/view/shell/desktop/Search.tsx:128
|
||||||
|
msgid "Search for “{tQuery}”"
|
||||||
|
msgstr "Search for “{tQuery}”"
|
||||||
|
|
||||||
#: src/screens/StarterPack/Wizard/index.tsx:552
|
#: src/screens/StarterPack/Wizard/index.tsx:552
|
||||||
msgid "Search for feeds that you want to suggest to others."
|
msgid "Search for feeds that you want to suggest to others."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -8864,12 +8873,12 @@ msgstr ""
|
|||||||
msgid "See jobs at Bluesky"
|
msgid "See jobs at Bluesky"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/FeedInterstitials.tsx:584
|
#: src/components/FeedInterstitials.tsx:583
|
||||||
#: src/components/FeedInterstitials.tsx:645
|
#: src/components/FeedInterstitials.tsx:641
|
||||||
msgid "See more"
|
msgid "See more"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/FeedInterstitials.tsx:565
|
#: src/components/FeedInterstitials.tsx:564
|
||||||
msgid "See more suggested profiles"
|
msgid "See more suggested profiles"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -9565,7 +9574,7 @@ msgstr ""
|
|||||||
msgid "Some of your verifications are invalid."
|
msgid "Some of your verifications are invalid."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/FeedInterstitials.tsx:724
|
#: src/components/FeedInterstitials.tsx:720
|
||||||
msgid "Some other feeds you might like"
|
msgid "Some other feeds you might like"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -10609,7 +10618,7 @@ msgstr ""
|
|||||||
msgid "Toggle to enable or disable adult content"
|
msgid "Toggle to enable or disable adult content"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:171
|
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:173
|
||||||
msgid "Toggles the sound"
|
msgid "Toggles the sound"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -10664,6 +10673,10 @@ msgstr "Translating"
|
|||||||
msgid "Translating…"
|
msgid "Translating…"
|
||||||
msgstr "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:87
|
||||||
#: src/screens/Settings/ThreadPreferences.tsx:92
|
#: src/screens/Settings/ThreadPreferences.tsx:92
|
||||||
msgid "Tree view"
|
msgid "Tree view"
|
||||||
@@ -10882,7 +10895,7 @@ msgstr ""
|
|||||||
msgid "Unmute"
|
msgid "Unmute"
|
||||||
msgstr ""
|
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
|
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx:96
|
||||||
msgctxt "video"
|
msgctxt "video"
|
||||||
msgid "Unmute"
|
msgid "Unmute"
|
||||||
@@ -10981,6 +10994,10 @@ msgstr ""
|
|||||||
msgid "Unsubscribed from list"
|
msgid "Unsubscribed from list"
|
||||||
msgstr ""
|
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
|
#: src/view/com/composer/Composer.tsx:1369
|
||||||
msgid "Unsupported video type: {mimeType}"
|
msgid "Unsupported video type: {mimeType}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -11295,8 +11312,8 @@ msgstr ""
|
|||||||
msgid "Version {0}"
|
msgid "Version {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:86
|
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:87
|
||||||
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:147
|
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:149
|
||||||
msgid "Video"
|
msgid "Video"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -11344,7 +11361,7 @@ msgid "Video uploaded"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: embed.alt
|
#. 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}"
|
msgid "Video: {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import {AtUri} from '@atproto/api'
|
import {AtUri, type BskyAgent} from '@atproto/api'
|
||||||
import {
|
import {type QueryClient, queryOptions, useQuery} from '@tanstack/react-query'
|
||||||
type QueryClient,
|
|
||||||
useQuery,
|
|
||||||
type UseQueryResult,
|
|
||||||
} from '@tanstack/react-query'
|
|
||||||
|
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
@@ -12,26 +8,12 @@ import {useUnstableProfileViewCache} from './profile'
|
|||||||
const RQKEY_ROOT = 'resolved-did'
|
const RQKEY_ROOT = 'resolved-did'
|
||||||
export const RQKEY = (didOrHandle: string) => [RQKEY_ROOT, didOrHandle]
|
export const RQKEY = (didOrHandle: string) => [RQKEY_ROOT, didOrHandle]
|
||||||
|
|
||||||
type UriUseQueryResult = UseQueryResult<{did: string; uri: string}, Error>
|
const resolvedDidQueryOptions = (
|
||||||
export function useResolveUriQuery(uri: string | undefined): UriUseQueryResult {
|
agent: BskyAgent,
|
||||||
const urip = new AtUri(uri || '')
|
getUnstableProfile: (did: string) => {did: string} | undefined,
|
||||||
const res = useResolveDidQuery(urip.host)
|
didOrHandle: string | undefined,
|
||||||
if (res.data) {
|
) =>
|
||||||
// @ts-expect-error TODO new-sdk-migration
|
queryOptions({
|
||||||
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>({
|
|
||||||
staleTime: STALE.HOURS.ONE,
|
staleTime: STALE.HOURS.ONE,
|
||||||
queryKey: RQKEY(didOrHandle ?? ''),
|
queryKey: RQKEY(didOrHandle ?? ''),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
@@ -50,6 +32,30 @@ export function useResolveDidQuery(didOrHandle: string | undefined) {
|
|||||||
},
|
},
|
||||||
enabled: !!didOrHandle,
|
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(
|
export function precacheResolvedUri(
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export function GifAltTextDialogLoaded({
|
|||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<Admonition type="tip" style={[a.mt_sm]}>
|
<Admonition type="info" style={[a.mt_sm]}>
|
||||||
<Trans>
|
<Trans>
|
||||||
Alt text describes images for blind and low-vision users, and helps
|
Alt text describes images for blind and low-vision users, and helps
|
||||||
give context to everyone.
|
give context to everyone.
|
||||||
|
|||||||
@@ -99,12 +99,14 @@ const GalleryInner = ({images, dispatch}: GalleryInnerProps) => {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
<Admonition type="tip" style={[a.mt_sm]}>
|
{images.some(image => !image.alt) && (
|
||||||
<Trans>
|
<Admonition type="info" style={[a.mt_sm]}>
|
||||||
Alt text describes images for blind and low-vision users, and helps
|
<Trans>
|
||||||
give context to everyone.
|
Alt text describes images for blind and low-vision users, and helps
|
||||||
</Trans>
|
give context to everyone.
|
||||||
</Admonition>
|
</Trans>
|
||||||
|
</Admonition>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,21 +8,17 @@ import {
|
|||||||
import {
|
import {
|
||||||
type NativeSyntheticEvent,
|
type NativeSyntheticEvent,
|
||||||
Text as RNText,
|
Text as RNText,
|
||||||
|
TextInput as RNTextInput,
|
||||||
type TextInputSelectionChangeEventData,
|
type TextInputSelectionChangeEventData,
|
||||||
View,
|
View,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
|
import {type PasteEventPayload, TextInputWrapper} from 'expo-paste-input'
|
||||||
import {AppBskyRichtextFacet, RichText} from '@atproto/api'
|
import {AppBskyRichtextFacet, RichText} from '@atproto/api'
|
||||||
import PasteInput, {
|
import {useLingui} from '@lingui/react/macro'
|
||||||
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 {POST_IMG_MAX} from '#/lib/constants'
|
import {POST_IMG_MAX} from '#/lib/constants'
|
||||||
import {downloadAndResize} from '#/lib/media/manip'
|
import {downloadAndResize} from '#/lib/media/manip'
|
||||||
import {isUriImage} from '#/lib/media/util'
|
import {isUriImage} from '#/lib/media/util'
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
|
||||||
import {getMentionAt, insertMentionAt} from '#/lib/strings/mention-manip'
|
import {getMentionAt, insertMentionAt} from '#/lib/strings/mention-manip'
|
||||||
import {useTheme} from '#/lib/ThemeContext'
|
import {useTheme} from '#/lib/ThemeContext'
|
||||||
import {
|
import {
|
||||||
@@ -51,8 +47,9 @@ export function TextInput({
|
|||||||
onError,
|
onError,
|
||||||
...props
|
...props
|
||||||
}: TextInputProps) {
|
}: TextInputProps) {
|
||||||
|
const {t: l} = useLingui()
|
||||||
const {theme: t, fonts} = useAlf()
|
const {theme: t, fonts} = useAlf()
|
||||||
const textInput = useRef<PasteInputRef>(null)
|
const textInput = useRef<RNTextInput>(null)
|
||||||
const textInputSelection = useRef<Selection>({start: 0, end: 0})
|
const textInputSelection = useRef<Selection>({start: 0, end: 0})
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const [autocompletePrefix, setAutocompletePrefix] = useState('')
|
const [autocompletePrefix, setAutocompletePrefix] = useState('')
|
||||||
@@ -129,19 +126,21 @@ export function TextInput({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const onPaste = useCallback(
|
const onPaste = useCallback(
|
||||||
async (err: string | undefined, files: PastedFile[]) => {
|
(payload: PasteEventPayload) => {
|
||||||
if (err) {
|
if (payload.type === 'unsupported') {
|
||||||
return onError(cleanError(err))
|
onError(l`Unsupported clipboard content`)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const uris = files.map(f => f.uri)
|
if (payload.type === 'images') {
|
||||||
const uri = uris.find(isUriImage)
|
for (const uri of payload.uris) {
|
||||||
|
if (isUriImage(uri)) {
|
||||||
if (uri) {
|
onPhotoPasted(uri)
|
||||||
onPhotoPasted(uri)
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onError, onPhotoPasted],
|
[l, onError, onPhotoPasted],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onSelectionChange = useCallback(
|
const onSelectionChange = useCallback(
|
||||||
@@ -217,41 +216,42 @@ export function TextInput({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.flex_1, a.pl_md, hasRightPadding && a.pr_4xl]}>
|
<View style={[a.flex_1, a.pl_md, hasRightPadding && a.pr_4xl]}>
|
||||||
<PasteInput
|
<TextInputWrapper onPaste={onPaste}>
|
||||||
testID="composerTextInput"
|
<RNTextInput
|
||||||
ref={textInput}
|
testID="composerTextInput"
|
||||||
onChangeText={onChangeText}
|
ref={textInput}
|
||||||
onPaste={onPaste}
|
onChangeText={onChangeText}
|
||||||
onSelectionChange={onSelectionChange}
|
onSelectionChange={onSelectionChange}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
placeholderTextColor={t.atoms.text_contrast_low.color}
|
placeholderTextColor={t.atoms.text_contrast_low.color}
|
||||||
keyboardAppearance={theme.colorScheme}
|
keyboardAppearance={theme.colorScheme}
|
||||||
autoFocus={props.autoFocus !== undefined ? props.autoFocus : true}
|
autoFocus={props.autoFocus !== undefined ? props.autoFocus : true}
|
||||||
allowFontScaling
|
allowFontScaling
|
||||||
multiline
|
multiline
|
||||||
scrollEnabled={false}
|
scrollEnabled={false}
|
||||||
numberOfLines={2}
|
numberOfLines={2}
|
||||||
// Note: should be the default value, but as of v1.104
|
// Note: should be the default value, but as of v1.104
|
||||||
// it switched to "none" on Android
|
// it switched to "none" on Android
|
||||||
autoCapitalize="sentences"
|
autoCapitalize="sentences"
|
||||||
{...props}
|
{...props}
|
||||||
style={[
|
style={[
|
||||||
inputTextStyle,
|
inputTextStyle,
|
||||||
a.w_full,
|
a.w_full,
|
||||||
!autocompletePrefix && a.h_full,
|
!autocompletePrefix && a.h_full,
|
||||||
{
|
{
|
||||||
textAlignVertical: 'top',
|
textAlignVertical: 'top',
|
||||||
minHeight: 60,
|
minHeight: 60,
|
||||||
includeFontPadding: false,
|
includeFontPadding: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: 'transparent',
|
borderColor: 'transparent',
|
||||||
},
|
},
|
||||||
props.style,
|
props.style,
|
||||||
]}>
|
]}>
|
||||||
{textDecorated}
|
{textDecorated}
|
||||||
</PasteInput>
|
</RNTextInput>
|
||||||
|
</TextInputWrapper>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
prefix={autocompletePrefix}
|
prefix={autocompletePrefix}
|
||||||
onSelect={onSelectAutocompleteItem}
|
onSelect={onSelectAutocompleteItem}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {memo, useCallback, useState} from 'react'
|
import {memo, useCallback, useState} from 'react'
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
type StyleProp,
|
||||||
StyleSheet,
|
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
type ViewStyle,
|
type ViewStyle,
|
||||||
@@ -9,15 +8,17 @@ import {
|
|||||||
import {useLingui} from '@lingui/react/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {StackActions, useNavigation} from '@react-navigation/native'
|
import {StackActions, useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
|
||||||
import {type NavigationProp} from '#/lib/routes/types'
|
import {type NavigationProp} from '#/lib/routes/types'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
|
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
|
||||||
import {Link} from '#/view/com/util/Link'
|
|
||||||
import {Text} from '#/view/com/util/text/Text'
|
|
||||||
import {SearchProfileCard} from '#/screens/Search/components/SearchProfileCard'
|
import {SearchProfileCard} from '#/screens/Search/components/SearchProfileCard'
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {SearchInput} from '#/components/forms/SearchInput'
|
import {SearchInput} from '#/components/forms/SearchInput'
|
||||||
|
import {Link} from '#/components/Link'
|
||||||
|
import {Loader} from '#/components/Loader'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
|
const WHITESPACE_RE = /\s+/gu
|
||||||
|
|
||||||
let SearchLinkCard = ({
|
let SearchLinkCard = ({
|
||||||
label,
|
label,
|
||||||
@@ -28,20 +29,17 @@ let SearchLinkCard = ({
|
|||||||
label: string
|
label: string
|
||||||
to?: string
|
to?: string
|
||||||
onPress?: () => void
|
onPress?: () => void
|
||||||
style?: ViewStyle
|
style?: StyleProp<ViewStyle>
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const pal = usePalette('default')
|
const t = useTheme()
|
||||||
|
|
||||||
const inner = (
|
const inner = (
|
||||||
<View
|
<View style={[a.py_lg, a.px_md, t.atoms.border_contrast_low, style]}>
|
||||||
style={[pal.border, {paddingVertical: 16, paddingHorizontal: 12}, style]}>
|
<Text style={[a.text_md, t.atoms.text]}>{label}</Text>
|
||||||
<Text type="md" style={[pal.text]}>
|
|
||||||
{label}
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
||||||
if (onPress) {
|
if (onPress || !to) {
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
@@ -53,17 +51,12 @@ let SearchLinkCard = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={to} asAnchor anchorNoUnderline>
|
<Link
|
||||||
<View
|
label={label}
|
||||||
style={[
|
to={to}
|
||||||
pal.border,
|
style={[a.py_lg, a.px_md, t.atoms.border_contrast_low, style]}
|
||||||
{paddingVertical: 16, paddingHorizontal: 12},
|
hoverStyle={[t.atoms.bg_contrast_25]}>
|
||||||
style,
|
<Text style={[a.text_md, t.atoms.text]}>{label}</Text>
|
||||||
]}>
|
|
||||||
<Text type="md" style={[pal.text]}>
|
|
||||||
{label}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -71,8 +64,8 @@ SearchLinkCard = memo(SearchLinkCard)
|
|||||||
export {SearchLinkCard}
|
export {SearchLinkCard}
|
||||||
|
|
||||||
export function DesktopSearch() {
|
export function DesktopSearch() {
|
||||||
|
const t = useTheme()
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const pal = usePalette('default')
|
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
const [isActive, setIsActive] = useState<boolean>(false)
|
const [isActive, setIsActive] = useState<boolean>(false)
|
||||||
const [query, setQuery] = useState<string>('')
|
const [query, setQuery] = useState<string>('')
|
||||||
@@ -80,6 +73,7 @@ export function DesktopSearch() {
|
|||||||
query,
|
query,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
const tQuery = query.replace(WHITESPACE_RE, ' ').trim()
|
||||||
|
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
|
|
||||||
@@ -95,9 +89,9 @@ export function DesktopSearch() {
|
|||||||
|
|
||||||
const onSubmit = useCallback(() => {
|
const onSubmit = useCallback(() => {
|
||||||
setIsActive(false)
|
setIsActive(false)
|
||||||
if (!query.length) return
|
if (!tQuery.length) return
|
||||||
navigation.dispatch(StackActions.push('Search', {q: query}))
|
navigation.dispatch(StackActions.push('Search', {q: tQuery}))
|
||||||
}, [query, navigation])
|
}, [tQuery, navigation])
|
||||||
|
|
||||||
const onSearchProfileCardPress = useCallback(() => {
|
const onSearchProfileCardPress = useCallback(() => {
|
||||||
setQuery('')
|
setQuery('')
|
||||||
@@ -105,62 +99,58 @@ export function DesktopSearch() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, pal.view]}>
|
<View style={[a.relative, a.w_full, a.z_10, t.atoms.bg]}>
|
||||||
<SearchInput
|
<SearchInput
|
||||||
value={query}
|
value={query}
|
||||||
onChangeText={onChangeText}
|
onChangeText={onChangeText}
|
||||||
onClearText={onPressCancelSearch}
|
onClearText={onPressCancelSearch}
|
||||||
onSubmitEditing={onSubmit}
|
onSubmitEditing={onSubmit}
|
||||||
/>
|
/>
|
||||||
{query !== '' && isActive && moderationOpts && (
|
{tQuery !== '' && isActive && moderationOpts && (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
pal.view,
|
a.mt_sm,
|
||||||
pal.borderDark,
|
a.flex_col,
|
||||||
styles.resultsContainer,
|
a.w_full,
|
||||||
a.overflow_hidden,
|
a.border,
|
||||||
|
a.rounded_sm,
|
||||||
|
a.zoom_fade_in,
|
||||||
|
t.atoms.bg,
|
||||||
|
t.atoms.shadow_sm,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
{
|
||||||
|
overflow: 'hidden',
|
||||||
|
position: 'absolute',
|
||||||
|
top: '100%',
|
||||||
|
},
|
||||||
]}>
|
]}>
|
||||||
|
<SearchLinkCard
|
||||||
|
label={l`Search for “${tQuery}”`}
|
||||||
|
to={`/search?q=${encodeURIComponent(tQuery)}`}
|
||||||
|
style={(autocompleteData?.length ?? 0) > 0 ? a.border_b : undefined}
|
||||||
|
/>
|
||||||
{isFetching && !autocompleteData?.length ? (
|
{isFetching && !autocompleteData?.length ? (
|
||||||
<View style={{padding: 8}}>
|
<View
|
||||||
<ActivityIndicator />
|
style={[
|
||||||
|
a.py_lg,
|
||||||
|
a.align_center,
|
||||||
|
a.border_t,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
]}>
|
||||||
|
<Loader size="lg" />
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<>
|
autocompleteData?.map(item => (
|
||||||
<SearchLinkCard
|
<SearchProfileCard
|
||||||
label={l`Search for "${query}"`}
|
key={item.did}
|
||||||
to={`/search?q=${encodeURIComponent(query)}`}
|
profile={item}
|
||||||
style={
|
moderationOpts={moderationOpts}
|
||||||
(autocompleteData?.length ?? 0) > 0
|
onPress={onSearchProfileCardPress}
|
||||||
? {borderBottomWidth: 1}
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
{autocompleteData?.map(item => (
|
))
|
||||||
<SearchProfileCard
|
|
||||||
key={item.did}
|
|
||||||
profile={item}
|
|
||||||
moderationOpts={moderationOpts}
|
|
||||||
onPress={onSearchProfileCardPress}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
position: 'relative',
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
resultsContainer: {
|
|
||||||
marginTop: 10,
|
|
||||||
flexDirection: 'column',
|
|
||||||
width: '100%',
|
|
||||||
borderWidth: 1,
|
|
||||||
borderRadius: 6,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -3894,12 +3894,6 @@
|
|||||||
"@babel/runtime" "^7.20.13"
|
"@babel/runtime" "^7.20.13"
|
||||||
"@lingui/core" "5.9.2"
|
"@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":
|
"@messageformat/parser@^5.0.0":
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.1.0.tgz#05e4851c782d633ad735791dd0a68ee65d2a7201"
|
resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.1.0.tgz#05e4851c782d633ad735791dd0a68ee65d2a7201"
|
||||||
@@ -5108,38 +5102,39 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@sinonjs/commons" "^3.0.0"
|
"@sinonjs/commons" "^3.0.0"
|
||||||
|
|
||||||
"@tanstack/query-async-storage-persister@^5.25.0":
|
"@tanstack/query-async-storage-persister@^5.95.2":
|
||||||
version "5.25.0"
|
version "5.95.2"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/query-async-storage-persister/-/query-async-storage-persister-5.25.0.tgz#0e8a2a781b8e32a81a5d02a688d6fcdfd055235b"
|
resolved "https://registry.yarnpkg.com/@tanstack/query-async-storage-persister/-/query-async-storage-persister-5.95.2.tgz#0c7ed1c8013823e2d5abbb8d55bc0e9305abf7e0"
|
||||||
integrity sha512-58UTp1CuLr2mehsJRMOd8IZPtYGHFeL+uHnHyRd1kmbwo7wDaa8HXstiBdTRq5KokxIXy9FiFbA06LtKuOiwMQ==
|
integrity sha512-ZhPIHH8J833OVZhEWwwdOk0uhY94d9Wgdnq97JoQx4Ui4xx4Dh6e7WPUrjlUWo88Yqi4Ij+T1o/VR7Vlbnkbjw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/query-persist-client-core" "5.25.0"
|
"@tanstack/query-core" "5.95.2"
|
||||||
|
"@tanstack/query-persist-client-core" "5.95.2"
|
||||||
|
|
||||||
"@tanstack/query-core@5.25.0":
|
"@tanstack/query-core@5.95.2":
|
||||||
version "5.25.0"
|
version "5.95.2"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.25.0.tgz#e08ed0a9fad34c8005d1a282e57280031ac50cdc"
|
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.95.2.tgz#9e3299d0c1c8785dd9e3d0cac1993e45f35113f2"
|
||||||
integrity sha512-vlobHP64HTuSE68lWF1mEhwSRC5Q7gaT+a/m9S+ItuN+ruSOxe1rFnR9j0ACWQ314BPhBEVKfBQ6mHL0OWfdbQ==
|
integrity sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ==
|
||||||
|
|
||||||
"@tanstack/query-persist-client-core@5.25.0":
|
"@tanstack/query-persist-client-core@5.95.2":
|
||||||
version "5.25.0"
|
version "5.95.2"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/query-persist-client-core/-/query-persist-client-core-5.25.0.tgz#52fa634a8067d7b965854a532a33077fd4df0eff"
|
resolved "https://registry.yarnpkg.com/@tanstack/query-persist-client-core/-/query-persist-client-core-5.95.2.tgz#1c94a87c9886a8e1c6a0a3ebbb325afdaf486f81"
|
||||||
integrity sha512-sEUsEZ/XWkOosO45CDBI5nj5woCS+DUd9Dk8pGpU8MkeH0EVd3p4N5CdbjNhrreyy5Krf3rpNaiRN9ygLX/rWA==
|
integrity sha512-Opfj34WZ594YXpEcZEs8WBiyPGrjrKlGILfk/Ss283uwWQ36C5nX3tRY/bBiXmM82KWauUuNvahwGwiyco/8cQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/query-core" "5.25.0"
|
"@tanstack/query-core" "5.95.2"
|
||||||
|
|
||||||
"@tanstack/react-query-persist-client@^5.25.0":
|
"@tanstack/react-query-persist-client@^5.95.2":
|
||||||
version "5.25.0"
|
version "5.95.2"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query-persist-client/-/react-query-persist-client-5.25.0.tgz#ecbd1362cd6fd94e723d54f5af477d0812852dab"
|
resolved "https://registry.yarnpkg.com/@tanstack/react-query-persist-client/-/react-query-persist-client-5.95.2.tgz#4d6fe899513725978e86c13c1727ee7e393eaca5"
|
||||||
integrity sha512-j1+GyFj4UQGWuiFZoDUVJZS+wxqKd9SGvPlyHG619zzYNN+QQu4B5uvvHc6U8MroM377EOBOuLKK3W6qsAdahQ==
|
integrity sha512-i3fvzD8gaLgQyFvRc/+iSUr60aL31tMN+5QM11zdPRg0K9CirIQjHD7WgXFBnD29KJDvcjcv7OrIBaPwZ+H9xw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/query-persist-client-core" "5.25.0"
|
"@tanstack/query-persist-client-core" "5.95.2"
|
||||||
|
|
||||||
"@tanstack/react-query@5.25.0":
|
"@tanstack/react-query@^5.95.2":
|
||||||
version "5.25.0"
|
version "5.95.2"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.25.0.tgz#f4dac794cf10dd956aa56dbbdf67049a5ba2669d"
|
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.95.2.tgz#7daf77342a4e374c22fad88bb98ea13fc19ae086"
|
||||||
integrity sha512-u+n5R7mLO7RmeiIonpaCRVXNRWtZEef/aVZ/XGWRPa7trBIvGtzlfo0Ah7ZtnTYfrKEVwnZ/tzRCBcoiqJ/tFw==
|
integrity sha512-/wGkvLj/st5Ud1Q76KF1uFxScV7WeqN1slQx5280ycwAyYkIPGaRZAEgHxe3bjirSd5Zpwkj6zNcR4cqYni/ZA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/query-core" "5.25.0"
|
"@tanstack/query-core" "5.95.2"
|
||||||
|
|
||||||
"@testing-library/react-native@^13.2.0":
|
"@testing-library/react-native@^13.2.0":
|
||||||
version "13.2.0"
|
version "13.2.0"
|
||||||
@@ -9086,6 +9081,11 @@ expo-notifications@~0.32.16:
|
|||||||
expo-application "~7.0.8"
|
expo-application "~7.0.8"
|
||||||
expo-constants "~18.0.13"
|
expo-constants "~18.0.13"
|
||||||
|
|
||||||
|
expo-paste-input@^0.1.10:
|
||||||
|
version "0.1.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-paste-input/-/expo-paste-input-0.1.10.tgz#7df0a07ae6ecd381004624dbe88762997d962780"
|
||||||
|
integrity sha512-TkAauK1eIq7+vk2SA39trmDO6dLfFLQL+09KIVZh//3XWs8gHC5ezH3vjfjr7xj9xi67amyeiDCErP5caTDIKg==
|
||||||
|
|
||||||
expo-privacy-sensitive@^0.1.0:
|
expo-privacy-sensitive@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/expo-privacy-sensitive/-/expo-privacy-sensitive-0.1.0.tgz#2177d7a3cb8ed352df94c5806d012dfb7b48bc84"
|
resolved "https://registry.yarnpkg.com/expo-privacy-sensitive/-/expo-privacy-sensitive-0.1.0.tgz#2177d7a3cb8ed352df94c5806d012dfb7b48bc84"
|
||||||
@@ -14648,16 +14648,16 @@ selfsigned@^2.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
node-forge "^1"
|
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:
|
semver@^6.0.0, semver@^6.3.0, semver@^6.3.1:
|
||||||
version "6.3.1"
|
version "6.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
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:
|
semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@~7.5.4:
|
||||||
version "7.5.4"
|
version "7.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
||||||
|
|||||||
Reference in New Issue
Block a user