Merge remote-tracking branch 'origin/main' into samuel/alf-login
commit
4794ab6b9a
|
@ -0,0 +1,55 @@
|
||||||
|
---
|
||||||
|
name: Bundle and Deploy EAS Update
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
runtimeVersion:
|
||||||
|
type: string
|
||||||
|
description: Runtime version (in x.x.x format) that this update is for
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
bundleDeploy:
|
||||||
|
name: Bundle and Deploy EAS Update
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: 🧐 Validate version
|
||||||
|
run: |
|
||||||
|
[[ "${{ github.event.inputs.runtimeVersion }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && echo "Version is valid" || exit 1
|
||||||
|
|
||||||
|
- name: ⬇️ Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: 🔧 Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: .nvmrc
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
|
- name: ⚙️ Install Dependencies
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
|
- name: 🪛 Install jq
|
||||||
|
uses: dcarbone/install-jq-action@v2
|
||||||
|
|
||||||
|
- name: ⛏️ Setup Expo
|
||||||
|
run: yarn global add eas-cli-local-build-plugin
|
||||||
|
|
||||||
|
- name: 🔤 Compile Translations
|
||||||
|
run: yarn intl:build
|
||||||
|
|
||||||
|
- name: ✏️ Write environment variables
|
||||||
|
run: |
|
||||||
|
export json='${{ secrets.GOOGLE_SERVICES_TOKEN }}'
|
||||||
|
echo "${{ secrets.ENV_TOKEN }}" > .env
|
||||||
|
echo "$json" > google-services.json
|
||||||
|
|
||||||
|
- name: 🏗️ Create Bundle
|
||||||
|
run: yarn export
|
||||||
|
|
||||||
|
- name: 📦 Package Bundle and 🚀 Deploy
|
||||||
|
run: yarn make-deploy-bundle
|
||||||
|
env:
|
||||||
|
DENIS_API_KEY: ${{ secrets.DENIS_API_KEY }}
|
||||||
|
RUNTIME_VERSION: ${{ github.event.inputs.runtimeVersion }}
|
|
@ -4,6 +4,7 @@ import {
|
||||||
linkRequiresWarning,
|
linkRequiresWarning,
|
||||||
isPossiblyAUrl,
|
isPossiblyAUrl,
|
||||||
splitApexDomain,
|
splitApexDomain,
|
||||||
|
isTrustedUrl,
|
||||||
} from '../../../src/lib/strings/url-helpers'
|
} from '../../../src/lib/strings/url-helpers'
|
||||||
|
|
||||||
describe('linkRequiresWarning', () => {
|
describe('linkRequiresWarning', () => {
|
||||||
|
@ -74,6 +75,10 @@ describe('linkRequiresWarning', () => {
|
||||||
// bad uri inputs, default to true
|
// bad uri inputs, default to true
|
||||||
['', '', true],
|
['', '', true],
|
||||||
['example.com', 'example.com', true],
|
['example.com', 'example.com', true],
|
||||||
|
['/profile', 'Username', false],
|
||||||
|
['#', 'Show More', false],
|
||||||
|
['https://docs.bsky.app', 'https://docs.bsky.app', false],
|
||||||
|
['https://bsky.app/compose/intent?text=test', 'Compose a post', false],
|
||||||
]
|
]
|
||||||
|
|
||||||
it.each(cases)(
|
it.each(cases)(
|
||||||
|
@ -139,3 +144,36 @@ describe('splitApexDomain', () => {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('isTrustedUrl', () => {
|
||||||
|
const cases = [
|
||||||
|
['#', true],
|
||||||
|
['#profile', true],
|
||||||
|
['/', true],
|
||||||
|
['/profile', true],
|
||||||
|
['/profile/', true],
|
||||||
|
['/profile/bob.test', true],
|
||||||
|
['https://bsky.app', true],
|
||||||
|
['https://bsky.app/', true],
|
||||||
|
['https://bsky.app/profile/bob.test', true],
|
||||||
|
['https://www.bsky.app', true],
|
||||||
|
['https://www.bsky.app/', true],
|
||||||
|
['https://docs.bsky.app', true],
|
||||||
|
['https://bsky.social', true],
|
||||||
|
['https://bsky.social/blog', true],
|
||||||
|
['https://blueskyweb.xyz', true],
|
||||||
|
['https://blueskyweb.zendesk.com', true],
|
||||||
|
['http://bsky.app', true],
|
||||||
|
['http://bsky.social', true],
|
||||||
|
['http://blueskyweb.xyz', true],
|
||||||
|
['http://blueskyweb.zendesk.com', true],
|
||||||
|
['https://google.com', false],
|
||||||
|
['https://docs.google.com', false],
|
||||||
|
['https://google.com/#', false],
|
||||||
|
]
|
||||||
|
|
||||||
|
it.each(cases)('given input uri %p, returns %p', (str, expected) => {
|
||||||
|
const output = isTrustedUrl(str)
|
||||||
|
expect(output).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC0TCCAbmgAwIBAgIJcMN2yt5KNDqTMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV
|
||||||
|
BAMTB0JsdWVza3kwHhcNMjQwMzE0MDA1OTU4WhcNMzQwMzE0MDA1OTU4WjASMRAw
|
||||||
|
DgYDVQQDEwdCbHVlc2t5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||||
|
izSAWEc3wRoa3eTBEh/kE9pH0d6jhEGw9GrYfei60MHT1pSq2cTdyUM1yUZchAeW
|
||||||
|
gFFtqFxX0pfIZQyMlIZbjkaOxOqzWhB0aCsxngnhbSahFwRxkVwTAuonhqIpaLBL
|
||||||
|
hrCCCQ2IfZUpy8QeasqlTlmvmijuCC34fXxJlxNcj8SqzIZi+civ7U5PMPfIMMnD
|
||||||
|
tCDIBy1vxMk57m25X2ikcWUFW64qNVLkFAL36xEnmFTL4Ivqpz23gUcUIe1zbesY
|
||||||
|
jAgDtlwnAE7mU3oagCUDcSuOveT4POhT35Xp3Y/07I68kmXtrPxwd5k0L0zbisEm
|
||||||
|
poKZ87E2X29BitihicMpBwIDAQABoyowKDAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0l
|
||||||
|
AQH/BAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAED1gdMF0yr8Gy87
|
||||||
|
RgyaeVpPySwSsO0selmXXrcmOWgiPA05lubyhFEa4P5kdzBEByG2MT+pJkjGYpvK
|
||||||
|
XRnqXM5VvdS2RhYYFH0cFOIUqBKwCnzViCMuGQeoGUx4oPcKFS0PQ1WjW2d4pS75
|
||||||
|
51GBfB6LOepsCHUG0A9XEk7EAyUWc4M2ITCJsTtJh8CVn2pTks2q14ETDs86YQv4
|
||||||
|
peDaJv8nhIe8oQkeGn2o/P/ctkwJg/uBydQUsWgjjGTQZTilVjGTW1mwDr9FucAE
|
||||||
|
d5gKIk4rtR/3Zd/NDdqp8PrkoWeVM7Hwr789/mpUOeqa/j7YNkDYQh7x+M/odd1D
|
||||||
|
KY0bQEQ=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -41,10 +41,12 @@
|
||||||
"intl:extract": "lingui extract",
|
"intl:extract": "lingui extract",
|
||||||
"intl:compile": "lingui compile",
|
"intl:compile": "lingui compile",
|
||||||
"nuke": "rm -rf ./node_modules && rm -rf ./ios && rm -rf ./android",
|
"nuke": "rm -rf ./node_modules && rm -rf ./ios && rm -rf ./android",
|
||||||
"update-extensions": "scripts/updateExtensions.sh"
|
"update-extensions": "bash scripts/updateExtensions.sh",
|
||||||
|
"export": "npx expo export",
|
||||||
|
"make-deploy-bundle": "bash scripts/bundleUpdate.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@atproto/api": "^0.12.0",
|
"@atproto/api": "^0.12.1",
|
||||||
"@bam.tech/react-native-image-resizer": "^3.0.4",
|
"@bam.tech/react-native-image-resizer": "^3.0.4",
|
||||||
"@braintree/sanitize-url": "^6.0.2",
|
"@braintree/sanitize-url": "^6.0.2",
|
||||||
"@emoji-mart/react": "^1.1.1",
|
"@emoji-mart/react": "^1.1.1",
|
||||||
|
|
|
@ -1,8 +1,56 @@
|
||||||
|
diff --git a/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/ImagePickerModule.kt b/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/ImagePickerModule.kt
|
||||||
|
index 3f50f8c..ee47fa1 100644
|
||||||
|
--- a/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/ImagePickerModule.kt
|
||||||
|
+++ b/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/ImagePickerModule.kt
|
||||||
|
@@ -33,7 +33,9 @@ import kotlin.coroutines.resumeWithException
|
||||||
|
// TODO(@bbarthec): rename to ExpoImagePicker
|
||||||
|
private const val moduleName = "ExponentImagePicker"
|
||||||
|
|
||||||
|
+
|
||||||
|
class ImagePickerModule : Module() {
|
||||||
|
+ private var isPickerOpen = false
|
||||||
|
|
||||||
|
override fun definition() = ModuleDefinition {
|
||||||
|
Name(moduleName)
|
||||||
|
@@ -129,6 +131,11 @@ class ImagePickerModule : Module() {
|
||||||
|
options: ImagePickerOptions
|
||||||
|
): Any {
|
||||||
|
return try {
|
||||||
|
+ if(isPickerOpen) {
|
||||||
|
+ return ImagePickerResponse(canceled = true)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ isPickerOpen = true
|
||||||
|
var result = launchPicker(pickerLauncher)
|
||||||
|
if (
|
||||||
|
!options.allowsMultipleSelection &&
|
||||||
|
@@ -143,6 +150,8 @@ class ImagePickerModule : Module() {
|
||||||
|
mediaHandler.readExtras(result.data, options)
|
||||||
|
} catch (cause: OperationCanceledException) {
|
||||||
|
return ImagePickerResponse(canceled = true)
|
||||||
|
+ } finally {
|
||||||
|
+ isPickerOpen = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diff --git a/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/contracts/ImageLibraryContract.kt b/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/contracts/ImageLibraryContract.kt
|
diff --git a/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/contracts/ImageLibraryContract.kt b/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/contracts/ImageLibraryContract.kt
|
||||||
index ff15c91..41aaf12 100644
|
index ff15c91..9763012 100644
|
||||||
--- a/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/contracts/ImageLibraryContract.kt
|
--- a/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/contracts/ImageLibraryContract.kt
|
||||||
+++ b/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/contracts/ImageLibraryContract.kt
|
+++ b/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/contracts/ImageLibraryContract.kt
|
||||||
@@ -26,51 +26,26 @@ import java.io.Serializable
|
@@ -5,12 +5,7 @@ import android.content.ContentResolver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
-import androidx.activity.result.PickVisualMediaRequest
|
||||||
|
-import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
|
||||||
|
-import androidx.activity.result.contract.ActivityResultContracts.PickMultipleVisualMedia
|
||||||
|
import expo.modules.imagepicker.ImagePickerOptions
|
||||||
|
-import expo.modules.imagepicker.MediaTypes
|
||||||
|
-import expo.modules.imagepicker.UNLIMITED_SELECTION
|
||||||
|
import expo.modules.imagepicker.getAllDataUris
|
||||||
|
import expo.modules.imagepicker.toMediaType
|
||||||
|
import expo.modules.kotlin.activityresult.AppContextActivityResultContract
|
||||||
|
@@ -26,51 +21,26 @@ import java.io.Serializable
|
||||||
* @see [androidx.activity.result.contract.ActivityResultContracts.GetMultipleContents]
|
* @see [androidx.activity.result.contract.ActivityResultContracts.GetMultipleContents]
|
||||||
*/
|
*/
|
||||||
internal class ImageLibraryContract(
|
internal class ImageLibraryContract(
|
||||||
|
@ -12,7 +60,7 @@ index ff15c91..41aaf12 100644
|
||||||
private val contentResolver: ContentResolver
|
private val contentResolver: ContentResolver
|
||||||
get() = appContextProvider.appContext.reactContext?.contentResolver
|
get() = appContextProvider.appContext.reactContext?.contentResolver
|
||||||
?: throw Exceptions.ReactContextLost()
|
?: throw Exceptions.ReactContextLost()
|
||||||
|
|
||||||
override fun createIntent(context: Context, input: ImageLibraryContractOptions): Intent {
|
override fun createIntent(context: Context, input: ImageLibraryContractOptions): Intent {
|
||||||
- val request = PickVisualMediaRequest.Builder()
|
- val request = PickVisualMediaRequest.Builder()
|
||||||
- .setMediaType(
|
- .setMediaType(
|
||||||
|
@ -34,7 +82,7 @@ index ff15c91..41aaf12 100644
|
||||||
+ val intent = Intent(Intent.ACTION_GET_CONTENT)
|
+ val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||||
+ .addCategory(Intent.CATEGORY_OPENABLE)
|
+ .addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
+ .setType("image/*")
|
+ .setType("image/*")
|
||||||
|
|
||||||
if (input.options.allowsMultipleSelection) {
|
if (input.options.allowsMultipleSelection) {
|
||||||
- val selectionLimit = input.options.selectionLimit
|
- val selectionLimit = input.options.selectionLimit
|
||||||
-
|
-
|
||||||
|
@ -45,7 +93,7 @@ index ff15c91..41aaf12 100644
|
||||||
+ if(input.options.selectionLimit == 1) {
|
+ if(input.options.selectionLimit == 1) {
|
||||||
+ return intent
|
+ return intent
|
||||||
}
|
}
|
||||||
|
|
||||||
- if (selectionLimit > 1) {
|
- if (selectionLimit > 1) {
|
||||||
- return PickMultipleVisualMedia(selectionLimit).createIntent(context, request)
|
- return PickMultipleVisualMedia(selectionLimit).createIntent(context, request)
|
||||||
- }
|
- }
|
||||||
|
@ -56,9 +104,9 @@ index ff15c91..41aaf12 100644
|
||||||
- }
|
- }
|
||||||
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
- return PickVisualMedia().createIntent(context, request)
|
- return PickVisualMedia().createIntent(context, request)
|
||||||
+ return intent
|
+ return intent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun parseResult(input: ImageLibraryContractOptions, resultCode: Int, intent: Intent?) =
|
override fun parseResult(input: ImageLibraryContractOptions, resultCode: Int, intent: Intent?) =
|
||||||
|
|
|
@ -1,92 +1,19 @@
|
||||||
diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.mm b/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.mm
|
diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m
|
||||||
index 9dca6a5..090bda5 100644
|
index b09e653..d290dab 100644
|
||||||
--- a/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.mm
|
--- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m
|
||||||
+++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.mm
|
+++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m
|
||||||
@@ -266,11 +266,10 @@ - (void)textViewDidChange:(__unused UITextView *)textView
|
@@ -198,6 +198,14 @@ - (void)refreshControlValueChanged
|
||||||
|
[self setCurrentRefreshingState:super.refreshing];
|
||||||
|
_refreshingProgrammatically = NO;
|
||||||
|
|
||||||
- (void)textViewDidChangeSelection:(__unused UITextView *)textView
|
+ if (@available(iOS 17.4, *)) {
|
||||||
{
|
+ if (_currentRefreshingState) {
|
||||||
- if (_lastStringStateWasUpdatedWith && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
|
+ UIImpactFeedbackGenerator *feedbackGenerator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
|
||||||
+ if (![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
|
+ [feedbackGenerator prepare];
|
||||||
[self textViewDidChange:_backedTextInputView];
|
+ [feedbackGenerator impactOccurred];
|
||||||
_ignoreNextTextInputCall = YES;
|
|
||||||
}
|
|
||||||
- _lastStringStateWasUpdatedWith = _backedTextInputView.attributedText;
|
|
||||||
[self textViewProbablyDidChangeSelection];
|
|
||||||
}
|
|
||||||
|
|
||||||
diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputShadowView.mm b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputShadowView.mm
|
|
||||||
index 1f06b79..ab458f3 100644
|
|
||||||
--- a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputShadowView.mm
|
|
||||||
+++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputShadowView.mm
|
|
||||||
@@ -87,7 +87,7 @@ - (void)invalidateContentSize
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
- CGSize maximumSize = self.layoutMetrics.frame.size;
|
|
||||||
+ CGSize maximumSize = self.layoutMetrics.contentFrame.size;
|
|
||||||
|
|
||||||
if (_maximumNumberOfLines == 1) {
|
|
||||||
maximumSize.width = CGFLOAT_MAX;
|
|
||||||
@@ -158,6 +158,8 @@ - (void)uiManagerWillPerformMounting
|
|
||||||
[attributedText insertAttributedString:propertyAttributedText atIndex:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ [self postprocessAttributedText:attributedText];
|
|
||||||
+
|
|
||||||
NSAttributedString *newAttributedText;
|
|
||||||
if (![_previousAttributedText isEqualToAttributedString:attributedText]) {
|
|
||||||
// We have to follow `set prop` pattern:
|
|
||||||
@@ -191,6 +193,52 @@ - (void)uiManagerWillPerformMounting
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
+- (void)postprocessAttributedText:(NSMutableAttributedString *)attributedText
|
|
||||||
+{
|
|
||||||
+ __block CGFloat maximumLineHeight = 0;
|
|
||||||
+
|
|
||||||
+ [attributedText enumerateAttribute:NSParagraphStyleAttributeName
|
|
||||||
+ inRange:NSMakeRange(0, attributedText.length)
|
|
||||||
+ options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
|
|
||||||
+ usingBlock:^(NSParagraphStyle *paragraphStyle, __unused NSRange range, __unused BOOL *stop) {
|
|
||||||
+ if (!paragraphStyle) {
|
|
||||||
+ return;
|
|
||||||
+ }
|
+ }
|
||||||
+
|
|
||||||
+ maximumLineHeight = MAX(paragraphStyle.maximumLineHeight, maximumLineHeight);
|
|
||||||
+ }];
|
|
||||||
+
|
|
||||||
+ if (maximumLineHeight == 0) {
|
|
||||||
+ // `lineHeight` was not specified, nothing to do.
|
|
||||||
+ return;
|
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ __block CGFloat maximumFontLineHeight = 0;
|
if (_onRefresh) {
|
||||||
+
|
_onRefresh(nil);
|
||||||
+ [attributedText enumerateAttribute:NSFontAttributeName
|
}
|
||||||
+ inRange:NSMakeRange(0, attributedText.length)
|
|
||||||
+ options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
|
|
||||||
+ usingBlock:^(UIFont *font, NSRange range, __unused BOOL *stop) {
|
|
||||||
+ if (!font) {
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (maximumFontLineHeight <= font.lineHeight) {
|
|
||||||
+ maximumFontLineHeight = font.lineHeight;
|
|
||||||
+ }
|
|
||||||
+ }];
|
|
||||||
+
|
|
||||||
+ if (maximumLineHeight < maximumFontLineHeight) {
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ CGFloat baseLineOffset = maximumLineHeight / 2.0 - maximumFontLineHeight / 2.0;
|
|
||||||
+
|
|
||||||
+ [attributedText addAttribute:NSBaselineOffsetAttributeName
|
|
||||||
+ value:@(baseLineOffset)
|
|
||||||
+ range:NSMakeRange(0, attributedText.length)];
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
- (NSAttributedString *)measurableAttributedText
|
|
|
@ -1,5 +1,5 @@
|
||||||
# TextInput Patch
|
# RefreshControl Patch
|
||||||
|
|
||||||
Patching `RCTBaseTextShadowInput.mm` from https://github.com/facebook/react-native/pull/38359. This fixes some text
|
Patching `RCTRefreshControl.mm` temporarily to play an impact haptic on refresh when using iOS 17.4 or higher. Since
|
||||||
getting cut off inside the composer. This was merged in December, so we should be able to remove this patch when RN
|
17.4, there has been a regression somewhere causing haptics to not play on iOS on refresh. Should monitor for an update
|
||||||
ships the next release.
|
in the RN repo: https://github.com/facebook/react-native/issues/43388
|
|
@ -0,0 +1,104 @@
|
||||||
|
const crypto = require('crypto')
|
||||||
|
const fs = require('fs')
|
||||||
|
const fsp = fs.promises
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const DIST_DIR = './dist'
|
||||||
|
const BUNDLES_DIR = '/_expo/static/js'
|
||||||
|
const IOS_BUNDLE_DIR = path.join(DIST_DIR, BUNDLES_DIR, '/ios')
|
||||||
|
const ANDROID_BUNDLE_DIR = path.join(DIST_DIR, BUNDLES_DIR, '/android')
|
||||||
|
const METADATA_PATH = path.join(DIST_DIR, '/metadata.json')
|
||||||
|
const DEST_DIR = './bundleTempDir'
|
||||||
|
|
||||||
|
// Weird, don't feel like figuring out _why_ it wants this
|
||||||
|
const METADATA = require(`../${METADATA_PATH}`)
|
||||||
|
const IOS_METADATA_ASSETS = METADATA.fileMetadata.ios.assets
|
||||||
|
const ANDROID_METADATA_ASSETS = METADATA.fileMetadata.android.assets
|
||||||
|
|
||||||
|
const getMd5 = async path => {
|
||||||
|
return new Promise(res => {
|
||||||
|
const hash = crypto.createHash('md5')
|
||||||
|
const rStream = fs.createReadStream(path)
|
||||||
|
rStream.on('data', data => {
|
||||||
|
hash.update(data)
|
||||||
|
})
|
||||||
|
rStream.on('end', () => {
|
||||||
|
res(hash.digest('hex'))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const moveFiles = async () => {
|
||||||
|
console.log('Making directory...')
|
||||||
|
await fsp.mkdir(DEST_DIR)
|
||||||
|
await fsp.mkdir(path.join(DEST_DIR, '/assets'))
|
||||||
|
|
||||||
|
console.log('Getting ios md5...')
|
||||||
|
const iosCurrPath = path.join(
|
||||||
|
IOS_BUNDLE_DIR,
|
||||||
|
(await fsp.readdir(IOS_BUNDLE_DIR))[0],
|
||||||
|
)
|
||||||
|
const iosMd5 = await getMd5(iosCurrPath)
|
||||||
|
const iosNewPath = `bundles/${iosMd5}.bundle`
|
||||||
|
|
||||||
|
console.log('Copying ios bundle...')
|
||||||
|
await fsp.cp(iosCurrPath, path.join(DEST_DIR, iosNewPath))
|
||||||
|
|
||||||
|
console.log('Getting android md5...')
|
||||||
|
const androidCurrPath = path.join(
|
||||||
|
ANDROID_BUNDLE_DIR,
|
||||||
|
(await fsp.readdir(ANDROID_BUNDLE_DIR))[0],
|
||||||
|
)
|
||||||
|
const androidMd5 = await getMd5(androidCurrPath)
|
||||||
|
const androidNewPath = `bundles/${androidMd5}.bundle`
|
||||||
|
|
||||||
|
console.log('Copying android bundle...')
|
||||||
|
await fsp.cp(androidCurrPath, path.join(DEST_DIR, androidNewPath))
|
||||||
|
|
||||||
|
const iosAssets = []
|
||||||
|
const androidAssets = []
|
||||||
|
|
||||||
|
console.log('Getting ios asset md5s and moving them...')
|
||||||
|
for (const asset of IOS_METADATA_ASSETS) {
|
||||||
|
const currPath = path.join(DIST_DIR, asset.path)
|
||||||
|
const md5 = await getMd5(currPath)
|
||||||
|
const withExtPath = `assets/${md5}.${asset.ext}`
|
||||||
|
iosAssets.push(withExtPath)
|
||||||
|
await fsp.cp(currPath, path.join(DEST_DIR, withExtPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Getting android asset md5s and moving them...')
|
||||||
|
for (const asset of ANDROID_METADATA_ASSETS) {
|
||||||
|
const currPath = path.join(DIST_DIR, asset.path)
|
||||||
|
const md5 = await getMd5(currPath)
|
||||||
|
const withExtPath = `assets/${md5}.${asset.ext}`
|
||||||
|
androidAssets.push(withExtPath)
|
||||||
|
await fsp.cp(currPath, path.join(DEST_DIR, withExtPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
version: 0,
|
||||||
|
bundler: 'metro',
|
||||||
|
fileMetadata: {
|
||||||
|
ios: {
|
||||||
|
bundle: iosNewPath,
|
||||||
|
assets: iosAssets,
|
||||||
|
},
|
||||||
|
android: {
|
||||||
|
bundle: androidNewPath,
|
||||||
|
assets: androidAssets,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Writing metadata...')
|
||||||
|
await fsp.writeFile(
|
||||||
|
path.join(DEST_DIR, 'metadata.json'),
|
||||||
|
JSON.stringify(result),
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log('Finished!')
|
||||||
|
console.log('Metadata:', result)
|
||||||
|
}
|
||||||
|
|
||||||
|
moveFiles()
|
|
@ -0,0 +1,26 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
rm -rf bundleTempDir
|
||||||
|
rm -rf bundle.tar.gz
|
||||||
|
|
||||||
|
echo "Creating tarball..."
|
||||||
|
node scripts/bundleUpdate.js
|
||||||
|
|
||||||
|
cd bundleTempDir || exit
|
||||||
|
|
||||||
|
BUNDLE_VERSION=$(date +%s)
|
||||||
|
DEPLOYMENT_URL="https://updates.bsky.app/v1/upload?runtime-version=$RUNTIME_VERSION&bundle-version=$BUNDLE_VERSION"
|
||||||
|
|
||||||
|
tar czvf bundle.tar.gz ./*
|
||||||
|
|
||||||
|
echo "Deploying to $DEPLOYMENT_URL..."
|
||||||
|
|
||||||
|
curl -o - --form "bundle=@./bundle.tar.gz" --user "bsky:$DENIS_API_KEY" --basic "$DEPLOYMENT_URL"
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
rm -rf bundleTempDir
|
||||||
|
rm -rf bundle.tar.gz
|
|
@ -16,6 +16,7 @@ type BreakpointName = keyof typeof breakpoints
|
||||||
const breakpoints: {
|
const breakpoints: {
|
||||||
[key: string]: number
|
[key: string]: number
|
||||||
} = {
|
} = {
|
||||||
|
gtPhone: 500,
|
||||||
gtMobile: 800,
|
gtMobile: 800,
|
||||||
gtTablet: 1300,
|
gtTablet: 1300,
|
||||||
}
|
}
|
||||||
|
@ -26,6 +27,7 @@ function getActiveBreakpoints({width}: {width: number}) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
active: active[active.length - 1],
|
active: active[active.length - 1],
|
||||||
|
gtPhone: active.includes('gtPhone'),
|
||||||
gtMobile: active.includes('gtMobile'),
|
gtMobile: active.includes('gtMobile'),
|
||||||
gtTablet: active.includes('gtTablet'),
|
gtTablet: active.includes('gtTablet'),
|
||||||
}
|
}
|
||||||
|
@ -39,6 +41,7 @@ export const Context = React.createContext<{
|
||||||
theme: themes.Theme
|
theme: themes.Theme
|
||||||
breakpoints: {
|
breakpoints: {
|
||||||
active: BreakpointName | undefined
|
active: BreakpointName | undefined
|
||||||
|
gtPhone: boolean
|
||||||
gtMobile: boolean
|
gtMobile: boolean
|
||||||
gtTablet: boolean
|
gtTablet: boolean
|
||||||
}
|
}
|
||||||
|
@ -47,6 +50,7 @@ export const Context = React.createContext<{
|
||||||
theme: themes.light,
|
theme: themes.light,
|
||||||
breakpoints: {
|
breakpoints: {
|
||||||
active: undefined,
|
active: undefined,
|
||||||
|
gtPhone: false,
|
||||||
gtMobile: false,
|
gtMobile: false,
|
||||||
gtTablet: false,
|
gtTablet: false,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import type {AccessibilityProps} from 'react-native'
|
import type {AccessibilityProps, GestureResponderEvent} from 'react-native'
|
||||||
import {BottomSheetProps} from '@gorhom/bottom-sheet'
|
import {BottomSheetProps} from '@gorhom/bottom-sheet'
|
||||||
|
|
||||||
import {ViewStyleProp} from '#/alf'
|
import {ViewStyleProp} from '#/alf'
|
||||||
|
@ -10,9 +10,15 @@ type A11yProps = Required<AccessibilityProps>
|
||||||
* Mutated by useImperativeHandle to provide a public API for controlling the
|
* Mutated by useImperativeHandle to provide a public API for controlling the
|
||||||
* dialog. The methods here will actually become the handlers defined within
|
* dialog. The methods here will actually become the handlers defined within
|
||||||
* the `Dialog.Outer` component.
|
* the `Dialog.Outer` component.
|
||||||
|
*
|
||||||
|
* `Partial<GestureResponderEvent>` here allows us to add this directly to the
|
||||||
|
* `onPress` prop of a button, for example. If this type was not added, we
|
||||||
|
* would need to create a function to wrap `.open()` with.
|
||||||
*/
|
*/
|
||||||
export type DialogControlRefProps = {
|
export type DialogControlRefProps = {
|
||||||
open: (options?: DialogControlOpenOptions) => void
|
open: (
|
||||||
|
options?: DialogControlOpenOptions & Partial<GestureResponderEvent>,
|
||||||
|
) => void
|
||||||
close: (callback?: () => void) => void
|
close: (callback?: () => void) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import {CenteredView} from 'view/com/util/Views'
|
||||||
|
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
import {View} from 'react-native'
|
||||||
|
import {Button} from '#/components/Button'
|
||||||
|
import {useNavigation} from '@react-navigation/core'
|
||||||
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
|
import {StackActions} from '@react-navigation/native'
|
||||||
|
import {router} from '#/routes'
|
||||||
|
|
||||||
|
export function Error({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
onRetry,
|
||||||
|
}: {
|
||||||
|
title?: string
|
||||||
|
message?: string
|
||||||
|
onRetry?: () => unknown
|
||||||
|
}) {
|
||||||
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
const t = useTheme()
|
||||||
|
const {gtMobile} = useBreakpoints()
|
||||||
|
|
||||||
|
const canGoBack = navigation.canGoBack()
|
||||||
|
const onGoBack = React.useCallback(() => {
|
||||||
|
if (canGoBack) {
|
||||||
|
navigation.goBack()
|
||||||
|
} else {
|
||||||
|
navigation.navigate('HomeTab')
|
||||||
|
|
||||||
|
// Checking the state for routes ensures that web doesn't encounter errors while going back
|
||||||
|
if (navigation.getState()?.routes) {
|
||||||
|
navigation.dispatch(StackActions.push(...router.matchPath('/')))
|
||||||
|
} else {
|
||||||
|
navigation.navigate('HomeTab')
|
||||||
|
navigation.dispatch(StackActions.popToTop())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [navigation, canGoBack])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CenteredView
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
a.align_center,
|
||||||
|
!gtMobile ? a.justify_between : a.gap_5xl,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
{paddingTop: 175, paddingBottom: 110},
|
||||||
|
]}
|
||||||
|
sideBorders>
|
||||||
|
<View style={[a.w_full, a.align_center, a.gap_lg]}>
|
||||||
|
<Text style={[a.font_bold, a.text_3xl]}>{title}</Text>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.text_md,
|
||||||
|
a.text_center,
|
||||||
|
t.atoms.text_contrast_high,
|
||||||
|
{lineHeight: 1.4},
|
||||||
|
gtMobile && {width: 450},
|
||||||
|
]}>
|
||||||
|
{message}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[a.gap_md, gtMobile ? {width: 350} : [a.w_full, a.px_lg]]}>
|
||||||
|
{onRetry && (
|
||||||
|
<Button
|
||||||
|
variant="solid"
|
||||||
|
color="primary"
|
||||||
|
label="Click here"
|
||||||
|
onPress={onRetry}
|
||||||
|
size="large"
|
||||||
|
style={[a.rounded_sm, a.overflow_hidden, {paddingVertical: 10}]}>
|
||||||
|
Retry
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
variant="solid"
|
||||||
|
color={onRetry ? 'secondary' : 'primary'}
|
||||||
|
label="Click here"
|
||||||
|
onPress={onGoBack}
|
||||||
|
size="large"
|
||||||
|
style={[a.rounded_sm, a.overflow_hidden, {paddingVertical: 10}]}>
|
||||||
|
Go Back
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</CenteredView>
|
||||||
|
)
|
||||||
|
}
|
|
@ -104,7 +104,7 @@ export function Default({
|
||||||
}: LabelingServiceProps & ViewStyleProp) {
|
}: LabelingServiceProps & ViewStyleProp) {
|
||||||
return (
|
return (
|
||||||
<Outer style={style}>
|
<Outer style={style}>
|
||||||
<Avatar />
|
<Avatar avatar={labeler.creator.avatar} />
|
||||||
<Content>
|
<Content>
|
||||||
<Title
|
<Title
|
||||||
value={getLabelingServiceTitle({
|
value={getLabelingServiceTitle({
|
||||||
|
|
|
@ -1,26 +1,28 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {Trans, msg} from '@lingui/macro'
|
||||||
|
|
||||||
import {CenteredView} from 'view/com/util/Views'
|
import {CenteredView} from 'view/com/util/Views'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import {Trans} from '@lingui/macro'
|
|
||||||
import {cleanError} from 'lib/strings/errors'
|
import {cleanError} from 'lib/strings/errors'
|
||||||
import {Button} from '#/components/Button'
|
import {Button} from '#/components/Button'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {StackActions} from '@react-navigation/native'
|
import {Error} from '#/components/Error'
|
||||||
import {router} from '#/routes'
|
|
||||||
import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped'
|
|
||||||
|
|
||||||
export function ListFooter({
|
export function ListFooter({
|
||||||
isFetching,
|
isFetching,
|
||||||
isError,
|
isError,
|
||||||
error,
|
error,
|
||||||
onRetry,
|
onRetry,
|
||||||
|
height,
|
||||||
}: {
|
}: {
|
||||||
isFetching: boolean
|
isFetching?: boolean
|
||||||
isError: boolean
|
isError?: boolean
|
||||||
error?: string
|
error?: string
|
||||||
onRetry?: () => Promise<unknown>
|
onRetry?: () => Promise<unknown>
|
||||||
|
height?: number
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
|
@ -29,11 +31,10 @@ export function ListFooter({
|
||||||
style={[
|
style={[
|
||||||
a.w_full,
|
a.w_full,
|
||||||
a.align_center,
|
a.align_center,
|
||||||
a.justify_center,
|
|
||||||
a.border_t,
|
a.border_t,
|
||||||
a.pb_lg,
|
a.pb_lg,
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
{height: 180},
|
{height: height ?? 180, paddingTop: 30},
|
||||||
]}>
|
]}>
|
||||||
{isFetching ? (
|
{isFetching ? (
|
||||||
<Loader size="xl" />
|
<Loader size="xl" />
|
||||||
|
@ -53,11 +54,12 @@ function ListFooterMaybeError({
|
||||||
error,
|
error,
|
||||||
onRetry,
|
onRetry,
|
||||||
}: {
|
}: {
|
||||||
isError: boolean
|
isError?: boolean
|
||||||
error?: string
|
error?: string
|
||||||
onRetry?: () => Promise<unknown>
|
onRetry?: () => Promise<unknown>
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
const {_} = useLingui()
|
||||||
|
|
||||||
if (!isError) return null
|
if (!isError) return null
|
||||||
|
|
||||||
|
@ -83,7 +85,7 @@ function ListFooterMaybeError({
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
label="Press to retry"
|
label={_(msg`Press to retry`)}
|
||||||
style={[
|
style={[
|
||||||
a.align_center,
|
a.align_center,
|
||||||
a.justify_center,
|
a.justify_center,
|
||||||
|
@ -93,7 +95,7 @@ function ListFooterMaybeError({
|
||||||
a.py_sm,
|
a.py_sm,
|
||||||
]}
|
]}
|
||||||
onPress={onRetry}>
|
onPress={onRetry}>
|
||||||
Retry
|
<Trans>Retry</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -128,121 +130,72 @@ export function ListMaybePlaceholder({
|
||||||
isLoading,
|
isLoading,
|
||||||
isEmpty,
|
isEmpty,
|
||||||
isError,
|
isError,
|
||||||
empty,
|
emptyTitle,
|
||||||
error,
|
emptyMessage,
|
||||||
notFoundType = 'page',
|
errorTitle,
|
||||||
|
errorMessage,
|
||||||
|
emptyType = 'page',
|
||||||
onRetry,
|
onRetry,
|
||||||
}: {
|
}: {
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
isEmpty: boolean
|
isEmpty?: boolean
|
||||||
isError: boolean
|
isError?: boolean
|
||||||
empty?: string
|
emptyTitle?: string
|
||||||
error?: string
|
emptyMessage?: string
|
||||||
notFoundType?: 'page' | 'results'
|
errorTitle?: string
|
||||||
|
errorMessage?: string
|
||||||
|
emptyType?: 'page' | 'results'
|
||||||
onRetry?: () => Promise<unknown>
|
onRetry?: () => Promise<unknown>
|
||||||
}) {
|
}) {
|
||||||
const navigation = useNavigationDeduped()
|
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
const {_} = useLingui()
|
||||||
const {gtMobile, gtTablet} = useBreakpoints()
|
const {gtMobile, gtTablet} = useBreakpoints()
|
||||||
|
const {_} = useLingui()
|
||||||
|
|
||||||
const canGoBack = navigation.canGoBack()
|
if (!isLoading && isError) {
|
||||||
const onGoBack = React.useCallback(() => {
|
return (
|
||||||
if (canGoBack) {
|
<Error
|
||||||
navigation.goBack()
|
title={errorTitle ?? _(msg`Oops!`)}
|
||||||
} else {
|
message={errorMessage ?? _(`Something went wrong!`)}
|
||||||
navigation.navigate('HomeTab')
|
onRetry={onRetry}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Checking the state for routes ensures that web doesn't encounter errors while going back
|
if (isLoading) {
|
||||||
if (navigation.getState()?.routes) {
|
return (
|
||||||
navigation.dispatch(StackActions.push(...router.matchPath('/')))
|
<CenteredView
|
||||||
} else {
|
style={[
|
||||||
navigation.navigate('HomeTab')
|
a.flex_1,
|
||||||
navigation.dispatch(StackActions.popToTop())
|
a.align_center,
|
||||||
}
|
!gtMobile ? a.justify_between : a.gap_5xl,
|
||||||
}
|
t.atoms.border_contrast_low,
|
||||||
}, [navigation, canGoBack])
|
{paddingTop: 175, paddingBottom: 110},
|
||||||
|
]}
|
||||||
if (!isEmpty) return null
|
sideBorders={gtMobile}
|
||||||
|
topBorder={!gtTablet}>
|
||||||
return (
|
|
||||||
<CenteredView
|
|
||||||
style={[
|
|
||||||
a.flex_1,
|
|
||||||
a.align_center,
|
|
||||||
!gtMobile ? a.justify_between : a.gap_5xl,
|
|
||||||
t.atoms.border_contrast_low,
|
|
||||||
{paddingTop: 175, paddingBottom: 110},
|
|
||||||
]}
|
|
||||||
sideBorders={gtMobile}
|
|
||||||
topBorder={!gtTablet}>
|
|
||||||
{isLoading ? (
|
|
||||||
<View style={[a.w_full, a.align_center, {top: 100}]}>
|
<View style={[a.w_full, a.align_center, {top: 100}]}>
|
||||||
<Loader size="xl" />
|
<Loader size="xl" />
|
||||||
</View>
|
</View>
|
||||||
) : (
|
</CenteredView>
|
||||||
<>
|
)
|
||||||
<View style={[a.w_full, a.align_center, a.gap_lg]}>
|
}
|
||||||
<Text style={[a.font_bold, a.text_3xl]}>
|
|
||||||
{isError ? (
|
|
||||||
<Trans>Oops!</Trans>
|
|
||||||
) : isEmpty ? (
|
|
||||||
<>
|
|
||||||
{notFoundType === 'results' ? (
|
|
||||||
<Trans>No results found</Trans>
|
|
||||||
) : (
|
|
||||||
<Trans>Page not found</Trans>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : undefined}
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
{isError ? (
|
if (isEmpty) {
|
||||||
<Text
|
return (
|
||||||
style={[a.text_md, a.text_center, t.atoms.text_contrast_high]}>
|
<Error
|
||||||
{error ? error : <Trans>Something went wrong!</Trans>}
|
title={
|
||||||
</Text>
|
emptyTitle ??
|
||||||
) : isEmpty ? (
|
(emptyType === 'results'
|
||||||
<Text
|
? _(msg`No results found`)
|
||||||
style={[a.text_md, a.text_center, t.atoms.text_contrast_high]}>
|
: _(msg`Page not found`))
|
||||||
{empty ? (
|
}
|
||||||
empty
|
message={
|
||||||
) : (
|
emptyMessage ??
|
||||||
<Trans>
|
_(msg`We're sorry! We can't find the page you were looking for.`)
|
||||||
We're sorry! We can't find the page you were looking for.
|
}
|
||||||
</Trans>
|
onRetry={onRetry}
|
||||||
)}
|
/>
|
||||||
</Text>
|
)
|
||||||
) : undefined}
|
}
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[a.gap_md, !gtMobile ? [a.w_full, a.px_lg] : {width: 350}]}>
|
|
||||||
{isError && onRetry && (
|
|
||||||
<Button
|
|
||||||
variant="solid"
|
|
||||||
color="primary"
|
|
||||||
label="Click here"
|
|
||||||
onPress={onRetry}
|
|
||||||
size="large"
|
|
||||||
style={[
|
|
||||||
a.rounded_sm,
|
|
||||||
a.overflow_hidden,
|
|
||||||
{paddingVertical: 10},
|
|
||||||
]}>
|
|
||||||
Retry
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
variant="solid"
|
|
||||||
color={isError && onRetry ? 'secondary' : 'primary'}
|
|
||||||
label="Click here"
|
|
||||||
onPress={onGoBack}
|
|
||||||
size="large"
|
|
||||||
style={[a.rounded_sm, a.overflow_hidden, {paddingVertical: 10}]}>
|
|
||||||
Go Back
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</CenteredView>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
ItemIconProps,
|
ItemIconProps,
|
||||||
} from '#/components/Menu/types'
|
} from '#/components/Menu/types'
|
||||||
import {Button, ButtonText} from '#/components/Button'
|
import {Button, ButtonText} from '#/components/Button'
|
||||||
import {msg} from '@lingui/macro'
|
import {Trans, msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {isNative} from 'platform/detection'
|
import {isNative} from 'platform/detection'
|
||||||
|
|
||||||
|
@ -209,7 +209,9 @@ function Cancel() {
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
onPress={() => control.close()}>
|
onPress={() => control.close()}>
|
||||||
<ButtonText>Cancel</ButtonText>
|
<ButtonText>
|
||||||
|
<Trans>Cancel</Trans>
|
||||||
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,13 @@ import {useLingui} from '@lingui/react'
|
||||||
import {AppBskyLabelerDefs} from '@atproto/api'
|
import {AppBskyLabelerDefs} from '@atproto/api'
|
||||||
|
|
||||||
export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
|
export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
|
||||||
|
import {getLabelingServiceTitle} from '#/lib/moderation'
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme, useBreakpoints} from '#/alf'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {Button, useButtonContext} from '#/components/Button'
|
import {Button, useButtonContext} from '#/components/Button'
|
||||||
import {Divider} from '#/components/Divider'
|
import {Divider} from '#/components/Divider'
|
||||||
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
|
import * as LabelingServiceCard from '#/components/LabelingServiceCard'
|
||||||
|
|
||||||
import {ReportDialogProps} from './types'
|
import {ReportDialogProps} from './types'
|
||||||
|
|
||||||
|
@ -22,31 +23,29 @@ export function SelectLabelerView({
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
const {gtMobile} = useBreakpoints()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.gap_lg]}>
|
<View style={[a.gap_lg]}>
|
||||||
<View style={[a.justify_center, a.gap_sm]}>
|
<View style={[a.justify_center, gtMobile ? a.gap_sm : a.gap_xs]}>
|
||||||
<Text style={[a.text_2xl, a.font_bold]}>
|
<Text style={[a.text_2xl, a.font_bold]}>
|
||||||
<Trans>Select moderation service</Trans>
|
<Trans>Select moderator</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
<Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
||||||
<Trans>Who do you want to send this report to?</Trans>
|
<Trans>To whom would you like to send this report?</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<View style={[a.gap_sm, {marginHorizontal: a.p_md.padding * -1}]}>
|
<View style={[a.gap_xs, {marginHorizontal: a.p_md.padding * -1}]}>
|
||||||
{props.labelers.map(labeler => {
|
{props.labelers.map(labeler => {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
key={labeler.creator.did}
|
key={labeler.creator.did}
|
||||||
label={_(msg`Send report to ${labeler.creator.displayName}`)}
|
label={_(msg`Send report to ${labeler.creator.displayName}`)}
|
||||||
onPress={() => props.onSelectLabeler(labeler.creator.did)}>
|
onPress={() => props.onSelectLabeler(labeler.creator.did)}>
|
||||||
<LabelerButton
|
<LabelerButton labeler={labeler} />
|
||||||
title={labeler.creator.displayName || labeler.creator.handle}
|
|
||||||
description={labeler.creator.description || ''}
|
|
||||||
/>
|
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
@ -56,11 +55,9 @@ export function SelectLabelerView({
|
||||||
}
|
}
|
||||||
|
|
||||||
function LabelerButton({
|
function LabelerButton({
|
||||||
title,
|
labeler,
|
||||||
description,
|
|
||||||
}: {
|
}: {
|
||||||
title: string
|
labeler: AppBskyLabelerDefs.LabelerViewDetailed
|
||||||
description: string
|
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {hovered, pressed} = useButtonContext()
|
const {hovered, pressed} = useButtonContext()
|
||||||
|
@ -75,41 +72,21 @@ function LabelerButton({
|
||||||
}, [t])
|
}, [t])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<LabelingServiceCard.Outer
|
||||||
style={[
|
style={[a.p_md, a.rounded_sm, interacted && styles.interacted]}>
|
||||||
a.w_full,
|
<LabelingServiceCard.Avatar avatar={labeler.creator.avatar} />
|
||||||
a.flex_row,
|
<LabelingServiceCard.Content>
|
||||||
a.align_center,
|
<LabelingServiceCard.Title
|
||||||
a.justify_between,
|
value={getLabelingServiceTitle({
|
||||||
a.p_md,
|
displayName: labeler.creator.displayName,
|
||||||
a.rounded_md,
|
handle: labeler.creator.handle,
|
||||||
{paddingRight: 70},
|
})}
|
||||||
interacted && styles.interacted,
|
|
||||||
]}>
|
|
||||||
<View style={[a.flex_1, a.gap_xs]}>
|
|
||||||
<Text style={[a.text_md, a.font_bold, t.atoms.text_contrast_medium]}>
|
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
<Text style={[a.leading_tight, {maxWidth: 400}]} numberOfLines={3}>
|
|
||||||
{description}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
a.absolute,
|
|
||||||
a.inset_0,
|
|
||||||
a.justify_center,
|
|
||||||
a.pr_md,
|
|
||||||
{left: 'auto'},
|
|
||||||
]}>
|
|
||||||
<ChevronRight
|
|
||||||
size="md"
|
|
||||||
fill={
|
|
||||||
hovered ? t.palette.primary_500 : t.atoms.text_contrast_low.color
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
<Text
|
||||||
</View>
|
style={[t.atoms.text_contrast_medium, a.text_sm, a.font_semibold]}>
|
||||||
|
@{labeler.creator.handle}
|
||||||
|
</Text>
|
||||||
|
</LabelingServiceCard.Content>
|
||||||
|
</LabelingServiceCard.Outer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {DMCA_LINK} from '#/components/ReportDialog/const'
|
||||||
import {Link} from '#/components/Link'
|
import {Link} from '#/components/Link'
|
||||||
export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
|
export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme, useBreakpoints} from '#/alf'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
@ -35,6 +35,7 @@ export function SelectReportOptionView({
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
const {gtMobile} = useBreakpoints()
|
||||||
const allReportOptions = useReportOptions()
|
const allReportOptions = useReportOptions()
|
||||||
const reportOptions = allReportOptions[props.params.type]
|
const reportOptions = allReportOptions[props.params.type]
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ export function SelectReportOptionView({
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<View style={[a.justify_center, a.gap_sm]}>
|
<View style={[a.justify_center, gtMobile ? a.gap_sm : a.gap_xs]}>
|
||||||
<Text style={[a.text_2xl, a.font_bold]}>{i18n.title}</Text>
|
<Text style={[a.text_2xl, a.font_bold]}>{i18n.title}</Text>
|
||||||
<Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
<Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
||||||
{i18n.description}
|
{i18n.description}
|
||||||
|
|
|
@ -264,7 +264,9 @@ export function TagMenu({
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
onPress={() => control.close()}>
|
onPress={() => control.close()}>
|
||||||
<ButtonText>Cancel</ButtonText>
|
<ButtonText>
|
||||||
|
<Trans>Cancel</Trans>
|
||||||
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,48 +1,64 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {Trans, msg} from '@lingui/macro'
|
import {Trans, msg} from '@lingui/macro'
|
||||||
|
import {View} from 'react-native'
|
||||||
|
|
||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
import {Text} from '../Typography'
|
import {Text} from '../Typography'
|
||||||
import {DateInput} from '#/view/com/util/forms/DateInput'
|
import {DateInput} from '#/view/com/util/forms/DateInput'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {
|
import {
|
||||||
|
usePreferencesQuery,
|
||||||
usePreferencesSetBirthDateMutation,
|
usePreferencesSetBirthDateMutation,
|
||||||
UsePreferencesQueryResponse,
|
UsePreferencesQueryResponse,
|
||||||
} from '#/state/queries/preferences'
|
} from '#/state/queries/preferences'
|
||||||
import {Button, ButtonText} from '../Button'
|
import {Button, ButtonIcon, ButtonText} from '../Button'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
|
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
import {ActivityIndicator, View} from 'react-native'
|
|
||||||
import {isIOS, isWeb} from '#/platform/detection'
|
import {isIOS, isWeb} from '#/platform/detection'
|
||||||
|
import {Loader} from '#/components/Loader'
|
||||||
|
|
||||||
export function BirthDateSettingsDialog({
|
export function BirthDateSettingsDialog({
|
||||||
control,
|
control,
|
||||||
preferences,
|
|
||||||
}: {
|
}: {
|
||||||
control: Dialog.DialogControlProps
|
control: Dialog.DialogControlProps
|
||||||
preferences: UsePreferencesQueryResponse | undefined
|
|
||||||
}) {
|
}) {
|
||||||
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {isPending, isError, error, mutateAsync} =
|
const {isLoading, error, data: preferences} = usePreferencesQuery()
|
||||||
usePreferencesSetBirthDateMutation()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control}>
|
<Dialog.Outer control={control}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|
||||||
<Dialog.ScrollableInner label={_(msg`My Birthday`)}>
|
<Dialog.ScrollableInner label={_(msg`My Birthday`)}>
|
||||||
{preferences && !isPending ? (
|
<View style={[a.gap_sm, a.pb_lg]}>
|
||||||
<BirthdayInner
|
<Text style={[a.text_2xl, a.font_bold]}>
|
||||||
control={control}
|
<Trans>My Birthday</Trans>
|
||||||
preferences={preferences}
|
</Text>
|
||||||
isError={isError}
|
<Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
||||||
error={error}
|
<Trans>This information is not shared with other users.</Trans>
|
||||||
setBirthDate={mutateAsync}
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{isLoading ? (
|
||||||
|
<Loader size="xl" />
|
||||||
|
) : error || !preferences ? (
|
||||||
|
<ErrorMessage
|
||||||
|
message={
|
||||||
|
error?.toString() ||
|
||||||
|
_(
|
||||||
|
msg`We were unable to load your birth date preferences. Please try again.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
style={[a.rounded_sm]}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ActivityIndicator size="large" style={a.my_5xl} />
|
<BirthdayInner control={control} preferences={preferences} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Dialog.Close />
|
||||||
</Dialog.ScrollableInner>
|
</Dialog.ScrollableInner>
|
||||||
</Dialog.Outer>
|
</Dialog.Outer>
|
||||||
)
|
)
|
||||||
|
@ -51,20 +67,18 @@ export function BirthDateSettingsDialog({
|
||||||
function BirthdayInner({
|
function BirthdayInner({
|
||||||
control,
|
control,
|
||||||
preferences,
|
preferences,
|
||||||
isError,
|
|
||||||
error,
|
|
||||||
setBirthDate,
|
|
||||||
}: {
|
}: {
|
||||||
control: Dialog.DialogControlProps
|
control: Dialog.DialogControlProps
|
||||||
preferences: UsePreferencesQueryResponse
|
preferences: UsePreferencesQueryResponse
|
||||||
isError: boolean
|
|
||||||
error: unknown
|
|
||||||
setBirthDate: (args: {birthDate: Date}) => Promise<unknown>
|
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const [date, setDate] = React.useState(preferences.birthDate || new Date())
|
const [date, setDate] = React.useState(preferences.birthDate || new Date())
|
||||||
const t = useTheme()
|
const {
|
||||||
|
isPending,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
mutateAsync: setBirthDate,
|
||||||
|
} = usePreferencesSetBirthDateMutation()
|
||||||
const hasChanged = date !== preferences.birthDate
|
const hasChanged = date !== preferences.birthDate
|
||||||
|
|
||||||
const onSave = React.useCallback(async () => {
|
const onSave = React.useCallback(async () => {
|
||||||
|
@ -74,21 +88,13 @@ function BirthdayInner({
|
||||||
await setBirthDate({birthDate: date})
|
await setBirthDate({birthDate: date})
|
||||||
}
|
}
|
||||||
control.close()
|
control.close()
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
logger.error(`setBirthDate failed`, {message: e})
|
logger.error(`setBirthDate failed`, {message: e.message})
|
||||||
}
|
}
|
||||||
}, [date, setBirthDate, control, hasChanged])
|
}, [date, setBirthDate, control, hasChanged])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={a.gap_lg} testID="birthDateSettingsDialog">
|
<View style={a.gap_lg} testID="birthDateSettingsDialog">
|
||||||
<View style={[a.gap_sm]}>
|
|
||||||
<Text style={[a.text_2xl, a.font_bold]}>
|
|
||||||
<Trans>My Birthday</Trans>
|
|
||||||
</Text>
|
|
||||||
<Text style={t.atoms.text_contrast_medium}>
|
|
||||||
<Trans>This information is not shared with other users.</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={isIOS && [a.w_full, a.align_center]}>
|
<View style={isIOS && [a.w_full, a.align_center]}>
|
||||||
<DateInput
|
<DateInput
|
||||||
handleAsUTC
|
handleAsUTC
|
||||||
|
@ -103,6 +109,7 @@ function BirthdayInner({
|
||||||
accessibilityLabelledBy="birthDate"
|
accessibilityLabelledBy="birthDate"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{isError ? (
|
{isError ? (
|
||||||
<ErrorMessage message={cleanError(error)} style={[a.rounded_sm]} />
|
<ErrorMessage message={cleanError(error)} style={[a.rounded_sm]} />
|
||||||
) : undefined}
|
) : undefined}
|
||||||
|
@ -110,13 +117,14 @@ function BirthdayInner({
|
||||||
<View style={isWeb && [a.flex_row, a.justify_end]}>
|
<View style={isWeb && [a.flex_row, a.justify_end]}>
|
||||||
<Button
|
<Button
|
||||||
label={hasChanged ? _(msg`Save birthday`) : _(msg`Done`)}
|
label={hasChanged ? _(msg`Save birthday`) : _(msg`Done`)}
|
||||||
size={isWeb ? 'small' : 'medium'}
|
size="medium"
|
||||||
onPress={onSave}
|
onPress={onSave}
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="primary">
|
color="primary">
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
{hasChanged ? <Trans>Save</Trans> : <Trans>Done</Trans>}
|
{hasChanged ? <Trans>Save</Trans> : <Trans>Done</Trans>}
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
|
{isPending && <ButtonIcon icon={Loader} />}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
} from '#/state/queries/preferences'
|
} from '#/state/queries/preferences'
|
||||||
import {getLabelStrings} from '#/lib/moderation/useLabelInfo'
|
import {getLabelStrings} from '#/lib/moderation/useLabelInfo'
|
||||||
|
|
||||||
import {useTheme, atoms as a} from '#/alf'
|
import {useTheme, atoms as a, useBreakpoints} from '#/alf'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {InlineLink} from '#/components/Link'
|
import {InlineLink} from '#/components/Link'
|
||||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '../icons/CircleInfo'
|
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '../icons/CircleInfo'
|
||||||
|
@ -29,6 +29,7 @@ export function ModerationLabelPref({
|
||||||
}) {
|
}) {
|
||||||
const {_, i18n} = useLingui()
|
const {_, i18n} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
const {gtPhone} = useBreakpoints()
|
||||||
|
|
||||||
const isGlobalLabel = !labelValueDefinition.definedBy
|
const isGlobalLabel = !labelValueDefinition.definedBy
|
||||||
const {identifier} = labelValueDefinition
|
const {identifier} = labelValueDefinition
|
||||||
|
@ -57,6 +58,7 @@ export function ModerationLabelPref({
|
||||||
adultOnly && !preferences?.moderationPrefs.adultContentEnabled
|
adultOnly && !preferences?.moderationPrefs.adultContentEnabled
|
||||||
// are there any reasons we cant configure this label here?
|
// are there any reasons we cant configure this label here?
|
||||||
const cantConfigure = isGlobalLabel || adultDisabled
|
const cantConfigure = isGlobalLabel || adultDisabled
|
||||||
|
const showConfig = !disabled && (gtPhone || !cantConfigure)
|
||||||
|
|
||||||
// adjust the pref based on whether warn is available
|
// adjust the pref based on whether warn is available
|
||||||
let prefAdjusted = pref
|
let prefAdjusted = pref
|
||||||
|
@ -85,9 +87,19 @@ export function ModerationLabelPref({
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.flex_row, a.gap_sm, a.px_lg, a.py_lg, a.justify_between]}>
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_row,
|
||||||
|
a.gap_md,
|
||||||
|
a.px_lg,
|
||||||
|
a.py_lg,
|
||||||
|
a.justify_between,
|
||||||
|
a.flex_wrap,
|
||||||
|
]}>
|
||||||
<View style={[a.gap_xs, a.flex_1]}>
|
<View style={[a.gap_xs, a.flex_1]}>
|
||||||
<Text style={[a.font_bold]}>{labelStrings.name}</Text>
|
<Text style={[a.font_bold, gtPhone ? a.text_sm : a.text_md]}>
|
||||||
|
{labelStrings.name}
|
||||||
|
</Text>
|
||||||
<Text style={[t.atoms.text_contrast_medium, a.leading_snug]}>
|
<Text style={[t.atoms.text_contrast_medium, a.leading_snug]}>
|
||||||
{labelStrings.description}
|
{labelStrings.description}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -113,40 +125,51 @@ export function ModerationLabelPref({
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
{disabled ? (
|
|
||||||
<></>
|
{showConfig && (
|
||||||
) : cantConfigure ? (
|
<View style={[gtPhone ? undefined : a.w_full]}>
|
||||||
<View style={[{minHeight: 35}, a.px_sm, a.py_md]}>
|
{cantConfigure ? (
|
||||||
<Text style={[a.font_bold, t.atoms.text_contrast_medium]}>
|
<View
|
||||||
{currentPrefLabel}
|
style={[
|
||||||
</Text>
|
{minHeight: 35},
|
||||||
</View>
|
a.px_md,
|
||||||
) : (
|
a.py_md,
|
||||||
<View style={[{minHeight: 35}]}>
|
a.rounded_sm,
|
||||||
<ToggleButton.Group
|
a.border,
|
||||||
label={_(
|
t.atoms.border_contrast_low,
|
||||||
msg`Configure content filtering setting for category: ${labelStrings.name.toLowerCase()}`,
|
]}>
|
||||||
)}
|
<Text style={[a.font_bold, t.atoms.text_contrast_low]}>
|
||||||
values={[prefAdjusted]}
|
{currentPrefLabel}
|
||||||
onChange={newPref =>
|
</Text>
|
||||||
mutate({
|
</View>
|
||||||
label: identifier,
|
) : (
|
||||||
visibility: newPref[0] as LabelPreference,
|
<View style={[{minHeight: 35}]}>
|
||||||
labelerDid,
|
<ToggleButton.Group
|
||||||
})
|
label={_(
|
||||||
}>
|
msg`Configure content filtering setting for category: ${labelStrings.name.toLowerCase()}`,
|
||||||
<ToggleButton.Button name="ignore" label={ignoreLabel}>
|
)}
|
||||||
{ignoreLabel}
|
values={[prefAdjusted]}
|
||||||
</ToggleButton.Button>
|
onChange={newPref =>
|
||||||
{canWarn && (
|
mutate({
|
||||||
<ToggleButton.Button name="warn" label={warnLabel}>
|
label: identifier,
|
||||||
{warnLabel}
|
visibility: newPref[0] as LabelPreference,
|
||||||
</ToggleButton.Button>
|
labelerDid,
|
||||||
)}
|
})
|
||||||
<ToggleButton.Button name="hide" label={hideLabel}>
|
}>
|
||||||
{hideLabel}
|
<ToggleButton.Button name="ignore" label={ignoreLabel}>
|
||||||
</ToggleButton.Button>
|
{ignoreLabel}
|
||||||
</ToggleButton.Group>
|
</ToggleButton.Button>
|
||||||
|
{canWarn && (
|
||||||
|
<ToggleButton.Button name="warn" label={warnLabel}>
|
||||||
|
{warnLabel}
|
||||||
|
</ToggleButton.Button>
|
||||||
|
)}
|
||||||
|
<ToggleButton.Button name="hide" label={hideLabel}>
|
||||||
|
{hideLabel}
|
||||||
|
</ToggleButton.Button>
|
||||||
|
</ToggleButton.Group>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -4,6 +4,23 @@ import TLDs from 'tlds'
|
||||||
import psl from 'psl'
|
import psl from 'psl'
|
||||||
|
|
||||||
export const BSKY_APP_HOST = 'https://bsky.app'
|
export const BSKY_APP_HOST = 'https://bsky.app'
|
||||||
|
const BSKY_TRUSTED_HOSTS = [
|
||||||
|
'bsky.app',
|
||||||
|
'bsky.social',
|
||||||
|
'blueskyweb.xyz',
|
||||||
|
'blueskyweb.zendesk.com',
|
||||||
|
...(__DEV__ ? ['localhost:19006', 'localhost:8100'] : []),
|
||||||
|
]
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This will allow any BSKY_TRUSTED_HOSTS value by itself or with a subdomain.
|
||||||
|
* It will also allow relative paths like /profile as well as #.
|
||||||
|
*/
|
||||||
|
const TRUSTED_REGEX = new RegExp(
|
||||||
|
`^(http(s)?://(([\\w-]+\\.)?${BSKY_TRUSTED_HOSTS.join(
|
||||||
|
'|([\\w-]+\\.)?',
|
||||||
|
)})|/|#)`,
|
||||||
|
)
|
||||||
|
|
||||||
export function isValidDomain(str: string): boolean {
|
export function isValidDomain(str: string): boolean {
|
||||||
return !!TLDs.find(tld => {
|
return !!TLDs.find(tld => {
|
||||||
|
@ -86,6 +103,10 @@ export function isExternalUrl(url: string): boolean {
|
||||||
return external || rss
|
return external || rss
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isTrustedUrl(url: string): boolean {
|
||||||
|
return TRUSTED_REGEX.test(url)
|
||||||
|
}
|
||||||
|
|
||||||
export function isBskyPostUrl(url: string): boolean {
|
export function isBskyPostUrl(url: string): boolean {
|
||||||
if (isBskyAppUrl(url)) {
|
if (isBskyAppUrl(url)) {
|
||||||
try {
|
try {
|
||||||
|
@ -163,8 +184,8 @@ export function feedUriToHref(url: string): string {
|
||||||
export function linkRequiresWarning(uri: string, label: string) {
|
export function linkRequiresWarning(uri: string, label: string) {
|
||||||
const labelDomain = labelToDomain(label)
|
const labelDomain = labelToDomain(label)
|
||||||
|
|
||||||
// If the uri started with a / we know it is internal.
|
// We should trust any relative URL or a # since we know it links to internal content
|
||||||
if (isRelativeUrl(uri)) {
|
if (isRelativeUrl(uri) || uri === '#') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,18 +197,11 @@ export function linkRequiresWarning(uri: string, label: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const host = urip.hostname.toLowerCase()
|
const host = urip.hostname.toLowerCase()
|
||||||
// Hosts that end with bsky.app or bsky.social should be trusted by default.
|
if (isTrustedUrl(uri)) {
|
||||||
if (
|
// if this is a link to internal content, warn if it represents itself as a URL to another app
|
||||||
host.endsWith('bsky.app') ||
|
|
||||||
host.endsWith('bsky.social') ||
|
|
||||||
host.endsWith('blueskyweb.xyz')
|
|
||||||
) {
|
|
||||||
// if this is a link to internal content,
|
|
||||||
// warn if it represents itself as a URL to another app
|
|
||||||
return !!labelDomain && labelDomain !== host && isPossiblyAUrl(labelDomain)
|
return !!labelDomain && labelDomain !== host && isPossiblyAUrl(labelDomain)
|
||||||
} else {
|
} else {
|
||||||
// if this is a link to external content,
|
// if this is a link to external content, warn if the label doesnt match the target
|
||||||
// warn if the label doesnt match the target
|
|
||||||
if (!labelDomain) {
|
if (!labelDomain) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,12 +244,12 @@ msgstr "Avançat"
|
||||||
|
|
||||||
#: src/view/screens/Feeds.tsx:666
|
#: src/view/screens/Feeds.tsx:666
|
||||||
msgid "All the feeds you've saved, right in one place."
|
msgid "All the feeds you've saved, right in one place."
|
||||||
msgstr ""
|
msgstr "Tots els canals que has desat, en un sol lloc."
|
||||||
|
|
||||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:221
|
#: src/view/com/auth/login/ForgotPasswordForm.tsx:221
|
||||||
#: src/view/com/modals/ChangePassword.tsx:168
|
#: src/view/com/modals/ChangePassword.tsx:168
|
||||||
msgid "Already have a code?"
|
msgid "Already have a code?"
|
||||||
msgstr ""
|
msgstr "Ja tens un codi?"
|
||||||
|
|
||||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:98
|
#: src/view/com/auth/login/ChooseAccountForm.tsx:98
|
||||||
msgid "Already signed in as @{0}"
|
msgid "Already signed in as @{0}"
|
||||||
|
@ -287,7 +287,7 @@ msgstr "i"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:32
|
#: src/screens/Onboarding/index.tsx:32
|
||||||
msgid "Animals"
|
msgid "Animals"
|
||||||
msgstr ""
|
msgstr "Animals"
|
||||||
|
|
||||||
#: src/view/screens/LanguageSettings.tsx:95
|
#: src/view/screens/LanguageSettings.tsx:95
|
||||||
msgid "App Language"
|
msgid "App Language"
|
||||||
|
@ -366,7 +366,7 @@ msgstr "Estàs escrivint en <0>{0}</0>?"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:26
|
#: src/screens/Onboarding/index.tsx:26
|
||||||
msgid "Art"
|
msgid "Art"
|
||||||
msgstr ""
|
msgstr "Art"
|
||||||
|
|
||||||
#: src/view/com/modals/SelfLabel.tsx:123
|
#: src/view/com/modals/SelfLabel.tsx:123
|
||||||
msgid "Artistic or non-erotic nudity."
|
msgid "Artistic or non-erotic nudity."
|
||||||
|
@ -393,7 +393,7 @@ msgstr "Endarrere"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:136
|
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:136
|
||||||
msgid "Based on your interest in {interestsText}"
|
msgid "Based on your interest in {interestsText}"
|
||||||
msgstr ""
|
msgstr "Segons els teus interessos en {interestsText}"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:523
|
#: src/view/screens/Settings/index.tsx:523
|
||||||
msgid "Basics"
|
msgid "Basics"
|
||||||
|
@ -472,7 +472,7 @@ msgstr "Bluesky"
|
||||||
|
|
||||||
#: src/view/com/auth/server-input/index.tsx:150
|
#: src/view/com/auth/server-input/index.tsx:150
|
||||||
msgid "Bluesky is an open network where you can choose your hosting provider. Custom hosting is now available in beta for developers."
|
msgid "Bluesky is an open network where you can choose your hosting provider. Custom hosting is now available in beta for developers."
|
||||||
msgstr ""
|
msgstr "Bluesky és una xarxa oberta on pots escollir el teu proveïdor d'allotjament. L'allotjament personalitzat està disponible en beta per a desenvolupadors"
|
||||||
|
|
||||||
#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:80
|
#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:80
|
||||||
#: src/view/com/auth/onboarding/WelcomeMobile.tsx:80
|
#: src/view/com/auth/onboarding/WelcomeMobile.tsx:80
|
||||||
|
@ -503,7 +503,7 @@ msgstr "Bluesky no mostrarà el teu perfil ni les publicacions als usuaris que n
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:33
|
#: src/screens/Onboarding/index.tsx:33
|
||||||
msgid "Books"
|
msgid "Books"
|
||||||
msgstr ""
|
msgstr "Llibres"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:859
|
#: src/view/screens/Settings/index.tsx:859
|
||||||
msgid "Build version {0} {1}"
|
msgid "Build version {0} {1}"
|
||||||
|
@ -631,11 +631,11 @@ msgstr "Canvia el meu correu"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:732
|
#: src/view/screens/Settings/index.tsx:732
|
||||||
msgid "Change password"
|
msgid "Change password"
|
||||||
msgstr ""
|
msgstr "Canvia la contrasenya"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:741
|
#: src/view/screens/Settings/index.tsx:741
|
||||||
msgid "Change Password"
|
msgid "Change Password"
|
||||||
msgstr ""
|
msgstr "Canvia la contrasenya"
|
||||||
|
|
||||||
#: src/view/com/composer/select-language/SuggestedLanguage.tsx:73
|
#: src/view/com/composer/select-language/SuggestedLanguage.tsx:73
|
||||||
msgid "Change post language to {0}"
|
msgid "Change post language to {0}"
|
||||||
|
@ -643,7 +643,7 @@ msgstr "Canvia l'idioma de la publicació a {0}"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:733
|
#: src/view/screens/Settings/index.tsx:733
|
||||||
msgid "Change your Bluesky password"
|
msgid "Change your Bluesky password"
|
||||||
msgstr ""
|
msgstr "Canvia la teva contrasenya de Bluesky"
|
||||||
|
|
||||||
#: src/view/com/modals/ChangeEmail.tsx:109
|
#: src/view/com/modals/ChangeEmail.tsx:109
|
||||||
msgid "Change Your Email"
|
msgid "Change Your Email"
|
||||||
|
@ -652,7 +652,7 @@ msgstr "Canvia el teu correu"
|
||||||
#: src/screens/Deactivated.tsx:72
|
#: src/screens/Deactivated.tsx:72
|
||||||
#: src/screens/Deactivated.tsx:76
|
#: src/screens/Deactivated.tsx:76
|
||||||
msgid "Check my status"
|
msgid "Check my status"
|
||||||
msgstr ""
|
msgstr "Comprova el meu estat"
|
||||||
|
|
||||||
#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:121
|
#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:121
|
||||||
msgid "Check out some recommended feeds. Tap + to add them to your list of pinned feeds."
|
msgid "Check out some recommended feeds. Tap + to add them to your list of pinned feeds."
|
||||||
|
@ -680,7 +680,7 @@ msgstr "Tria un servei"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFinished.tsx:135
|
#: src/screens/Onboarding/StepFinished.tsx:135
|
||||||
msgid "Choose the algorithms that power your custom feeds."
|
msgid "Choose the algorithms that power your custom feeds."
|
||||||
msgstr ""
|
msgstr "Tria els algoritmes que alimentaran els teus canals personalitzats."
|
||||||
|
|
||||||
#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:83
|
#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:83
|
||||||
#: src/view/com/auth/onboarding/WelcomeMobile.tsx:83
|
#: src/view/com/auth/onboarding/WelcomeMobile.tsx:83
|
||||||
|
@ -689,7 +689,7 @@ msgstr "Tria els algoritmes que potenciaran la teva experiència amb els canals
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:103
|
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:103
|
||||||
msgid "Choose your main feeds"
|
msgid "Choose your main feeds"
|
||||||
msgstr ""
|
msgstr "Tria els teus canals principals"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step1.tsx:196
|
#: src/view/com/auth/create/Step1.tsx:196
|
||||||
msgid "Choose your password"
|
msgid "Choose your password"
|
||||||
|
@ -732,12 +732,12 @@ msgstr ""
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:35
|
#: src/screens/Onboarding/index.tsx:35
|
||||||
msgid "Climate"
|
msgid "Climate"
|
||||||
msgstr ""
|
msgstr "Clima"
|
||||||
|
|
||||||
#: src/view/com/modals/ChangePassword.tsx:265
|
#: src/view/com/modals/ChangePassword.tsx:265
|
||||||
#: src/view/com/modals/ChangePassword.tsx:268
|
#: src/view/com/modals/ChangePassword.tsx:268
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr ""
|
msgstr "Tanca"
|
||||||
|
|
||||||
#: src/components/Dialog/index.web.tsx:84
|
#: src/components/Dialog/index.web.tsx:84
|
||||||
#: src/components/Dialog/index.web.tsx:198
|
#: src/components/Dialog/index.web.tsx:198
|
||||||
|
@ -790,11 +790,11 @@ msgstr "Plega la llista d'usuaris per una notificació concreta"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:41
|
#: src/screens/Onboarding/index.tsx:41
|
||||||
msgid "Comedy"
|
msgid "Comedy"
|
||||||
msgstr ""
|
msgstr "Comèdia"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:27
|
#: src/screens/Onboarding/index.tsx:27
|
||||||
msgid "Comics"
|
msgid "Comics"
|
||||||
msgstr ""
|
msgstr "Còmics"
|
||||||
|
|
||||||
#: src/Navigation.tsx:229
|
#: src/Navigation.tsx:229
|
||||||
#: src/view/screens/CommunityGuidelines.tsx:32
|
#: src/view/screens/CommunityGuidelines.tsx:32
|
||||||
|
@ -803,7 +803,7 @@ msgstr "Directrius de la comunitat"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFinished.tsx:148
|
#: src/screens/Onboarding/StepFinished.tsx:148
|
||||||
msgid "Complete onboarding and start using your account"
|
msgid "Complete onboarding and start using your account"
|
||||||
msgstr ""
|
msgstr "Finalitza el registre i comença a utilitzar el teu compte"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step3.tsx:73
|
#: src/view/com/auth/create/Step3.tsx:73
|
||||||
msgid "Complete the challenge"
|
msgid "Complete the challenge"
|
||||||
|
@ -819,7 +819,7 @@ msgstr "Redacta una resposta"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepModeration/ModerationOption.tsx:67
|
#: src/screens/Onboarding/StepModeration/ModerationOption.tsx:67
|
||||||
msgid "Configure content filtering setting for category: {0}"
|
msgid "Configure content filtering setting for category: {0}"
|
||||||
msgstr ""
|
msgstr "Configura els filtres de continguts per la categoria: {0}"
|
||||||
|
|
||||||
#: src/components/Prompt.tsx:124
|
#: src/components/Prompt.tsx:124
|
||||||
#: src/view/com/modals/AppealLabel.tsx:98
|
#: src/view/com/modals/AppealLabel.tsx:98
|
||||||
|
@ -914,19 +914,19 @@ msgstr "Continua"
|
||||||
#: src/screens/Onboarding/StepModeration/index.tsx:115
|
#: src/screens/Onboarding/StepModeration/index.tsx:115
|
||||||
#: src/screens/Onboarding/StepTopicalFeeds.tsx:111
|
#: src/screens/Onboarding/StepTopicalFeeds.tsx:111
|
||||||
msgid "Continue to next step"
|
msgid "Continue to next step"
|
||||||
msgstr ""
|
msgstr "Continua"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:167
|
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:167
|
||||||
msgid "Continue to the next step"
|
msgid "Continue to the next step"
|
||||||
msgstr ""
|
msgstr "Continua"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:191
|
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:191
|
||||||
msgid "Continue to the next step without following any accounts"
|
msgid "Continue to the next step without following any accounts"
|
||||||
msgstr ""
|
msgstr "Continua sense seguir cap compte"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:44
|
#: src/screens/Onboarding/index.tsx:44
|
||||||
msgid "Cooking"
|
msgid "Cooking"
|
||||||
msgstr ""
|
msgstr "Cuina"
|
||||||
|
|
||||||
#: src/view/com/modals/AddAppPasswords.tsx:195
|
#: src/view/com/modals/AddAppPasswords.tsx:195
|
||||||
#: src/view/com/modals/InviteCodes.tsx:182
|
#: src/view/com/modals/InviteCodes.tsx:182
|
||||||
|
@ -1027,12 +1027,12 @@ msgstr "Crea una targeta amb una minuatura. La targeta enllaça a {url}"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:29
|
#: src/screens/Onboarding/index.tsx:29
|
||||||
msgid "Culture"
|
msgid "Culture"
|
||||||
msgstr ""
|
msgstr "Cultura"
|
||||||
|
|
||||||
#: src/view/com/auth/server-input/index.tsx:95
|
#: src/view/com/auth/server-input/index.tsx:95
|
||||||
#: src/view/com/auth/server-input/index.tsx:96
|
#: src/view/com/auth/server-input/index.tsx:96
|
||||||
msgid "Custom"
|
msgid "Custom"
|
||||||
msgstr ""
|
msgstr "Personalitzat"
|
||||||
|
|
||||||
#: src/view/com/modals/ChangeHandle.tsx:389
|
#: src/view/com/modals/ChangeHandle.tsx:389
|
||||||
msgid "Custom domain"
|
msgid "Custom domain"
|
||||||
|
@ -1041,7 +1041,7 @@ msgstr "Domini personalitzat"
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:106
|
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:106
|
||||||
#: src/view/screens/Feeds.tsx:692
|
#: src/view/screens/Feeds.tsx:692
|
||||||
msgid "Custom feeds built by the community bring you new experiences and help you find the content you love."
|
msgid "Custom feeds built by the community bring you new experiences and help you find the content you love."
|
||||||
msgstr ""
|
msgstr "Els canals personalitzats fets per la comunitat et porten noves experiències i t'ajuden a trobar contingut que t'agradarà."
|
||||||
|
|
||||||
#: src/view/screens/PreferencesExternalEmbeds.tsx:55
|
#: src/view/screens/PreferencesExternalEmbeds.tsx:55
|
||||||
msgid "Customize media from external sites."
|
msgid "Customize media from external sites."
|
||||||
|
@ -1062,7 +1062,7 @@ msgstr "Mode fosc"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:498
|
#: src/view/screens/Settings/index.tsx:498
|
||||||
msgid "Dark Theme"
|
msgid "Dark Theme"
|
||||||
msgstr ""
|
msgstr "Tema fosc"
|
||||||
|
|
||||||
#: src/view/screens/Debug.tsx:83
|
#: src/view/screens/Debug.tsx:83
|
||||||
msgid "Debug panel"
|
msgid "Debug panel"
|
||||||
|
@ -1096,7 +1096,7 @@ msgstr "Elimina el meu compte"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:784
|
#: src/view/screens/Settings/index.tsx:784
|
||||||
msgid "Delete My Account…"
|
msgid "Delete My Account…"
|
||||||
msgstr ""
|
msgstr "Elimina el meu compte…"
|
||||||
|
|
||||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:317
|
#: src/view/com/util/forms/PostDropdownBtn.tsx:317
|
||||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:326
|
#: src/view/com/util/forms/PostDropdownBtn.tsx:326
|
||||||
|
@ -1136,7 +1136,7 @@ msgstr "Vols dir alguna cosa?"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:504
|
#: src/view/screens/Settings/index.tsx:504
|
||||||
msgid "Dim"
|
msgid "Dim"
|
||||||
msgstr ""
|
msgstr "Tènue"
|
||||||
|
|
||||||
#: src/view/com/composer/Composer.tsx:151
|
#: src/view/com/composer/Composer.tsx:151
|
||||||
msgid "Discard"
|
msgid "Discard"
|
||||||
|
@ -1161,7 +1161,7 @@ msgstr "Descobreix nous canals personalitzats"
|
||||||
|
|
||||||
#: src/view/screens/Feeds.tsx:689
|
#: src/view/screens/Feeds.tsx:689
|
||||||
msgid "Discover New Feeds"
|
msgid "Discover New Feeds"
|
||||||
msgstr ""
|
msgstr "Descobreix nous canals"
|
||||||
|
|
||||||
#: src/view/com/modals/EditProfile.tsx:192
|
#: src/view/com/modals/EditProfile.tsx:192
|
||||||
msgid "Display name"
|
msgid "Display name"
|
||||||
|
@ -1218,20 +1218,20 @@ msgstr "Fes doble toc per iniciar la sessió"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:755
|
#: src/view/screens/Settings/index.tsx:755
|
||||||
msgid "Download Bluesky account data (repository)"
|
msgid "Download Bluesky account data (repository)"
|
||||||
msgstr ""
|
msgstr "Descarrega les dades del compte de Bluesky (repositori)"
|
||||||
|
|
||||||
#: src/view/screens/Settings/ExportCarDialog.tsx:59
|
#: src/view/screens/Settings/ExportCarDialog.tsx:59
|
||||||
#: src/view/screens/Settings/ExportCarDialog.tsx:63
|
#: src/view/screens/Settings/ExportCarDialog.tsx:63
|
||||||
msgid "Download CAR file"
|
msgid "Download CAR file"
|
||||||
msgstr ""
|
msgstr "Descarrega el fitxer CAR"
|
||||||
|
|
||||||
#: src/view/com/composer/text-input/TextInput.web.tsx:249
|
#: src/view/com/composer/text-input/TextInput.web.tsx:249
|
||||||
msgid "Drop to add images"
|
msgid "Drop to add images"
|
||||||
msgstr ""
|
msgstr "Deixa anar per afegir imatges"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:111
|
#: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:111
|
||||||
msgid "Due to Apple policies, adult content can only be enabled on the web after completing sign up."
|
msgid "Due to Apple policies, adult content can only be enabled on the web after completing sign up."
|
||||||
msgstr ""
|
msgstr "Degut a les polítiques d'Apple, el contingut per a adults només es pot habilitar a la web després de registrar-se"
|
||||||
|
|
||||||
#: src/view/com/modals/EditProfile.tsx:185
|
#: src/view/com/modals/EditProfile.tsx:185
|
||||||
msgid "e.g. Alice Roberts"
|
msgid "e.g. Alice Roberts"
|
||||||
|
@ -1316,7 +1316,7 @@ msgstr "Edita la descripció del teu perfil"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:34
|
#: src/screens/Onboarding/index.tsx:34
|
||||||
msgid "Education"
|
msgid "Education"
|
||||||
msgstr ""
|
msgstr "Ensenyament"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step1.tsx:176
|
#: src/view/com/auth/create/Step1.tsx:176
|
||||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:156
|
#: src/view/com/auth/login/ForgotPasswordForm.tsx:156
|
||||||
|
@ -1357,7 +1357,7 @@ msgstr "Habilita el contingut per a adults"
|
||||||
#: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:76
|
#: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:76
|
||||||
#: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:77
|
#: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:77
|
||||||
msgid "Enable adult content in your feeds"
|
msgid "Enable adult content in your feeds"
|
||||||
msgstr ""
|
msgstr "Habilita veure el contingut per adults als teus canals"
|
||||||
|
|
||||||
#: src/view/com/modals/EmbedConsent.tsx:97
|
#: src/view/com/modals/EmbedConsent.tsx:97
|
||||||
msgid "Enable External Media"
|
msgid "Enable External Media"
|
||||||
|
@ -1394,7 +1394,7 @@ msgstr "Entra el codi de confirmació"
|
||||||
|
|
||||||
#: src/view/com/modals/ChangePassword.tsx:151
|
#: src/view/com/modals/ChangePassword.tsx:151
|
||||||
msgid "Enter the code you received to change your password."
|
msgid "Enter the code you received to change your password."
|
||||||
msgstr ""
|
msgstr "Introdueix el codi que has rebut per canviar la teva contrasenya."
|
||||||
|
|
||||||
#: src/view/com/modals/ChangeHandle.tsx:371
|
#: src/view/com/modals/ChangeHandle.tsx:371
|
||||||
msgid "Enter the domain you want to use"
|
msgid "Enter the domain you want to use"
|
||||||
|
@ -1473,12 +1473,12 @@ msgstr "Expandeix o replega la publicació completa a la qual estàs responent"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:753
|
#: src/view/screens/Settings/index.tsx:753
|
||||||
msgid "Export my data"
|
msgid "Export my data"
|
||||||
msgstr ""
|
msgstr "Exporta les meves dades"
|
||||||
|
|
||||||
#: src/view/screens/Settings/ExportCarDialog.tsx:44
|
#: src/view/screens/Settings/ExportCarDialog.tsx:44
|
||||||
#: src/view/screens/Settings/index.tsx:764
|
#: src/view/screens/Settings/index.tsx:764
|
||||||
msgid "Export My Data"
|
msgid "Export My Data"
|
||||||
msgstr ""
|
msgstr "Exporta les meves dades"
|
||||||
|
|
||||||
#: src/view/com/modals/EmbedConsent.tsx:64
|
#: src/view/com/modals/EmbedConsent.tsx:64
|
||||||
msgid "External Media"
|
msgid "External Media"
|
||||||
|
@ -1559,11 +1559,11 @@ msgstr "Els canals són algoritmes personalitzats creats per usuaris que coneixe
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepTopicalFeeds.tsx:76
|
#: src/screens/Onboarding/StepTopicalFeeds.tsx:76
|
||||||
msgid "Feeds can be topical as well!"
|
msgid "Feeds can be topical as well!"
|
||||||
msgstr ""
|
msgstr "Els canals també poden ser d'actualitat!"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFinished.tsx:151
|
#: src/screens/Onboarding/StepFinished.tsx:151
|
||||||
msgid "Finalizing"
|
msgid "Finalizing"
|
||||||
msgstr ""
|
msgstr "Finalitzant"
|
||||||
|
|
||||||
#: src/view/com/posts/CustomFeedEmptyState.tsx:47
|
#: src/view/com/posts/CustomFeedEmptyState.tsx:47
|
||||||
#: src/view/com/posts/FollowingEmptyState.tsx:57
|
#: src/view/com/posts/FollowingEmptyState.tsx:57
|
||||||
|
@ -1597,11 +1597,11 @@ msgstr "Ajusta els fils de debat."
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:38
|
#: src/screens/Onboarding/index.tsx:38
|
||||||
msgid "Fitness"
|
msgid "Fitness"
|
||||||
msgstr ""
|
msgstr "Exercici"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFinished.tsx:131
|
#: src/screens/Onboarding/StepFinished.tsx:131
|
||||||
msgid "Flexible"
|
msgid "Flexible"
|
||||||
msgstr ""
|
msgstr "Flexible"
|
||||||
|
|
||||||
#: src/view/com/modals/EditImage.tsx:115
|
#: src/view/com/modals/EditImage.tsx:115
|
||||||
msgid "Flip horizontal"
|
msgid "Flip horizontal"
|
||||||
|
@ -1631,11 +1631,11 @@ msgstr "Segueix {0}"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:179
|
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:179
|
||||||
msgid "Follow All"
|
msgid "Follow All"
|
||||||
msgstr ""
|
msgstr "Segueix-los a tots"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:174
|
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:174
|
||||||
msgid "Follow selected accounts and continue to the next step"
|
msgid "Follow selected accounts and continue to the next step"
|
||||||
msgstr ""
|
msgstr "Segueix els comptes seleccionats i continua"
|
||||||
|
|
||||||
#: src/view/com/auth/onboarding/RecommendedFollows.tsx:64
|
#: src/view/com/auth/onboarding/RecommendedFollows.tsx:64
|
||||||
msgid "Follow some users to get started. We can recommend you more users based on who you find interesting."
|
msgid "Follow some users to get started. We can recommend you more users based on who you find interesting."
|
||||||
|
@ -1693,7 +1693,7 @@ msgstr "Et segueix"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:43
|
#: src/screens/Onboarding/index.tsx:43
|
||||||
msgid "Food"
|
msgid "Food"
|
||||||
msgstr ""
|
msgstr "Menjar"
|
||||||
|
|
||||||
#: src/view/com/modals/DeleteAccount.tsx:111
|
#: src/view/com/modals/DeleteAccount.tsx:111
|
||||||
msgid "For security reasons, we'll need to send a confirmation code to your email address."
|
msgid "For security reasons, we'll need to send a confirmation code to your email address."
|
||||||
|
@ -1752,7 +1752,7 @@ msgstr "Ves enrere"
|
||||||
#: src/screens/Onboarding/Layout.tsx:104
|
#: src/screens/Onboarding/Layout.tsx:104
|
||||||
#: src/screens/Onboarding/Layout.tsx:193
|
#: src/screens/Onboarding/Layout.tsx:193
|
||||||
msgid "Go back to previous step"
|
msgid "Go back to previous step"
|
||||||
msgstr ""
|
msgstr "Ves al pas anterior"
|
||||||
|
|
||||||
#: src/view/screens/Search/Search.tsx:747
|
#: src/view/screens/Search/Search.tsx:747
|
||||||
#: src/view/shell/desktop/Search.tsx:262
|
#: src/view/shell/desktop/Search.tsx:262
|
||||||
|
@ -1794,15 +1794,15 @@ msgstr "Ajuda"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:132
|
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:132
|
||||||
msgid "Here are some accounts for you to follow"
|
msgid "Here are some accounts for you to follow"
|
||||||
msgstr ""
|
msgstr "Aquí tens uns quants comptes que pots seguir"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepTopicalFeeds.tsx:85
|
#: src/screens/Onboarding/StepTopicalFeeds.tsx:85
|
||||||
msgid "Here are some popular topical feeds. You can choose to follow as many as you like."
|
msgid "Here are some popular topical feeds. You can choose to follow as many as you like."
|
||||||
msgstr ""
|
msgstr "Aquí tens alguns canals d'actualitat populars. Pots seguir-ne tants com vulguis."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepTopicalFeeds.tsx:80
|
#: src/screens/Onboarding/StepTopicalFeeds.tsx:80
|
||||||
msgid "Here are some topical feeds based on your interests: {interestsText}. You can choose to follow as many as you like."
|
msgid "Here are some topical feeds based on your interests: {interestsText}. You can choose to follow as many as you like."
|
||||||
msgstr ""
|
msgstr "Aquí tens uns quants canals d'actualitat basats en els teus interesos: {interestsText}. Pots seguir-ne tants com vulguis."
|
||||||
|
|
||||||
#: src/view/com/modals/AddAppPasswords.tsx:153
|
#: src/view/com/modals/AddAppPasswords.tsx:153
|
||||||
msgid "Here is your app password."
|
msgid "Here is your app password."
|
||||||
|
@ -1914,7 +1914,7 @@ msgstr "Si no en selecciones cap, és apropiat per a totes les edats."
|
||||||
|
|
||||||
#: src/view/com/modals/ChangePassword.tsx:146
|
#: src/view/com/modals/ChangePassword.tsx:146
|
||||||
msgid "If you want to change your password, we will send you a code to verify that this is your account."
|
msgid "If you want to change your password, we will send you a code to verify that this is your account."
|
||||||
msgstr ""
|
msgstr "Si vols canviar la contrasenya t'enviarem un codi per verificar que aquest compte és teu."
|
||||||
|
|
||||||
#: src/view/com/util/images/Gallery.tsx:38
|
#: src/view/com/util/images/Gallery.tsx:38
|
||||||
msgid "Image"
|
msgid "Image"
|
||||||
|
@ -2024,7 +2024,7 @@ msgstr "Codis d'invitació: 1 disponible"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFollowingFeed.tsx:64
|
#: src/screens/Onboarding/StepFollowingFeed.tsx:64
|
||||||
msgid "It shows posts from the people you follow as they happen."
|
msgid "It shows posts from the people you follow as they happen."
|
||||||
msgstr ""
|
msgstr "Mostra les publicacions de les persones que segueixes cronològicament."
|
||||||
|
|
||||||
#: src/view/com/auth/HomeLoggedOutCTA.tsx:99
|
#: src/view/com/auth/HomeLoggedOutCTA.tsx:99
|
||||||
#: src/view/com/auth/SplashScreen.web.tsx:138
|
#: src/view/com/auth/SplashScreen.web.tsx:138
|
||||||
|
@ -2046,7 +2046,7 @@ msgstr "Feines"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:24
|
#: src/screens/Onboarding/index.tsx:24
|
||||||
msgid "Journalism"
|
msgid "Journalism"
|
||||||
msgstr ""
|
msgstr "Periodisme"
|
||||||
|
|
||||||
#: src/view/com/composer/select-language/SelectLangBtn.tsx:104
|
#: src/view/com/composer/select-language/SelectLangBtn.tsx:104
|
||||||
msgid "Language selection"
|
msgid "Language selection"
|
||||||
|
@ -2101,7 +2101,7 @@ msgstr "Sortint de Bluesky"
|
||||||
|
|
||||||
#: src/screens/Deactivated.tsx:128
|
#: src/screens/Deactivated.tsx:128
|
||||||
msgid "left to go."
|
msgid "left to go."
|
||||||
msgstr ""
|
msgstr "queda."
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:278
|
#: src/view/screens/Settings/index.tsx:278
|
||||||
msgid "Legacy storage cleared, you need to restart the app now."
|
msgid "Legacy storage cleared, you need to restart the app now."
|
||||||
|
@ -2114,7 +2114,7 @@ msgstr "Restablirem la teva contrasenya!"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFinished.tsx:151
|
#: src/screens/Onboarding/StepFinished.tsx:151
|
||||||
msgid "Let's go!"
|
msgid "Let's go!"
|
||||||
msgstr ""
|
msgstr "Som-hi!"
|
||||||
|
|
||||||
#: src/view/com/util/UserAvatar.tsx:248
|
#: src/view/com/util/UserAvatar.tsx:248
|
||||||
#: src/view/com/util/UserBanner.tsx:62
|
#: src/view/com/util/UserBanner.tsx:62
|
||||||
|
@ -2140,7 +2140,7 @@ msgstr "Li ha agradat a"
|
||||||
#: src/view/screens/PostLikedBy.tsx:27
|
#: src/view/screens/PostLikedBy.tsx:27
|
||||||
#: src/view/screens/ProfileFeedLikedBy.tsx:27
|
#: src/view/screens/ProfileFeedLikedBy.tsx:27
|
||||||
msgid "Liked By"
|
msgid "Liked By"
|
||||||
msgstr ""
|
msgstr "Li ha agradat a"
|
||||||
|
|
||||||
#: src/view/com/feeds/FeedSourceCard.tsx:279
|
#: src/view/com/feeds/FeedSourceCard.tsx:279
|
||||||
msgid "Liked by {0} {1}"
|
msgid "Liked by {0} {1}"
|
||||||
|
@ -2152,7 +2152,7 @@ msgstr "Li ha agradat a {likeCount} {0}"
|
||||||
|
|
||||||
#: src/view/com/notifications/FeedItem.tsx:170
|
#: src/view/com/notifications/FeedItem.tsx:170
|
||||||
msgid "liked your custom feed"
|
msgid "liked your custom feed"
|
||||||
msgstr ""
|
msgstr "els hi ha agradat el teu canal personalitzat"
|
||||||
|
|
||||||
#: src/view/com/notifications/FeedItem.tsx:171
|
#: src/view/com/notifications/FeedItem.tsx:171
|
||||||
#~ msgid "liked your custom feed{0}"
|
#~ msgid "liked your custom feed{0}"
|
||||||
|
@ -2247,7 +2247,7 @@ msgstr "Registre"
|
||||||
#: src/screens/Deactivated.tsx:178
|
#: src/screens/Deactivated.tsx:178
|
||||||
#: src/screens/Deactivated.tsx:181
|
#: src/screens/Deactivated.tsx:181
|
||||||
msgid "Log out"
|
msgid "Log out"
|
||||||
msgstr ""
|
msgstr "Desconnecta"
|
||||||
|
|
||||||
#: src/view/screens/Moderation.tsx:155
|
#: src/view/screens/Moderation.tsx:155
|
||||||
msgid "Logged-out visibility"
|
msgid "Logged-out visibility"
|
||||||
|
@ -2477,7 +2477,7 @@ msgstr "Els meus canals desats"
|
||||||
|
|
||||||
#: src/view/com/auth/server-input/index.tsx:118
|
#: src/view/com/auth/server-input/index.tsx:118
|
||||||
msgid "my-server.com"
|
msgid "my-server.com"
|
||||||
msgstr ""
|
msgstr "el-meu-servidor.com"
|
||||||
|
|
||||||
#: src/view/com/modals/AddAppPasswords.tsx:179
|
#: src/view/com/modals/AddAppPasswords.tsx:179
|
||||||
#: src/view/com/modals/CreateOrEditList.tsx:290
|
#: src/view/com/modals/CreateOrEditList.tsx:290
|
||||||
|
@ -2490,7 +2490,7 @@ msgstr "Es requereix un nom"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:25
|
#: src/screens/Onboarding/index.tsx:25
|
||||||
msgid "Nature"
|
msgid "Nature"
|
||||||
msgstr ""
|
msgstr "Natura"
|
||||||
|
|
||||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:190
|
#: src/view/com/auth/login/ForgotPasswordForm.tsx:190
|
||||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:219
|
#: src/view/com/auth/login/ForgotPasswordForm.tsx:219
|
||||||
|
@ -2516,7 +2516,7 @@ msgstr "No perdis mai accés als teus seguidors ni a les teves dades."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFinished.tsx:119
|
#: src/screens/Onboarding/StepFinished.tsx:119
|
||||||
msgid "Never lose access to your followers or data."
|
msgid "Never lose access to your followers or data."
|
||||||
msgstr ""
|
msgstr "No perdis mai accés als teus seguidors i les teves dades."
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:293
|
#: src/components/dialogs/MutedWords.tsx:293
|
||||||
msgid "Nevermind"
|
msgid "Nevermind"
|
||||||
|
@ -2541,7 +2541,7 @@ msgstr "Nova contrasenya"
|
||||||
|
|
||||||
#: src/view/com/modals/ChangePassword.tsx:215
|
#: src/view/com/modals/ChangePassword.tsx:215
|
||||||
msgid "New Password"
|
msgid "New Password"
|
||||||
msgstr ""
|
msgstr "Nova contrasenya"
|
||||||
|
|
||||||
#: src/view/com/feeds/FeedPage.tsx:126
|
#: src/view/com/feeds/FeedPage.tsx:126
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
|
@ -2577,7 +2577,7 @@ msgstr "Les respostes més noves primer"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:23
|
#: src/screens/Onboarding/index.tsx:23
|
||||||
msgid "News"
|
msgid "News"
|
||||||
msgstr ""
|
msgstr "Notícies"
|
||||||
|
|
||||||
#: src/view/com/auth/create/CreateAccount.tsx:172
|
#: src/view/com/auth/create/CreateAccount.tsx:172
|
||||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:182
|
#: src/view/com/auth/login/ForgotPasswordForm.tsx:182
|
||||||
|
@ -2687,7 +2687,7 @@ msgstr "Ostres!"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepInterests/index.tsx:128
|
#: src/screens/Onboarding/StepInterests/index.tsx:128
|
||||||
msgid "Oh no! Something went wrong."
|
msgid "Oh no! Something went wrong."
|
||||||
msgstr ""
|
msgstr "Ostres! Alguna cosa ha fallat."
|
||||||
|
|
||||||
#: src/view/com/auth/login/PasswordUpdatedForm.tsx:41
|
#: src/view/com/auth/login/PasswordUpdatedForm.tsx:41
|
||||||
msgid "Okay"
|
msgid "Okay"
|
||||||
|
@ -2721,7 +2721,7 @@ msgstr "Ostres!"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFinished.tsx:115
|
#: src/screens/Onboarding/StepFinished.tsx:115
|
||||||
msgid "Open"
|
msgid "Open"
|
||||||
msgstr ""
|
msgstr "Obre"
|
||||||
|
|
||||||
#: src/view/screens/Moderation.tsx:75
|
#: src/view/screens/Moderation.tsx:75
|
||||||
msgid "Open content filtering settings"
|
msgid "Open content filtering settings"
|
||||||
|
@ -2750,7 +2750,7 @@ msgstr ""
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:804
|
#: src/view/screens/Settings/index.tsx:804
|
||||||
msgid "Open storybook page"
|
msgid "Open storybook page"
|
||||||
msgstr ""
|
msgstr "Obre la pàgina d'historial"
|
||||||
|
|
||||||
#: src/view/com/util/forms/DropdownButton.tsx:154
|
#: src/view/com/util/forms/DropdownButton.tsx:154
|
||||||
msgid "Opens {numItems} options"
|
msgid "Opens {numItems} options"
|
||||||
|
@ -2876,7 +2876,7 @@ msgstr "Pàgina no trobada"
|
||||||
|
|
||||||
#: src/view/screens/NotFound.tsx:42
|
#: src/view/screens/NotFound.tsx:42
|
||||||
msgid "Page Not Found"
|
msgid "Page Not Found"
|
||||||
msgstr ""
|
msgstr "Pàgina no trobada"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step1.tsx:191
|
#: src/view/com/auth/create/Step1.tsx:191
|
||||||
#: src/view/com/auth/create/Step1.tsx:201
|
#: src/view/com/auth/create/Step1.tsx:201
|
||||||
|
@ -2912,7 +2912,7 @@ msgstr "S'ha denegat el permís per accedir a la càmera. Activa'l a la configur
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:31
|
#: src/screens/Onboarding/index.tsx:31
|
||||||
msgid "Pets"
|
msgid "Pets"
|
||||||
msgstr ""
|
msgstr "Mascotes"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:183
|
#: src/view/com/auth/create/Step2.tsx:183
|
||||||
#~ msgid "Phone number"
|
#~ msgid "Phone number"
|
||||||
|
@ -3010,7 +3010,7 @@ msgstr "Espera que es generi la targeta de l'enllaç"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:37
|
#: src/screens/Onboarding/index.tsx:37
|
||||||
msgid "Politics"
|
msgid "Politics"
|
||||||
msgstr ""
|
msgstr "Política"
|
||||||
|
|
||||||
#: src/view/com/modals/SelfLabel.tsx:111
|
#: src/view/com/modals/SelfLabel.tsx:111
|
||||||
msgid "Porn"
|
msgid "Porn"
|
||||||
|
@ -3129,7 +3129,7 @@ msgstr "Protegeix el teu compte verificant el teu correu."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFinished.tsx:101
|
#: src/screens/Onboarding/StepFinished.tsx:101
|
||||||
msgid "Public"
|
msgid "Public"
|
||||||
msgstr ""
|
msgstr "Públic"
|
||||||
|
|
||||||
#: src/view/screens/ModerationModlists.tsx:61
|
#: src/view/screens/ModerationModlists.tsx:61
|
||||||
msgid "Public, shareable lists of users to mute or block in bulk."
|
msgid "Public, shareable lists of users to mute or block in bulk."
|
||||||
|
@ -3300,7 +3300,7 @@ msgstr "Informa de la publicació"
|
||||||
#: src/view/com/util/post-ctrls/RepostButton.tsx:61
|
#: src/view/com/util/post-ctrls/RepostButton.tsx:61
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Repost"
|
msgid "Repost"
|
||||||
msgstr "Respon"
|
msgstr "Republica"
|
||||||
|
|
||||||
#: src/view/com/util/post-ctrls/RepostButton.web.tsx:48
|
#: src/view/com/util/post-ctrls/RepostButton.web.tsx:48
|
||||||
msgid "Repost"
|
msgid "Repost"
|
||||||
|
@ -3317,11 +3317,11 @@ msgstr "Republica o cita la publicació"
|
||||||
|
|
||||||
#: src/view/screens/PostRepostedBy.tsx:27
|
#: src/view/screens/PostRepostedBy.tsx:27
|
||||||
msgid "Reposted By"
|
msgid "Reposted By"
|
||||||
msgstr ""
|
msgstr "Republicat per"
|
||||||
|
|
||||||
#: src/view/com/posts/FeedItem.tsx:207
|
#: src/view/com/posts/FeedItem.tsx:207
|
||||||
msgid "Reposted by {0}"
|
msgid "Reposted by {0}"
|
||||||
msgstr ""
|
msgstr "Republicat per {0}"
|
||||||
|
|
||||||
#: src/view/com/posts/FeedItem.tsx:206
|
#: src/view/com/posts/FeedItem.tsx:206
|
||||||
#~ msgid "Reposted by {0})"
|
#~ msgid "Reposted by {0})"
|
||||||
|
@ -3351,7 +3351,7 @@ msgstr "Demana un canvi"
|
||||||
#: src/view/com/modals/ChangePassword.tsx:239
|
#: src/view/com/modals/ChangePassword.tsx:239
|
||||||
#: src/view/com/modals/ChangePassword.tsx:241
|
#: src/view/com/modals/ChangePassword.tsx:241
|
||||||
msgid "Request Code"
|
msgid "Request Code"
|
||||||
msgstr ""
|
msgstr "Demana un codi"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:456
|
#: src/view/screens/Settings/index.tsx:456
|
||||||
msgid "Require alt text before posting"
|
msgid "Require alt text before posting"
|
||||||
|
@ -3368,7 +3368,7 @@ msgstr "Codi de restabliment"
|
||||||
|
|
||||||
#: src/view/com/modals/ChangePassword.tsx:190
|
#: src/view/com/modals/ChangePassword.tsx:190
|
||||||
msgid "Reset Code"
|
msgid "Reset Code"
|
||||||
msgstr ""
|
msgstr "Codi de restabliment"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:824
|
#: src/view/screens/Settings/index.tsx:824
|
||||||
msgid "Reset onboarding"
|
msgid "Reset onboarding"
|
||||||
|
@ -3475,7 +3475,7 @@ msgstr "Desa el canvi d'identificador a {handle}"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:36
|
#: src/screens/Onboarding/index.tsx:36
|
||||||
msgid "Science"
|
msgid "Science"
|
||||||
msgstr ""
|
msgstr "Ciència"
|
||||||
|
|
||||||
#: src/view/screens/ProfileList.tsx:859
|
#: src/view/screens/ProfileList.tsx:859
|
||||||
msgid "Scroll to top"
|
msgid "Scroll to top"
|
||||||
|
@ -3584,19 +3584,19 @@ msgstr "Selecciona el servei"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:52
|
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:52
|
||||||
msgid "Select some accounts below to follow"
|
msgid "Select some accounts below to follow"
|
||||||
msgstr ""
|
msgstr "Selecciona alguns d'aquests comptes per seguir-los"
|
||||||
|
|
||||||
#: src/view/com/auth/server-input/index.tsx:82
|
#: src/view/com/auth/server-input/index.tsx:82
|
||||||
msgid "Select the service that hosts your data."
|
msgid "Select the service that hosts your data."
|
||||||
msgstr ""
|
msgstr "Selecciona el servei que allotja les teves dades."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepTopicalFeeds.tsx:96
|
#: src/screens/Onboarding/StepTopicalFeeds.tsx:96
|
||||||
msgid "Select topical feeds to follow from the list below"
|
msgid "Select topical feeds to follow from the list below"
|
||||||
msgstr ""
|
msgstr "Selecciona els canals d'actualitat per seguir d'aquesta llista"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepModeration/index.tsx:75
|
#: src/screens/Onboarding/StepModeration/index.tsx:75
|
||||||
msgid "Select what you want to see (or not see), and we’ll handle the rest."
|
msgid "Select what you want to see (or not see), and we’ll handle the rest."
|
||||||
msgstr ""
|
msgstr "Selecciona què vols veure (o què no vols veure) i nosaltres farem la resta."
|
||||||
|
|
||||||
#: src/view/screens/LanguageSettings.tsx:281
|
#: src/view/screens/LanguageSettings.tsx:281
|
||||||
msgid "Select which languages you want your subscribed feeds to include. If none are selected, all languages will be shown."
|
msgid "Select which languages you want your subscribed feeds to include. If none are selected, all languages will be shown."
|
||||||
|
@ -3608,7 +3608,7 @@ msgstr "Selecciona l'idioma de l'aplicació perquè el text predeterminat es mos
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepInterests/index.tsx:196
|
#: src/screens/Onboarding/StepInterests/index.tsx:196
|
||||||
msgid "Select your interests from the options below"
|
msgid "Select your interests from the options below"
|
||||||
msgstr ""
|
msgstr "Selecciona els teus interesos d'entre aquestes opcions"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:155
|
#: src/view/com/auth/create/Step2.tsx:155
|
||||||
#~ msgid "Select your phone's country"
|
#~ msgid "Select your phone's country"
|
||||||
|
@ -3620,11 +3620,11 @@ msgstr "Selecciona el teu idioma preferit per a les traduccions al teu canal."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:116
|
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:116
|
||||||
msgid "Select your primary algorithmic feeds"
|
msgid "Select your primary algorithmic feeds"
|
||||||
msgstr ""
|
msgstr "Selecciona els teus canals algorítmics primaris"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:142
|
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:142
|
||||||
msgid "Select your secondary algorithmic feeds"
|
msgid "Select your secondary algorithmic feeds"
|
||||||
msgstr ""
|
msgstr "Selecciona els teus canals algorítmics secundaris"
|
||||||
|
|
||||||
#: src/view/com/modals/VerifyEmail.tsx:202
|
#: src/view/com/modals/VerifyEmail.tsx:202
|
||||||
#: src/view/com/modals/VerifyEmail.tsx:204
|
#: src/view/com/modals/VerifyEmail.tsx:204
|
||||||
|
@ -3659,7 +3659,7 @@ msgstr "Envia un correu amb el codi de confirmació per l'eliminació del compte
|
||||||
|
|
||||||
#: src/view/com/auth/server-input/index.tsx:110
|
#: src/view/com/auth/server-input/index.tsx:110
|
||||||
msgid "Server address"
|
msgid "Server address"
|
||||||
msgstr ""
|
msgstr "Adreça del servidor"
|
||||||
|
|
||||||
#: src/view/com/modals/ContentFilteringSettings.tsx:311
|
#: src/view/com/modals/ContentFilteringSettings.tsx:311
|
||||||
msgid "Set {value} for {labelGroup} content moderation policy"
|
msgid "Set {value} for {labelGroup} content moderation policy"
|
||||||
|
@ -3685,11 +3685,11 @@ msgstr "Estableix el tema de colors a la configuració del sistema"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:514
|
#: src/view/screens/Settings/index.tsx:514
|
||||||
msgid "Set dark theme to the dark theme"
|
msgid "Set dark theme to the dark theme"
|
||||||
msgstr ""
|
msgstr "Posa el tema fosc"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:507
|
#: src/view/screens/Settings/index.tsx:507
|
||||||
msgid "Set dark theme to the dim theme"
|
msgid "Set dark theme to the dim theme"
|
||||||
msgstr ""
|
msgstr "Posa el tema fosc al tema atenuat"
|
||||||
|
|
||||||
#: src/view/com/auth/login/SetNewPasswordForm.tsx:104
|
#: src/view/com/auth/login/SetNewPasswordForm.tsx:104
|
||||||
msgid "Set new password"
|
msgid "Set new password"
|
||||||
|
@ -3725,7 +3725,7 @@ msgstr ""
|
||||||
|
|
||||||
#: src/screens/Onboarding/Layout.tsx:50
|
#: src/screens/Onboarding/Layout.tsx:50
|
||||||
msgid "Set up your account"
|
msgid "Set up your account"
|
||||||
msgstr ""
|
msgstr "Configura el teu compte"
|
||||||
|
|
||||||
#: src/view/com/modals/ChangeHandle.tsx:266
|
#: src/view/com/modals/ChangeHandle.tsx:266
|
||||||
msgid "Sets Bluesky username"
|
msgid "Sets Bluesky username"
|
||||||
|
@ -3813,15 +3813,15 @@ msgstr "Mostra les publicacions citades"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFollowingFeed.tsx:118
|
#: src/screens/Onboarding/StepFollowingFeed.tsx:118
|
||||||
msgid "Show quote-posts in Following feed"
|
msgid "Show quote-posts in Following feed"
|
||||||
msgstr ""
|
msgstr "Mostra les publicacions citades en el canal Seguint"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFollowingFeed.tsx:134
|
#: src/screens/Onboarding/StepFollowingFeed.tsx:134
|
||||||
msgid "Show quotes in Following"
|
msgid "Show quotes in Following"
|
||||||
msgstr ""
|
msgstr "Mostra els citats a Seguint"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFollowingFeed.tsx:94
|
#: src/screens/Onboarding/StepFollowingFeed.tsx:94
|
||||||
msgid "Show re-posts in Following feed"
|
msgid "Show re-posts in Following feed"
|
||||||
msgstr ""
|
msgstr "Mostra les republicacions al canal Seguint"
|
||||||
|
|
||||||
#: src/view/screens/PreferencesFollowingFeed.tsx:119
|
#: src/view/screens/PreferencesFollowingFeed.tsx:119
|
||||||
msgid "Show Replies"
|
msgid "Show Replies"
|
||||||
|
@ -3833,11 +3833,11 @@ msgstr "Mostra les respostes dels comptes que segueixes abans que les altres."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFollowingFeed.tsx:86
|
#: src/screens/Onboarding/StepFollowingFeed.tsx:86
|
||||||
msgid "Show replies in Following"
|
msgid "Show replies in Following"
|
||||||
msgstr ""
|
msgstr "Mostra les respostes a Seguint"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFollowingFeed.tsx:70
|
#: src/screens/Onboarding/StepFollowingFeed.tsx:70
|
||||||
msgid "Show replies in Following feed"
|
msgid "Show replies in Following feed"
|
||||||
msgstr ""
|
msgstr "Mostrea les respostes al canal Seguint"
|
||||||
|
|
||||||
#: src/view/screens/PreferencesFollowingFeed.tsx:70
|
#: src/view/screens/PreferencesFollowingFeed.tsx:70
|
||||||
msgid "Show replies with at least {value} {0}"
|
msgid "Show replies with at least {value} {0}"
|
||||||
|
@ -3849,7 +3849,7 @@ msgstr "Mostra republicacions"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFollowingFeed.tsx:110
|
#: src/screens/Onboarding/StepFollowingFeed.tsx:110
|
||||||
msgid "Show reposts in Following"
|
msgid "Show reposts in Following"
|
||||||
msgstr ""
|
msgstr "Mostra les republicacions al canal Seguint"
|
||||||
|
|
||||||
#: src/view/com/util/moderation/ContentHider.tsx:67
|
#: src/view/com/util/moderation/ContentHider.tsx:67
|
||||||
#: src/view/com/util/moderation/PostHider.tsx:61
|
#: src/view/com/util/moderation/PostHider.tsx:61
|
||||||
|
@ -3949,7 +3949,7 @@ msgstr "Salta aquest pas"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepInterests/index.tsx:232
|
#: src/screens/Onboarding/StepInterests/index.tsx:232
|
||||||
msgid "Skip this flow"
|
msgid "Skip this flow"
|
||||||
msgstr ""
|
msgstr "Salta aquest flux"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:82
|
#: src/view/com/auth/create/Step2.tsx:82
|
||||||
#~ msgid "SMS verification"
|
#~ msgid "SMS verification"
|
||||||
|
@ -3957,7 +3957,7 @@ msgstr ""
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:40
|
#: src/screens/Onboarding/index.tsx:40
|
||||||
msgid "Software Dev"
|
msgid "Software Dev"
|
||||||
msgstr ""
|
msgstr "Desenvolupament de programari"
|
||||||
|
|
||||||
#: src/view/com/modals/ProfilePreview.tsx:62
|
#: src/view/com/modals/ProfilePreview.tsx:62
|
||||||
#~ msgid "Something went wrong and we're not sure what."
|
#~ msgid "Something went wrong and we're not sure what."
|
||||||
|
@ -3985,7 +3985,7 @@ msgstr "Ordena les respostes a la mateixa publicació per:"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:30
|
#: src/screens/Onboarding/index.tsx:30
|
||||||
msgid "Sports"
|
msgid "Sports"
|
||||||
msgstr ""
|
msgstr "Esports"
|
||||||
|
|
||||||
#: src/view/com/modals/crop-image/CropImage.web.tsx:122
|
#: src/view/com/modals/crop-image/CropImage.web.tsx:122
|
||||||
msgid "Square"
|
msgid "Square"
|
||||||
|
@ -4023,7 +4023,7 @@ msgstr "Subscriure's"
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/FeedCard.tsx:173
|
#: src/screens/Onboarding/StepAlgoFeeds/FeedCard.tsx:173
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/FeedCard.tsx:308
|
#: src/screens/Onboarding/StepAlgoFeeds/FeedCard.tsx:308
|
||||||
msgid "Subscribe to the {0} feed"
|
msgid "Subscribe to the {0} feed"
|
||||||
msgstr ""
|
msgstr "Subscriu-te al canal {0}"
|
||||||
|
|
||||||
#: src/view/screens/ProfileList.tsx:604
|
#: src/view/screens/ProfileList.tsx:604
|
||||||
msgid "Subscribe to this list"
|
msgid "Subscribe to this list"
|
||||||
|
@ -4095,7 +4095,7 @@ msgstr "Toca per veure-ho completament"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:39
|
#: src/screens/Onboarding/index.tsx:39
|
||||||
msgid "Tech"
|
msgid "Tech"
|
||||||
msgstr ""
|
msgstr "Tecnologia"
|
||||||
|
|
||||||
#: src/view/shell/desktop/RightNav.tsx:81
|
#: src/view/shell/desktop/RightNav.tsx:81
|
||||||
msgid "Terms"
|
msgid "Terms"
|
||||||
|
@ -4135,7 +4135,7 @@ msgstr "La política de drets d'autoria ha estat traslladada a <0/>"
|
||||||
|
|
||||||
#: src/screens/Onboarding/Layout.tsx:60
|
#: src/screens/Onboarding/Layout.tsx:60
|
||||||
msgid "The following steps will help customize your Bluesky experience."
|
msgid "The following steps will help customize your Bluesky experience."
|
||||||
msgstr ""
|
msgstr "Els següents passos t'ajudaran a personalitzar la teva experiència a Bluesky."
|
||||||
|
|
||||||
#: src/view/com/post-thread/PostThread.tsx:517
|
#: src/view/com/post-thread/PostThread.tsx:517
|
||||||
msgid "The post may have been deleted."
|
msgid "The post may have been deleted."
|
||||||
|
@ -4159,7 +4159,7 @@ msgstr "Les condicions del servei han estat traslladades a "
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:150
|
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:150
|
||||||
msgid "There are many feeds to try:"
|
msgid "There are many feeds to try:"
|
||||||
msgstr ""
|
msgstr "Hi ha molts canals per provar:"
|
||||||
|
|
||||||
#: src/view/screens/ProfileFeed.tsx:550
|
#: src/view/screens/ProfileFeed.tsx:550
|
||||||
msgid "There was an an issue contacting the server, please check your internet connection and try again."
|
msgid "There was an an issue contacting the server, please check your internet connection and try again."
|
||||||
|
@ -4239,7 +4239,7 @@ msgstr "S'ha produït un problema inesperat a l'aplicació. Fes-nos saber si aix
|
||||||
|
|
||||||
#: src/screens/Deactivated.tsx:106
|
#: src/screens/Deactivated.tsx:106
|
||||||
msgid "There's been a rush of new users to Bluesky! We'll activate your account as soon as we can."
|
msgid "There's been a rush of new users to Bluesky! We'll activate your account as soon as we can."
|
||||||
msgstr ""
|
msgstr "Hi ha hagut una gran quantitat d'usuaris nous a Bluesky! Activarem el teu compte tan aviat com puguem."
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:55
|
#: src/view/com/auth/create/Step2.tsx:55
|
||||||
#~ msgid "There's something wrong with this number. Please choose your country and enter your full phone number!"
|
#~ msgid "There's something wrong with this number. Please choose your country and enter your full phone number!"
|
||||||
|
@ -4247,7 +4247,7 @@ msgstr ""
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:138
|
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:138
|
||||||
msgid "These are popular accounts you might like:"
|
msgid "These are popular accounts you might like:"
|
||||||
msgstr ""
|
msgstr "Aquests són alguns comptes populars que et poden agradar:"
|
||||||
|
|
||||||
#~ msgid "This {0} has been labeled."
|
#~ msgid "This {0} has been labeled."
|
||||||
#~ msgstr "Aquest {0} ha estat etiquetat."
|
#~ msgstr "Aquest {0} ha estat etiquetat."
|
||||||
|
@ -4274,7 +4274,7 @@ msgstr "Aquest contingut no es pot veure sense un compte de Bluesky."
|
||||||
|
|
||||||
#: src/view/screens/Settings/ExportCarDialog.tsx:75
|
#: src/view/screens/Settings/ExportCarDialog.tsx:75
|
||||||
msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost.</0>"
|
msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost.</0>"
|
||||||
msgstr ""
|
msgstr "Aquesta funcionalitat està en beta. En <0>aquesta entrada al blog</0> tens més informació."
|
||||||
|
|
||||||
#: src/view/com/posts/FeedErrorMessage.tsx:114
|
#: src/view/com/posts/FeedErrorMessage.tsx:114
|
||||||
msgid "This feed is currently receiving high traffic and is temporarily unavailable. Please try again later."
|
msgid "This feed is currently receiving high traffic and is temporarily unavailable. Please try again later."
|
||||||
|
@ -4328,7 +4328,7 @@ msgstr "Aquest usuari està inclós a la llista <0/> que tens bloquejada"
|
||||||
|
|
||||||
#: src/view/com/modals/ModerationDetails.tsx:74
|
#: src/view/com/modals/ModerationDetails.tsx:74
|
||||||
msgid "This user is included in the <0/> list which you have muted."
|
msgid "This user is included in the <0/> list which you have muted."
|
||||||
msgstr ""
|
msgstr "Aquest usuari està inclòs a la llista <0/> que has silenciat."
|
||||||
|
|
||||||
#: src/view/com/modals/ModerationDetails.tsx:74
|
#: src/view/com/modals/ModerationDetails.tsx:74
|
||||||
#~ msgid "This user is included the <0/> list which you have muted."
|
#~ msgid "This user is included the <0/> list which you have muted."
|
||||||
|
@ -4614,7 +4614,7 @@ msgstr "Verifica el teu correu"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:42
|
#: src/screens/Onboarding/index.tsx:42
|
||||||
msgid "Video Games"
|
msgid "Video Games"
|
||||||
msgstr ""
|
msgstr "Videojocs"
|
||||||
|
|
||||||
#: src/view/com/profile/ProfileHeader.tsx:662
|
#: src/view/com/profile/ProfileHeader.tsx:662
|
||||||
msgid "View {0}'s avatar"
|
msgid "View {0}'s avatar"
|
||||||
|
@ -4647,7 +4647,7 @@ msgstr "Adverteix"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:134
|
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:134
|
||||||
msgid "We also think you'll like \"For You\" by Skygaze:"
|
msgid "We also think you'll like \"For You\" by Skygaze:"
|
||||||
msgstr ""
|
msgstr "També creiem que t'agradarà el canal \"For You\" d'Skygaze:"
|
||||||
|
|
||||||
#: src/screens/Hashtag.tsx:132
|
#: src/screens/Hashtag.tsx:132
|
||||||
msgid "We couldn't find any results for that hashtag."
|
msgid "We couldn't find any results for that hashtag."
|
||||||
|
@ -4655,11 +4655,11 @@ msgstr ""
|
||||||
|
|
||||||
#: src/screens/Deactivated.tsx:133
|
#: src/screens/Deactivated.tsx:133
|
||||||
msgid "We estimate {estimatedTime} until your account is ready."
|
msgid "We estimate {estimatedTime} until your account is ready."
|
||||||
msgstr ""
|
msgstr "Calculem {estimatedTime} fins que el teu compte estigui llest."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFinished.tsx:93
|
#: src/screens/Onboarding/StepFinished.tsx:93
|
||||||
msgid "We hope you have a wonderful time. Remember, Bluesky is:"
|
msgid "We hope you have a wonderful time. Remember, Bluesky is:"
|
||||||
msgstr ""
|
msgstr "Esperem que t'ho passis pipa. Recorda que Bluesky és:"
|
||||||
|
|
||||||
#: src/view/com/posts/DiscoverFallbackHeader.tsx:29
|
#: src/view/com/posts/DiscoverFallbackHeader.tsx:29
|
||||||
msgid "We ran out of posts from your follows. Here's the latest from <0/>."
|
msgid "We ran out of posts from your follows. Here's the latest from <0/>."
|
||||||
|
@ -4671,15 +4671,15 @@ msgstr ""
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:124
|
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:124
|
||||||
msgid "We recommend our \"Discover\" feed:"
|
msgid "We recommend our \"Discover\" feed:"
|
||||||
msgstr ""
|
msgstr "Et reomanem el nostre canal \"Discover\":"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepInterests/index.tsx:133
|
#: src/screens/Onboarding/StepInterests/index.tsx:133
|
||||||
msgid "We weren't able to connect. Please try again to continue setting up your account. If it continues to fail, you can skip this flow."
|
msgid "We weren't able to connect. Please try again to continue setting up your account. If it continues to fail, you can skip this flow."
|
||||||
msgstr ""
|
msgstr "No ens hem pogut connectar. Torna-ho a provar per continuar configurant el teu compte. Si continua fallant, pots ometre aquest flux."
|
||||||
|
|
||||||
#: src/screens/Deactivated.tsx:137
|
#: src/screens/Deactivated.tsx:137
|
||||||
msgid "We will let you know when your account is ready."
|
msgid "We will let you know when your account is ready."
|
||||||
msgstr ""
|
msgstr "T'informarem quan el teu compte estigui llest."
|
||||||
|
|
||||||
#: src/view/com/modals/AppealLabel.tsx:48
|
#: src/view/com/modals/AppealLabel.tsx:48
|
||||||
msgid "We'll look into your appeal promptly."
|
msgid "We'll look into your appeal promptly."
|
||||||
|
@ -4687,7 +4687,7 @@ msgstr "Analitzarem la teva apel·lació ràpidament."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepInterests/index.tsx:138
|
#: src/screens/Onboarding/StepInterests/index.tsx:138
|
||||||
msgid "We'll use this to help customize your experience."
|
msgid "We'll use this to help customize your experience."
|
||||||
msgstr ""
|
msgstr "Ho farem servir per personalitzar la teva experiència."
|
||||||
|
|
||||||
#: src/view/com/auth/create/CreateAccount.tsx:134
|
#: src/view/com/auth/create/CreateAccount.tsx:134
|
||||||
msgid "We're so excited to have you join us!"
|
msgid "We're so excited to have you join us!"
|
||||||
|
@ -4716,7 +4716,7 @@ msgstr "Benvingut a <0>Bluesky</0>"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepInterests/index.tsx:130
|
#: src/screens/Onboarding/StepInterests/index.tsx:130
|
||||||
msgid "What are your interests?"
|
msgid "What are your interests?"
|
||||||
msgstr ""
|
msgstr "Quins són els teus interesos?"
|
||||||
|
|
||||||
#: src/view/com/modals/report/Modal.tsx:169
|
#: src/view/com/modals/report/Modal.tsx:169
|
||||||
msgid "What is the issue with this {collectionName}?"
|
msgid "What is the issue with this {collectionName}?"
|
||||||
|
@ -4758,7 +4758,7 @@ msgstr "Escriu la teva resposta"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:28
|
#: src/screens/Onboarding/index.tsx:28
|
||||||
msgid "Writers"
|
msgid "Writers"
|
||||||
msgstr ""
|
msgstr "Escriptors"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:263
|
#: src/view/com/auth/create/Step2.tsx:263
|
||||||
#~ msgid "XXXXXX"
|
#~ msgid "XXXXXX"
|
||||||
|
@ -4776,7 +4776,7 @@ msgstr "Sí"
|
||||||
|
|
||||||
#: src/screens/Deactivated.tsx:130
|
#: src/screens/Deactivated.tsx:130
|
||||||
msgid "You are in line."
|
msgid "You are in line."
|
||||||
msgstr ""
|
msgstr "Estàs a la cua."
|
||||||
|
|
||||||
#: src/view/com/posts/FollowingEmptyState.tsx:67
|
#: src/view/com/posts/FollowingEmptyState.tsx:67
|
||||||
#: src/view/com/posts/FollowingEndOfFeed.tsx:68
|
#: src/view/com/posts/FollowingEndOfFeed.tsx:68
|
||||||
|
@ -4789,7 +4789,7 @@ msgstr "També pots descobrir nous canals personalitzats per seguir."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFollowingFeed.tsx:142
|
#: src/screens/Onboarding/StepFollowingFeed.tsx:142
|
||||||
msgid "You can change these settings later."
|
msgid "You can change these settings later."
|
||||||
msgstr ""
|
msgstr "Pots canviar aquests paràmetres més endavant."
|
||||||
|
|
||||||
#: src/view/com/auth/login/Login.tsx:158
|
#: src/view/com/auth/login/Login.tsx:158
|
||||||
#: src/view/com/auth/login/PasswordUpdatedForm.tsx:31
|
#: src/view/com/auth/login/PasswordUpdatedForm.tsx:31
|
||||||
|
@ -4825,7 +4825,7 @@ msgstr "Has bloquejat aquest usuari. No pots veure el seu contingut."
|
||||||
#: src/view/com/modals/ChangePassword.tsx:87
|
#: src/view/com/modals/ChangePassword.tsx:87
|
||||||
#: src/view/com/modals/ChangePassword.tsx:121
|
#: src/view/com/modals/ChangePassword.tsx:121
|
||||||
msgid "You have entered an invalid code. It should look like XXXXX-XXXXX."
|
msgid "You have entered an invalid code. It should look like XXXXX-XXXXX."
|
||||||
msgstr ""
|
msgstr "Has entrat un codi invàlid. Hauria de ser tipus XXXXX-XXXXX."
|
||||||
|
|
||||||
#: src/view/com/modals/ModerationDetails.tsx:87
|
#: src/view/com/modals/ModerationDetails.tsx:87
|
||||||
msgid "You have muted this user."
|
msgid "You have muted this user."
|
||||||
|
@ -4862,7 +4862,7 @@ msgstr "Has de tenir 18 anys o més per habilitar el contingut per a adults."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:103
|
#: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:103
|
||||||
msgid "You must be 18 years or older to enable adult content"
|
msgid "You must be 18 years or older to enable adult content"
|
||||||
msgstr ""
|
msgstr "Has de tenir 18 anys o més per habilitar el contingut per a adults"
|
||||||
|
|
||||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:147
|
#: src/view/com/util/forms/PostDropdownBtn.tsx:147
|
||||||
msgid "You will no longer receive notifications for this thread"
|
msgid "You will no longer receive notifications for this thread"
|
||||||
|
@ -4878,17 +4878,17 @@ msgstr "Rebràs un correu amb un \"codi de restabliment\". Introdueix aquí el c
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepModeration/index.tsx:72
|
#: src/screens/Onboarding/StepModeration/index.tsx:72
|
||||||
msgid "You're in control"
|
msgid "You're in control"
|
||||||
msgstr ""
|
msgstr "Tu tens el control"
|
||||||
|
|
||||||
#: src/screens/Deactivated.tsx:87
|
#: src/screens/Deactivated.tsx:87
|
||||||
#: src/screens/Deactivated.tsx:88
|
#: src/screens/Deactivated.tsx:88
|
||||||
#: src/screens/Deactivated.tsx:103
|
#: src/screens/Deactivated.tsx:103
|
||||||
msgid "You're in line"
|
msgid "You're in line"
|
||||||
msgstr ""
|
msgstr "Estàs a la cua"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFinished.tsx:90
|
#: src/screens/Onboarding/StepFinished.tsx:90
|
||||||
msgid "You're ready to go!"
|
msgid "You're ready to go!"
|
||||||
msgstr ""
|
msgstr "Ja està tot llest!"
|
||||||
|
|
||||||
#: src/view/com/posts/FollowingEndOfFeed.tsx:48
|
#: src/view/com/posts/FollowingEndOfFeed.tsx:48
|
||||||
msgid "You've reached the end of your feed! Find some more accounts to follow."
|
msgid "You've reached the end of your feed! Find some more accounts to follow."
|
||||||
|
@ -4904,7 +4904,7 @@ msgstr "El teu compte s'ha eliminat"
|
||||||
|
|
||||||
#: src/view/screens/Settings/ExportCarDialog.tsx:47
|
#: src/view/screens/Settings/ExportCarDialog.tsx:47
|
||||||
msgid "Your account repository, containing all public data records, can be downloaded as a \"CAR\" file. This file does not include media embeds, such as images, or your private data, which must be fetched separately."
|
msgid "Your account repository, containing all public data records, can be downloaded as a \"CAR\" file. This file does not include media embeds, such as images, or your private data, which must be fetched separately."
|
||||||
msgstr ""
|
msgstr "El repositori del teu compte, que conté tots els registres de dades públiques, es pot baixar com a fitxer \"CAR\". Aquest fitxer no inclou incrustacions multimèdia, com ara imatges, ni les teves dades privades, que s'han d'obtenir per separat."
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step1.tsx:215
|
#: src/view/com/auth/create/Step1.tsx:215
|
||||||
msgid "Your birth date"
|
msgid "Your birth date"
|
||||||
|
@ -4916,7 +4916,7 @@ msgstr "La teva elecció es desarà, però es pot canviar més endavant a la con
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFollowingFeed.tsx:61
|
#: src/screens/Onboarding/StepFollowingFeed.tsx:61
|
||||||
msgid "Your default feed is \"Following\""
|
msgid "Your default feed is \"Following\""
|
||||||
msgstr ""
|
msgstr "El teu canal per defecte és \"Seguint\""
|
||||||
|
|
||||||
#: src/view/com/auth/create/state.ts:110
|
#: src/view/com/auth/create/state.ts:110
|
||||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:70
|
#: src/view/com/auth/login/ForgotPasswordForm.tsx:70
|
||||||
|
@ -4964,7 +4964,7 @@ msgstr ""
|
||||||
|
|
||||||
#: src/view/com/modals/ChangePassword.tsx:155
|
#: src/view/com/modals/ChangePassword.tsx:155
|
||||||
msgid "Your password has been changed successfully!"
|
msgid "Your password has been changed successfully!"
|
||||||
msgstr ""
|
msgstr "S'ha canviat la teva contrasenya!"
|
||||||
|
|
||||||
#: src/view/com/composer/Composer.tsx:274
|
#: src/view/com/composer/Composer.tsx:274
|
||||||
msgid "Your post has been published"
|
msgid "Your post has been published"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -8,8 +8,8 @@ msgstr ""
|
||||||
"Language: pt-BR\n"
|
"Language: pt-BR\n"
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2024-02-08 19:59\n"
|
"PO-Revision-Date: 2024-03-12 11:36\n"
|
||||||
"Last-Translator: maisondasilva\n"
|
"Last-Translator: gildaswise\n"
|
||||||
"Language-Team: maisondasilva, MightyLoggor, gildaswise, gleydson, faeriarum\n"
|
"Language-Team: maisondasilva, MightyLoggor, gildaswise, gleydson, faeriarum\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
@ -17,28 +17,10 @@ msgstr ""
|
||||||
msgid "(no email)"
|
msgid "(no email)"
|
||||||
msgstr "(sem email)"
|
msgstr "(sem email)"
|
||||||
|
|
||||||
#: src/view/shell/desktop/RightNav.tsx:168
|
|
||||||
#~ msgid "{0, plural, one {# invite code available} other {# invite codes available}}"
|
|
||||||
#~ msgstr "{0, plural, one {# convite disponível} other {# convites disponíveis}}"
|
|
||||||
|
|
||||||
#: src/view/com/profile/ProfileHeader.tsx:593
|
#: src/view/com/profile/ProfileHeader.tsx:593
|
||||||
msgid "{following} following"
|
msgid "{following} following"
|
||||||
msgstr "{following} seguindo"
|
msgstr "{following} seguindo"
|
||||||
|
|
||||||
#: src/view/shell/desktop/RightNav.tsx:151
|
|
||||||
#~ msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}"
|
|
||||||
#~ msgstr "{invitesAvailable, plural, one {Convites: # disponível} other {Convites: # disponíveis}}"
|
|
||||||
|
|
||||||
#: src/view/screens/Settings.tsx:435
|
|
||||||
#: src/view/shell/Drawer.tsx:664
|
|
||||||
#~ msgid "{invitesAvailable} invite code available"
|
|
||||||
#~ msgstr "{invitesAvailable} convite disponível"
|
|
||||||
|
|
||||||
#: src/view/screens/Settings.tsx:437
|
|
||||||
#: src/view/shell/Drawer.tsx:666
|
|
||||||
#~ msgid "{invitesAvailable} invite codes available"
|
|
||||||
#~ msgstr "{invitesAvailable} convites disponíveis"
|
|
||||||
|
|
||||||
#: src/view/shell/Drawer.tsx:440
|
#: src/view/shell/Drawer.tsx:440
|
||||||
msgid "{numUnreadNotifications} unread"
|
msgid "{numUnreadNotifications} unread"
|
||||||
msgstr "{numUnreadNotifications} não lidas"
|
msgstr "{numUnreadNotifications} não lidas"
|
||||||
|
@ -179,11 +161,11 @@ msgstr "Adicionar prévia de link:"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:158
|
#: src/components/dialogs/MutedWords.tsx:158
|
||||||
msgid "Add mute word for configured settings"
|
msgid "Add mute word for configured settings"
|
||||||
msgstr ""
|
msgstr "Adicionar palavra silenciada para as configurações selecionadas"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:87
|
#: src/components/dialogs/MutedWords.tsx:87
|
||||||
msgid "Add muted words and tags"
|
msgid "Add muted words and tags"
|
||||||
msgstr ""
|
msgstr "Adicionar palavras/tags silenciadas"
|
||||||
|
|
||||||
#: src/view/com/modals/ChangeHandle.tsx:417
|
#: src/view/com/modals/ChangeHandle.tsx:417
|
||||||
msgid "Add the following DNS record to your domain:"
|
msgid "Add the following DNS record to your domain:"
|
||||||
|
@ -223,17 +205,13 @@ msgstr "Conteúdo Adulto"
|
||||||
msgid "Adult content can only be enabled via the Web at <0/>."
|
msgid "Adult content can only be enabled via the Web at <0/>."
|
||||||
msgstr "Conteúdo adulto só pode ser habilitado no site: <0/>."
|
msgstr "Conteúdo adulto só pode ser habilitado no site: <0/>."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:78
|
|
||||||
#~ msgid "Adult content can only be enabled via the Web at <0>bsky.app</0>."
|
|
||||||
#~ msgstr "Conteúdo adulto só pode ser habilitado no site: <0>bsky.app</0>."
|
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:664
|
#: src/view/screens/Settings/index.tsx:664
|
||||||
msgid "Advanced"
|
msgid "Advanced"
|
||||||
msgstr "Avançado"
|
msgstr "Avançado"
|
||||||
|
|
||||||
#: src/view/screens/Feeds.tsx:666
|
#: src/view/screens/Feeds.tsx:666
|
||||||
msgid "All the feeds you've saved, right in one place."
|
msgid "All the feeds you've saved, right in one place."
|
||||||
msgstr ""
|
msgstr "Todos os feeds que você salvou, em um único lugar."
|
||||||
|
|
||||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:221
|
#: src/view/com/auth/login/ForgotPasswordForm.tsx:221
|
||||||
#: src/view/com/modals/ChangePassword.tsx:168
|
#: src/view/com/modals/ChangePassword.tsx:168
|
||||||
|
@ -458,7 +436,7 @@ msgstr "Bluesky"
|
||||||
|
|
||||||
#: src/view/com/auth/server-input/index.tsx:150
|
#: src/view/com/auth/server-input/index.tsx:150
|
||||||
msgid "Bluesky is an open network where you can choose your hosting provider. Custom hosting is now available in beta for developers."
|
msgid "Bluesky is an open network where you can choose your hosting provider. Custom hosting is now available in beta for developers."
|
||||||
msgstr ""
|
msgstr "Bluesky é uma rede aberta que permite a escolha do seu provedor de hospedagem. Desenvolvedores já conseguem utilizar a versão beta de hospedagem própria."
|
||||||
|
|
||||||
#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:80
|
#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:80
|
||||||
#: src/view/com/auth/onboarding/WelcomeMobile.tsx:80
|
#: src/view/com/auth/onboarding/WelcomeMobile.tsx:80
|
||||||
|
@ -483,10 +461,6 @@ msgstr "Bluesky é público."
|
||||||
msgid "Bluesky will not show your profile and posts to logged-out users. Other apps may not honor this request. This does not make your account private."
|
msgid "Bluesky will not show your profile and posts to logged-out users. Other apps may not honor this request. This does not make your account private."
|
||||||
msgstr "O Bluesky não mostrará seu perfil e publicações para usuários desconectados. Outros aplicativos podem não honrar esta solicitação. Isso não torna a sua conta privada."
|
msgstr "O Bluesky não mostrará seu perfil e publicações para usuários desconectados. Outros aplicativos podem não honrar esta solicitação. Isso não torna a sua conta privada."
|
||||||
|
|
||||||
#: src/view/com/modals/ServerInput.tsx:78
|
|
||||||
#~ msgid "Bluesky.Social"
|
|
||||||
#~ msgstr "Bluesky.Social"
|
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:33
|
#: src/screens/Onboarding/index.tsx:33
|
||||||
msgid "Books"
|
msgid "Books"
|
||||||
msgstr "Livros"
|
msgstr "Livros"
|
||||||
|
@ -500,10 +474,6 @@ msgstr "Versão {0} {1}"
|
||||||
msgid "Business"
|
msgid "Business"
|
||||||
msgstr "Empresarial"
|
msgstr "Empresarial"
|
||||||
|
|
||||||
#: src/view/com/modals/ServerInput.tsx:115
|
|
||||||
#~ msgid "Button disabled. Input custom domain to proceed."
|
|
||||||
#~ msgstr "Botão desabilitado. Utilize um domínio personalizado para continuar."
|
|
||||||
|
|
||||||
#: src/view/com/profile/ProfileSubpageHeader.tsx:157
|
#: src/view/com/profile/ProfileSubpageHeader.tsx:157
|
||||||
msgid "by —"
|
msgid "by —"
|
||||||
msgstr "por -"
|
msgstr "por -"
|
||||||
|
@ -665,10 +635,6 @@ msgstr "Escolha os algoritmos que geram seus feeds customizados."
|
||||||
msgid "Choose the algorithms that power your experience with custom feeds."
|
msgid "Choose the algorithms that power your experience with custom feeds."
|
||||||
msgstr "Escolha os algoritmos que fazem sentido para você com os feeds personalizados."
|
msgstr "Escolha os algoritmos que fazem sentido para você com os feeds personalizados."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:103
|
|
||||||
#~ msgid "Choose your algorithmic feeds"
|
|
||||||
#~ msgstr "Escolha seus feeds algoritmicos"
|
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:103
|
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:103
|
||||||
msgid "Choose your main feeds"
|
msgid "Choose your main feeds"
|
||||||
msgstr "Escolha seus feeds principais"
|
msgstr "Escolha seus feeds principais"
|
||||||
|
@ -706,11 +672,11 @@ msgstr "clique aqui"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.web.tsx:138
|
#: src/components/TagMenu/index.web.tsx:138
|
||||||
msgid "Click here to open tag menu for {tag}"
|
msgid "Click here to open tag menu for {tag}"
|
||||||
msgstr ""
|
msgstr "Clique aqui para abrir o menu da tag {tag}"
|
||||||
|
|
||||||
#: src/components/RichText.tsx:191
|
#: src/components/RichText.tsx:191
|
||||||
msgid "Click here to open tag menu for #{tag}"
|
msgid "Click here to open tag menu for #{tag}"
|
||||||
msgstr ""
|
msgstr "Clique aqui para abrir o menu da tag #{tag}"
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:35
|
#: src/screens/Onboarding/index.tsx:35
|
||||||
msgid "Climate"
|
msgid "Climate"
|
||||||
|
@ -748,7 +714,7 @@ msgstr "Fechar o painel de navegação"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:262
|
#: src/components/TagMenu/index.tsx:262
|
||||||
msgid "Close this dialog"
|
msgid "Close this dialog"
|
||||||
msgstr ""
|
msgstr "Fechar esta janela"
|
||||||
|
|
||||||
#: src/view/shell/index.web.tsx:52
|
#: src/view/shell/index.web.tsx:52
|
||||||
msgid "Closes bottom navigation bar"
|
msgid "Closes bottom navigation bar"
|
||||||
|
@ -789,7 +755,7 @@ msgstr "Completar e começar a usar sua conta"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step3.tsx:73
|
#: src/view/com/auth/create/Step3.tsx:73
|
||||||
msgid "Complete the challenge"
|
msgid "Complete the challenge"
|
||||||
msgstr ""
|
msgstr "Complete o captcha"
|
||||||
|
|
||||||
#: src/view/com/composer/Composer.tsx:424
|
#: src/view/com/composer/Composer.tsx:424
|
||||||
msgid "Compose posts up to {MAX_GRAPHEME_LENGTH} characters in length"
|
msgid "Compose posts up to {MAX_GRAPHEME_LENGTH} characters in length"
|
||||||
|
@ -964,10 +930,6 @@ msgstr "Não foi possível carregar o feed"
|
||||||
msgid "Could not load list"
|
msgid "Could not load list"
|
||||||
msgstr "Não foi possível carregar a lista"
|
msgstr "Não foi possível carregar a lista"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:91
|
|
||||||
#~ msgid "Country"
|
|
||||||
#~ msgstr "País"
|
|
||||||
|
|
||||||
#: src/view/com/auth/HomeLoggedOutCTA.tsx:62
|
#: src/view/com/auth/HomeLoggedOutCTA.tsx:62
|
||||||
#: src/view/com/auth/SplashScreen.tsx:71
|
#: src/view/com/auth/SplashScreen.tsx:71
|
||||||
#: src/view/com/auth/SplashScreen.web.tsx:81
|
#: src/view/com/auth/SplashScreen.web.tsx:81
|
||||||
|
@ -1014,7 +976,7 @@ msgstr "Cultura"
|
||||||
#: src/view/com/auth/server-input/index.tsx:95
|
#: src/view/com/auth/server-input/index.tsx:95
|
||||||
#: src/view/com/auth/server-input/index.tsx:96
|
#: src/view/com/auth/server-input/index.tsx:96
|
||||||
msgid "Custom"
|
msgid "Custom"
|
||||||
msgstr ""
|
msgstr "Customizado"
|
||||||
|
|
||||||
#: src/view/com/modals/ChangeHandle.tsx:389
|
#: src/view/com/modals/ChangeHandle.tsx:389
|
||||||
msgid "Custom domain"
|
msgid "Custom domain"
|
||||||
|
@ -1029,10 +991,6 @@ msgstr "Feeds customizados feitos pela comunidade te proporcionam novas experiê
|
||||||
msgid "Customize media from external sites."
|
msgid "Customize media from external sites."
|
||||||
msgstr "Configurar mídia de sites externos."
|
msgstr "Configurar mídia de sites externos."
|
||||||
|
|
||||||
#: src/view/screens/Settings.tsx:687
|
|
||||||
#~ msgid "Danger Zone"
|
|
||||||
#~ msgstr "Zona Perigosa"
|
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:485
|
#: src/view/screens/Settings/index.tsx:485
|
||||||
#: src/view/screens/Settings/index.tsx:511
|
#: src/view/screens/Settings/index.tsx:511
|
||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
|
@ -1072,10 +1030,6 @@ msgstr "Excluir Lista"
|
||||||
msgid "Delete my account"
|
msgid "Delete my account"
|
||||||
msgstr "Excluir minha conta"
|
msgstr "Excluir minha conta"
|
||||||
|
|
||||||
#: src/view/screens/Settings.tsx:706
|
|
||||||
#~ msgid "Delete my account…"
|
|
||||||
#~ msgstr "Excluir minha conta…"
|
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:784
|
#: src/view/screens/Settings/index.tsx:784
|
||||||
msgid "Delete My Account…"
|
msgid "Delete My Account…"
|
||||||
msgstr "Excluir minha conta…"
|
msgstr "Excluir minha conta…"
|
||||||
|
@ -1133,13 +1087,9 @@ msgstr "Desencorajar aplicativos a mostrar minha conta para usuários deslogados
|
||||||
msgid "Discover new custom feeds"
|
msgid "Discover new custom feeds"
|
||||||
msgstr "Descubra novos feeds"
|
msgstr "Descubra novos feeds"
|
||||||
|
|
||||||
#: src/view/screens/Feeds.tsx:473
|
|
||||||
#~ msgid "Discover new feeds"
|
|
||||||
#~ msgstr "Descubra novos feeds"
|
|
||||||
|
|
||||||
#: src/view/screens/Feeds.tsx:689
|
#: src/view/screens/Feeds.tsx:689
|
||||||
msgid "Discover New Feeds"
|
msgid "Discover New Feeds"
|
||||||
msgstr ""
|
msgstr "Descubra Novos Feeds"
|
||||||
|
|
||||||
#: src/view/com/modals/EditProfile.tsx:192
|
#: src/view/com/modals/EditProfile.tsx:192
|
||||||
msgid "Display name"
|
msgid "Display name"
|
||||||
|
@ -1196,12 +1146,12 @@ msgstr "Toque duas vezes para logar"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:755
|
#: src/view/screens/Settings/index.tsx:755
|
||||||
msgid "Download Bluesky account data (repository)"
|
msgid "Download Bluesky account data (repository)"
|
||||||
msgstr ""
|
msgstr "Baixar os dados da minha conta Bluesky (repositório)"
|
||||||
|
|
||||||
#: src/view/screens/Settings/ExportCarDialog.tsx:59
|
#: src/view/screens/Settings/ExportCarDialog.tsx:59
|
||||||
#: src/view/screens/Settings/ExportCarDialog.tsx:63
|
#: src/view/screens/Settings/ExportCarDialog.tsx:63
|
||||||
msgid "Download CAR file"
|
msgid "Download CAR file"
|
||||||
msgstr ""
|
msgstr "Baixar arquivo CAR"
|
||||||
|
|
||||||
#: src/view/com/composer/text-input/TextInput.web.tsx:249
|
#: src/view/com/composer/text-input/TextInput.web.tsx:249
|
||||||
msgid "Drop to add images"
|
msgid "Drop to add images"
|
||||||
|
@ -1360,7 +1310,7 @@ msgstr "Insira um nome para esta Senha de Aplicativo"
|
||||||
#: src/components/dialogs/MutedWords.tsx:100
|
#: src/components/dialogs/MutedWords.tsx:100
|
||||||
#: src/components/dialogs/MutedWords.tsx:101
|
#: src/components/dialogs/MutedWords.tsx:101
|
||||||
msgid "Enter a word or tag"
|
msgid "Enter a word or tag"
|
||||||
msgstr ""
|
msgstr "Digite uma palavra ou tag"
|
||||||
|
|
||||||
#: src/view/com/modals/VerifyEmail.tsx:105
|
#: src/view/com/modals/VerifyEmail.tsx:105
|
||||||
msgid "Enter Confirmation Code"
|
msgid "Enter Confirmation Code"
|
||||||
|
@ -1399,17 +1349,13 @@ msgstr "Digite o novo e-mail acima"
|
||||||
msgid "Enter your new email address below."
|
msgid "Enter your new email address below."
|
||||||
msgstr "Digite seu novo endereço de e-mail abaixo."
|
msgstr "Digite seu novo endereço de e-mail abaixo."
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:188
|
|
||||||
#~ msgid "Enter your phone number"
|
|
||||||
#~ msgstr "Digite seu número de telefone"
|
|
||||||
|
|
||||||
#: src/view/com/auth/login/Login.tsx:99
|
#: src/view/com/auth/login/Login.tsx:99
|
||||||
msgid "Enter your username and password"
|
msgid "Enter your username and password"
|
||||||
msgstr "Digite seu nome de usuário e senha"
|
msgstr "Digite seu nome de usuário e senha"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step3.tsx:67
|
#: src/view/com/auth/create/Step3.tsx:67
|
||||||
msgid "Error receiving captcha response."
|
msgid "Error receiving captcha response."
|
||||||
msgstr ""
|
msgstr "Não foi possível processar o captcha."
|
||||||
|
|
||||||
#: src/view/screens/Search/Search.tsx:110
|
#: src/view/screens/Search/Search.tsx:110
|
||||||
msgid "Error:"
|
msgid "Error:"
|
||||||
|
@ -1447,12 +1393,12 @@ msgstr "Mostrar ou esconder o post a que você está respondendo"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:753
|
#: src/view/screens/Settings/index.tsx:753
|
||||||
msgid "Export my data"
|
msgid "Export my data"
|
||||||
msgstr ""
|
msgstr "Exportar meus dados"
|
||||||
|
|
||||||
#: src/view/screens/Settings/ExportCarDialog.tsx:44
|
#: src/view/screens/Settings/ExportCarDialog.tsx:44
|
||||||
#: src/view/screens/Settings/index.tsx:764
|
#: src/view/screens/Settings/index.tsx:764
|
||||||
msgid "Export My Data"
|
msgid "Export My Data"
|
||||||
msgstr ""
|
msgstr "Exportar Meus Dados"
|
||||||
|
|
||||||
#: src/view/com/modals/EmbedConsent.tsx:64
|
#: src/view/com/modals/EmbedConsent.tsx:64
|
||||||
msgid "External Media"
|
msgid "External Media"
|
||||||
|
@ -1523,14 +1469,6 @@ msgstr "Comentários"
|
||||||
msgid "Feeds"
|
msgid "Feeds"
|
||||||
msgstr "Feeds"
|
msgstr "Feeds"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:106
|
|
||||||
#~ msgid "Feeds are created by users and can give you entirely new experiences."
|
|
||||||
#~ msgstr "Feeds são criados por usuários e podem te dar experiências completamente únicas."
|
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:106
|
|
||||||
#~ msgid "Feeds are created by users and organizations. They offer you varied experiences and suggest content you may like using algorithms."
|
|
||||||
#~ msgstr "Feeds são criados por usuários ou organizações. Eles oferecem experiências únicas e podem te sugerir conteúdo usando algoritmos próprios."
|
|
||||||
|
|
||||||
#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:57
|
#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:57
|
||||||
msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting."
|
msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting."
|
||||||
msgstr "Os feeds são criados por usuários para curadoria de conteúdo. Escolha alguns feeds que você acha interessantes."
|
msgstr "Os feeds são criados por usuários para curadoria de conteúdo. Escolha alguns feeds que você acha interessantes."
|
||||||
|
@ -1567,11 +1505,7 @@ msgstr "Procurando contas semelhantes..."
|
||||||
|
|
||||||
#: src/view/screens/PreferencesFollowingFeed.tsx:111
|
#: src/view/screens/PreferencesFollowingFeed.tsx:111
|
||||||
msgid "Fine-tune the content you see on your Following feed."
|
msgid "Fine-tune the content you see on your Following feed."
|
||||||
msgstr ""
|
msgstr "Ajuste o conteúdo que você vê na sua tela inicial."
|
||||||
|
|
||||||
#: src/view/screens/PreferencesHomeFeed.tsx:111
|
|
||||||
#~ msgid "Fine-tune the content you see on your home screen."
|
|
||||||
#~ msgstr "Ajuste o conteúdo que você vê na sua tela inicial."
|
|
||||||
|
|
||||||
#: src/view/screens/PreferencesThreads.tsx:60
|
#: src/view/screens/PreferencesThreads.tsx:60
|
||||||
msgid "Fine-tune the discussion threads."
|
msgid "Fine-tune the discussion threads."
|
||||||
|
@ -1659,7 +1593,7 @@ msgstr "Seguindo {0}"
|
||||||
#: src/view/screens/PreferencesFollowingFeed.tsx:104
|
#: src/view/screens/PreferencesFollowingFeed.tsx:104
|
||||||
#: src/view/screens/Settings/index.tsx:543
|
#: src/view/screens/Settings/index.tsx:543
|
||||||
msgid "Following Feed Preferences"
|
msgid "Following Feed Preferences"
|
||||||
msgstr ""
|
msgstr "Configurações do feed principal"
|
||||||
|
|
||||||
#: src/view/com/profile/ProfileHeader.tsx:546
|
#: src/view/com/profile/ProfileHeader.tsx:546
|
||||||
msgid "Follows you"
|
msgid "Follows you"
|
||||||
|
@ -1697,7 +1631,7 @@ msgstr "Esqueci a Senha"
|
||||||
#: src/screens/Hashtag.tsx:108
|
#: src/screens/Hashtag.tsx:108
|
||||||
#: src/screens/Hashtag.tsx:148
|
#: src/screens/Hashtag.tsx:148
|
||||||
msgid "From @{sanitizedAuthor}"
|
msgid "From @{sanitizedAuthor}"
|
||||||
msgstr ""
|
msgstr "De @{sanitizedAuthor}"
|
||||||
|
|
||||||
#: src/view/com/posts/FeedItem.tsx:189
|
#: src/view/com/posts/FeedItem.tsx:189
|
||||||
msgctxt "from-feed"
|
msgctxt "from-feed"
|
||||||
|
@ -1751,15 +1685,15 @@ msgstr "Usuário"
|
||||||
|
|
||||||
#: src/Navigation.tsx:270
|
#: src/Navigation.tsx:270
|
||||||
msgid "Hashtag"
|
msgid "Hashtag"
|
||||||
msgstr ""
|
msgstr "Hashtag"
|
||||||
|
|
||||||
#: src/components/RichText.tsx:188
|
#: src/components/RichText.tsx:188
|
||||||
#~ msgid "Hashtag: {tag}"
|
#~ msgid "Hashtag: {tag}"
|
||||||
#~ msgstr ""
|
#~ msgstr "Hashtag: {tag}"
|
||||||
|
|
||||||
#: src/components/RichText.tsx:190
|
#: src/components/RichText.tsx:190
|
||||||
msgid "Hashtag: #{tag}"
|
msgid "Hashtag: #{tag}"
|
||||||
msgstr ""
|
msgstr "Hashtag: #{tag}"
|
||||||
|
|
||||||
#: src/view/com/auth/create/CreateAccount.tsx:208
|
#: src/view/com/auth/create/CreateAccount.tsx:208
|
||||||
msgid "Having trouble?"
|
msgid "Having trouble?"
|
||||||
|
@ -1930,10 +1864,6 @@ msgstr "Insira a nova senha"
|
||||||
msgid "Input password for account deletion"
|
msgid "Input password for account deletion"
|
||||||
msgstr "Insira a senha para excluir a conta"
|
msgstr "Insira a senha para excluir a conta"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:196
|
|
||||||
#~ msgid "Input phone number for SMS verification"
|
|
||||||
#~ msgstr "Insira o número de telefone para verificação via SMS"
|
|
||||||
|
|
||||||
#: src/view/com/auth/login/LoginForm.tsx:230
|
#: src/view/com/auth/login/LoginForm.tsx:230
|
||||||
msgid "Input the password tied to {identifier}"
|
msgid "Input the password tied to {identifier}"
|
||||||
msgstr "Insira a senha da conta {identifier}"
|
msgstr "Insira a senha da conta {identifier}"
|
||||||
|
@ -1942,10 +1872,6 @@ msgstr "Insira a senha da conta {identifier}"
|
||||||
msgid "Input the username or email address you used at signup"
|
msgid "Input the username or email address you used at signup"
|
||||||
msgstr "Insira o usuário ou e-mail que você cadastrou"
|
msgstr "Insira o usuário ou e-mail que você cadastrou"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:271
|
|
||||||
#~ msgid "Input the verification code we have texted to you"
|
|
||||||
#~ msgstr "Insira o código de verificação que enviamos para você"
|
|
||||||
|
|
||||||
#: src/view/com/modals/Waitlist.tsx:90
|
#: src/view/com/modals/Waitlist.tsx:90
|
||||||
#~ msgid "Input your email to get on the Bluesky waitlist"
|
#~ msgid "Input your email to get on the Bluesky waitlist"
|
||||||
#~ msgstr "Insira seu e-mail para entrar na lista de espera do Bluesky"
|
#~ msgstr "Insira seu e-mail para entrar na lista de espera do Bluesky"
|
||||||
|
@ -1966,10 +1892,6 @@ msgstr "Post inválido"
|
||||||
msgid "Invalid username or password"
|
msgid "Invalid username or password"
|
||||||
msgstr "Credenciais inválidas"
|
msgstr "Credenciais inválidas"
|
||||||
|
|
||||||
#: src/view/screens/Settings.tsx:411
|
|
||||||
#~ msgid "Invite"
|
|
||||||
#~ msgstr "Convidar"
|
|
||||||
|
|
||||||
#: src/view/com/modals/InviteCodes.tsx:93
|
#: src/view/com/modals/InviteCodes.tsx:93
|
||||||
msgid "Invite a Friend"
|
msgid "Invite a Friend"
|
||||||
msgstr "Convide um Amigo"
|
msgstr "Convide um Amigo"
|
||||||
|
@ -1987,10 +1909,6 @@ msgstr "Convite inválido. Verifique se você o inseriu corretamente e tente nov
|
||||||
msgid "Invite codes: {0} available"
|
msgid "Invite codes: {0} available"
|
||||||
msgstr "Convites: {0} disponíveis"
|
msgstr "Convites: {0} disponíveis"
|
||||||
|
|
||||||
#: src/view/shell/Drawer.tsx:645
|
|
||||||
#~ msgid "Invite codes: {invitesAvailable} available"
|
|
||||||
#~ msgstr "Convites: {invitesAvailable} disponível"
|
|
||||||
|
|
||||||
#: src/view/com/modals/InviteCodes.tsx:169
|
#: src/view/com/modals/InviteCodes.tsx:169
|
||||||
msgid "Invite codes: 1 available"
|
msgid "Invite codes: 1 available"
|
||||||
msgstr "Convites: 1 disponível"
|
msgstr "Convites: 1 disponível"
|
||||||
|
@ -2232,15 +2150,15 @@ msgstr "Certifique-se de onde está indo!"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:83
|
#: src/components/dialogs/MutedWords.tsx:83
|
||||||
msgid "Manage your muted words and tags"
|
msgid "Manage your muted words and tags"
|
||||||
msgstr ""
|
msgstr "Gerencie suas palavras/tags silenciadas"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:118
|
#: src/view/com/auth/create/Step2.tsx:118
|
||||||
msgid "May not be longer than 253 characters"
|
msgid "May not be longer than 253 characters"
|
||||||
msgstr ""
|
msgstr "Não pode ter mais que 253 caracteres"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:109
|
#: src/view/com/auth/create/Step2.tsx:109
|
||||||
msgid "May only contain letters and numbers"
|
msgid "May only contain letters and numbers"
|
||||||
msgstr ""
|
msgstr "Só pode conter letras e números"
|
||||||
|
|
||||||
#: src/view/screens/Profile.tsx:182
|
#: src/view/screens/Profile.tsx:182
|
||||||
msgid "Media"
|
msgid "Media"
|
||||||
|
@ -2332,15 +2250,15 @@ msgstr "Respostas mais curtidas primeiro"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:122
|
#: src/view/com/auth/create/Step2.tsx:122
|
||||||
msgid "Must be at least 3 characters"
|
msgid "Must be at least 3 characters"
|
||||||
msgstr ""
|
msgstr "Deve ter no mínimo 3 caracteres"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:249
|
#: src/components/TagMenu/index.tsx:249
|
||||||
msgid "Mute"
|
msgid "Mute"
|
||||||
msgstr ""
|
msgstr "Silenciar"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.web.tsx:105
|
#: src/components/TagMenu/index.web.tsx:105
|
||||||
msgid "Mute {truncatedTag}"
|
msgid "Mute {truncatedTag}"
|
||||||
msgstr ""
|
msgstr "Silenciar {truncatedTag}"
|
||||||
|
|
||||||
#: src/view/com/profile/ProfileHeader.tsx:327
|
#: src/view/com/profile/ProfileHeader.tsx:327
|
||||||
msgid "Mute Account"
|
msgid "Mute Account"
|
||||||
|
@ -2352,19 +2270,19 @@ msgstr "Silenciar contas"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:209
|
#: src/components/TagMenu/index.tsx:209
|
||||||
msgid "Mute all {displayTag} posts"
|
msgid "Mute all {displayTag} posts"
|
||||||
msgstr ""
|
msgstr "Silenciar posts com {displayTag}"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:211
|
#: src/components/TagMenu/index.tsx:211
|
||||||
#~ msgid "Mute all {tag} posts"
|
#~ msgid "Mute all {tag} posts"
|
||||||
#~ msgstr ""
|
#~ msgstr "Silenciar posts com {tag}"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:149
|
#: src/components/dialogs/MutedWords.tsx:149
|
||||||
msgid "Mute in tags only"
|
msgid "Mute in tags only"
|
||||||
msgstr ""
|
msgstr "Silenciar apenas as tags"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:134
|
#: src/components/dialogs/MutedWords.tsx:134
|
||||||
msgid "Mute in text & tags"
|
msgid "Mute in text & tags"
|
||||||
msgstr ""
|
msgstr "Silenciar texto e tags"
|
||||||
|
|
||||||
#: src/view/screens/ProfileList.tsx:491
|
#: src/view/screens/ProfileList.tsx:491
|
||||||
msgid "Mute list"
|
msgid "Mute list"
|
||||||
|
@ -2380,11 +2298,11 @@ msgstr "Silenciar esta lista"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:127
|
#: src/components/dialogs/MutedWords.tsx:127
|
||||||
msgid "Mute this word in post text and tags"
|
msgid "Mute this word in post text and tags"
|
||||||
msgstr ""
|
msgstr "Silenciar esta palavra no conteúdo de um post e tags"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:142
|
#: src/components/dialogs/MutedWords.tsx:142
|
||||||
msgid "Mute this word in tags only"
|
msgid "Mute this word in tags only"
|
||||||
msgstr ""
|
msgstr "Silenciar esta palavra apenas nas tags de um post"
|
||||||
|
|
||||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:251
|
#: src/view/com/util/forms/PostDropdownBtn.tsx:251
|
||||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:257
|
#: src/view/com/util/forms/PostDropdownBtn.tsx:257
|
||||||
|
@ -2394,7 +2312,7 @@ msgstr "Silenciar thread"
|
||||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:267
|
#: src/view/com/util/forms/PostDropdownBtn.tsx:267
|
||||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:269
|
#: src/view/com/util/forms/PostDropdownBtn.tsx:269
|
||||||
msgid "Mute words & tags"
|
msgid "Mute words & tags"
|
||||||
msgstr ""
|
msgstr "Silenciar palavras/tags"
|
||||||
|
|
||||||
#: src/view/com/lists/ListCard.tsx:102
|
#: src/view/com/lists/ListCard.tsx:102
|
||||||
msgid "Muted"
|
msgid "Muted"
|
||||||
|
@ -2415,7 +2333,7 @@ msgstr "Contas silenciadas não aparecem no seu feed ou nas suas notificações.
|
||||||
|
|
||||||
#: src/view/screens/Moderation.tsx:100
|
#: src/view/screens/Moderation.tsx:100
|
||||||
msgid "Muted words & tags"
|
msgid "Muted words & tags"
|
||||||
msgstr ""
|
msgstr "Palavras/tags silenciadas"
|
||||||
|
|
||||||
#: src/view/screens/ProfileList.tsx:277
|
#: src/view/screens/ProfileList.tsx:277
|
||||||
msgid "Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them."
|
msgid "Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them."
|
||||||
|
@ -2439,7 +2357,7 @@ msgstr "Meus Feeds Salvos"
|
||||||
|
|
||||||
#: src/view/com/auth/server-input/index.tsx:118
|
#: src/view/com/auth/server-input/index.tsx:118
|
||||||
msgid "my-server.com"
|
msgid "my-server.com"
|
||||||
msgstr ""
|
msgstr "meu-servidor.com.br"
|
||||||
|
|
||||||
#: src/view/com/modals/AddAppPasswords.tsx:179
|
#: src/view/com/modals/AddAppPasswords.tsx:179
|
||||||
#: src/view/com/modals/CreateOrEditList.tsx:290
|
#: src/view/com/modals/CreateOrEditList.tsx:290
|
||||||
|
@ -2482,7 +2400,7 @@ msgstr "Nunca perca o acesso aos seus seguidores ou dados."
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:293
|
#: src/components/dialogs/MutedWords.tsx:293
|
||||||
msgid "Nevermind"
|
msgid "Nevermind"
|
||||||
msgstr ""
|
msgstr "Deixa pra lá"
|
||||||
|
|
||||||
#: src/view/screens/Lists.tsx:76
|
#: src/view/screens/Lists.tsx:76
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
|
@ -2587,7 +2505,7 @@ msgstr "Nenhum resultado"
|
||||||
|
|
||||||
#: src/components/Lists.tsx:192
|
#: src/components/Lists.tsx:192
|
||||||
msgid "No results found"
|
msgid "No results found"
|
||||||
msgstr ""
|
msgstr "Nenhum resultado encontrado"
|
||||||
|
|
||||||
#: src/view/screens/Feeds.tsx:495
|
#: src/view/screens/Feeds.tsx:495
|
||||||
msgid "No results found for \"{query}\""
|
msgid "No results found for \"{query}\""
|
||||||
|
@ -2669,7 +2587,7 @@ msgstr "Apenas {0} pode responder."
|
||||||
|
|
||||||
#: src/components/Lists.tsx:82
|
#: src/components/Lists.tsx:82
|
||||||
msgid "Oops, something went wrong!"
|
msgid "Oops, something went wrong!"
|
||||||
msgstr ""
|
msgstr "Opa, algo deu errado!"
|
||||||
|
|
||||||
#: src/components/Lists.tsx:188
|
#: src/components/Lists.tsx:188
|
||||||
#: src/view/screens/AppPasswords.tsx:65
|
#: src/view/screens/AppPasswords.tsx:65
|
||||||
|
@ -2683,7 +2601,7 @@ msgstr "Abrir"
|
||||||
|
|
||||||
#: src/view/screens/Moderation.tsx:75
|
#: src/view/screens/Moderation.tsx:75
|
||||||
msgid "Open content filtering settings"
|
msgid "Open content filtering settings"
|
||||||
msgstr ""
|
msgstr "Abrir configurações de filtro"
|
||||||
|
|
||||||
#: src/view/com/composer/Composer.tsx:477
|
#: src/view/com/composer/Composer.tsx:477
|
||||||
#: src/view/com/composer/Composer.tsx:478
|
#: src/view/com/composer/Composer.tsx:478
|
||||||
|
@ -2696,7 +2614,7 @@ msgstr "Abrir links no navegador interno"
|
||||||
|
|
||||||
#: src/view/screens/Moderation.tsx:92
|
#: src/view/screens/Moderation.tsx:92
|
||||||
msgid "Open muted words settings"
|
msgid "Open muted words settings"
|
||||||
msgstr ""
|
msgstr "Abrir configurações das palavras silenciadas"
|
||||||
|
|
||||||
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:50
|
#: src/view/com/home/HomeHeaderLayoutMobile.tsx:50
|
||||||
msgid "Open navigation"
|
msgid "Open navigation"
|
||||||
|
@ -2704,7 +2622,7 @@ msgstr "Abrir navegação"
|
||||||
|
|
||||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:175
|
#: src/view/com/util/forms/PostDropdownBtn.tsx:175
|
||||||
msgid "Open post options menu"
|
msgid "Open post options menu"
|
||||||
msgstr ""
|
msgstr "Abrir opções do post"
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:804
|
#: src/view/screens/Settings/index.tsx:804
|
||||||
msgid "Open storybook page"
|
msgid "Open storybook page"
|
||||||
|
@ -2754,10 +2672,6 @@ msgstr "Abre lista de seguidores"
|
||||||
msgid "Opens following list"
|
msgid "Opens following list"
|
||||||
msgstr "Abre lista de seguidos"
|
msgstr "Abre lista de seguidos"
|
||||||
|
|
||||||
#: src/view/screens/Settings.tsx:412
|
|
||||||
#~ msgid "Opens invite code list"
|
|
||||||
#~ msgstr "Abre lista de convites"
|
|
||||||
|
|
||||||
#: src/view/com/modals/InviteCodes.tsx:172
|
#: src/view/com/modals/InviteCodes.tsx:172
|
||||||
msgid "Opens list of invite codes"
|
msgid "Opens list of invite codes"
|
||||||
msgstr "Abre a lista de códigos de convite"
|
msgstr "Abre a lista de códigos de convite"
|
||||||
|
@ -2819,10 +2733,6 @@ msgstr "Ou combine estas opções:"
|
||||||
msgid "Other account"
|
msgid "Other account"
|
||||||
msgstr "Outra conta"
|
msgstr "Outra conta"
|
||||||
|
|
||||||
#: src/view/com/modals/ServerInput.tsx:88
|
|
||||||
#~ msgid "Other service"
|
|
||||||
#~ msgstr "Outro serviço"
|
|
||||||
|
|
||||||
#: src/view/com/composer/select-language/SelectLangBtn.tsx:91
|
#: src/view/com/composer/select-language/SelectLangBtn.tsx:91
|
||||||
msgid "Other..."
|
msgid "Other..."
|
||||||
msgstr "Outro..."
|
msgstr "Outro..."
|
||||||
|
@ -2872,10 +2782,6 @@ msgstr "A permissão de galeria foi recusada. Por favor, habilite-a nas configur
|
||||||
msgid "Pets"
|
msgid "Pets"
|
||||||
msgstr "Pets"
|
msgstr "Pets"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:183
|
|
||||||
#~ msgid "Phone number"
|
|
||||||
#~ msgstr "Número de telefone"
|
|
||||||
|
|
||||||
#: src/view/com/modals/SelfLabel.tsx:121
|
#: src/view/com/modals/SelfLabel.tsx:121
|
||||||
msgid "Pictures meant for adults."
|
msgid "Pictures meant for adults."
|
||||||
msgstr "Imagens destinadas a adultos."
|
msgstr "Imagens destinadas a adultos."
|
||||||
|
@ -2912,7 +2818,7 @@ msgstr "Por favor, escolha sua senha."
|
||||||
|
|
||||||
#: src/view/com/auth/create/state.ts:131
|
#: src/view/com/auth/create/state.ts:131
|
||||||
msgid "Please complete the verification captcha."
|
msgid "Please complete the verification captcha."
|
||||||
msgstr ""
|
msgstr "Por favor, complete o captcha de verificação."
|
||||||
|
|
||||||
#: src/view/com/modals/ChangeEmail.tsx:67
|
#: src/view/com/modals/ChangeEmail.tsx:67
|
||||||
msgid "Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed."
|
msgid "Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed."
|
||||||
|
@ -2922,17 +2828,13 @@ msgstr "Por favor, confirme seu e-mail antes de alterá-lo. Este é um requisito
|
||||||
msgid "Please enter a name for your app password. All spaces is not allowed."
|
msgid "Please enter a name for your app password. All spaces is not allowed."
|
||||||
msgstr "Por favor, insira um nome para a sua Senha de Aplicativo."
|
msgstr "Por favor, insira um nome para a sua Senha de Aplicativo."
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:206
|
|
||||||
#~ msgid "Please enter a phone number that can receive SMS text messages."
|
|
||||||
#~ msgstr "Por favor, insira um número de telefone que possa receber mensagens SMS."
|
|
||||||
|
|
||||||
#: src/view/com/modals/AddAppPasswords.tsx:145
|
#: src/view/com/modals/AddAppPasswords.tsx:145
|
||||||
msgid "Please enter a unique name for this App Password or use our randomly generated one."
|
msgid "Please enter a unique name for this App Password or use our randomly generated one."
|
||||||
msgstr "Por favor, insira um nome único para esta Senha de Aplicativo ou use nosso nome gerado automaticamente."
|
msgstr "Por favor, insira um nome único para esta Senha de Aplicativo ou use nosso nome gerado automaticamente."
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:68
|
#: src/components/dialogs/MutedWords.tsx:68
|
||||||
msgid "Please enter a valid word, tag, or phrase to mute"
|
msgid "Please enter a valid word, tag, or phrase to mute"
|
||||||
msgstr ""
|
msgstr "Por favor, insira uma palavra, tag ou frase para silenciar"
|
||||||
|
|
||||||
#: src/view/com/auth/create/state.ts:170
|
#: src/view/com/auth/create/state.ts:170
|
||||||
#~ msgid "Please enter the code you received by SMS."
|
#~ msgid "Please enter the code you received by SMS."
|
||||||
|
@ -2955,11 +2857,6 @@ msgstr "Por favor, digite sua senha também:"
|
||||||
msgid "Please tell us why you think this content warning was incorrectly applied!"
|
msgid "Please tell us why you think this content warning was incorrectly applied!"
|
||||||
msgstr "Por favor, diga-nos por que você acha que este aviso de conteúdo foi aplicado incorretamente!"
|
msgstr "Por favor, diga-nos por que você acha que este aviso de conteúdo foi aplicado incorretamente!"
|
||||||
|
|
||||||
#: src/view/com/modals/AppealLabel.tsx:72
|
|
||||||
#: src/view/com/modals/AppealLabel.tsx:75
|
|
||||||
#~ msgid "Please tell us why you think this decision was incorrect."
|
|
||||||
#~ msgstr "Por favor, conte-nos por que achou que esta decisão está incorreta."
|
|
||||||
|
|
||||||
#: src/view/com/modals/VerifyEmail.tsx:101
|
#: src/view/com/modals/VerifyEmail.tsx:101
|
||||||
msgid "Please Verify Your Email"
|
msgid "Please Verify Your Email"
|
||||||
msgstr "Por favor, verifique seu e-mail"
|
msgstr "Por favor, verifique seu e-mail"
|
||||||
|
@ -3019,7 +2916,7 @@ msgstr "Post não encontrado"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:253
|
#: src/components/TagMenu/index.tsx:253
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr ""
|
msgstr "posts"
|
||||||
|
|
||||||
#: src/view/screens/Profile.tsx:180
|
#: src/view/screens/Profile.tsx:180
|
||||||
msgid "Posts"
|
msgid "Posts"
|
||||||
|
@ -3027,7 +2924,7 @@ msgstr "Posts"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:90
|
#: src/components/dialogs/MutedWords.tsx:90
|
||||||
msgid "Posts can be muted based on their text, their tags, or both."
|
msgid "Posts can be muted based on their text, their tags, or both."
|
||||||
msgstr ""
|
msgstr "Posts podem ser silenciados baseados no seu conteúdo, tags ou ambos."
|
||||||
|
|
||||||
#: src/view/com/posts/FeedErrorMessage.tsx:64
|
#: src/view/com/posts/FeedErrorMessage.tsx:64
|
||||||
msgid "Posts hidden"
|
msgid "Posts hidden"
|
||||||
|
@ -3171,7 +3068,7 @@ msgstr "Remover visualização da imagem"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:343
|
#: src/components/dialogs/MutedWords.tsx:343
|
||||||
msgid "Remove mute word from your list"
|
msgid "Remove mute word from your list"
|
||||||
msgstr ""
|
msgstr "Remover palavra silenciada da lista"
|
||||||
|
|
||||||
#: src/view/com/modals/Repost.tsx:47
|
#: src/view/com/modals/Repost.tsx:47
|
||||||
msgid "Remove repost"
|
msgid "Remove repost"
|
||||||
|
@ -3286,10 +3183,6 @@ msgstr "Reposts"
|
||||||
msgid "Request Change"
|
msgid "Request Change"
|
||||||
msgstr "Solicitar Alteração"
|
msgstr "Solicitar Alteração"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:219
|
|
||||||
#~ msgid "Request code"
|
|
||||||
#~ msgstr "Solicitar código"
|
|
||||||
|
|
||||||
#: src/view/com/modals/ChangePassword.tsx:239
|
#: src/view/com/modals/ChangePassword.tsx:239
|
||||||
#: src/view/com/modals/ChangePassword.tsx:241
|
#: src/view/com/modals/ChangePassword.tsx:241
|
||||||
msgid "Request Code"
|
msgid "Request Code"
|
||||||
|
@ -3368,10 +3261,6 @@ msgstr "Tente novamente"
|
||||||
msgid "Return to previous page"
|
msgid "Return to previous page"
|
||||||
msgstr "Voltar para página anterior"
|
msgstr "Voltar para página anterior"
|
||||||
|
|
||||||
#: src/view/shell/desktop/RightNav.tsx:55
|
|
||||||
#~ msgid "SANDBOX. Posts and accounts are not permanent."
|
|
||||||
#~ msgstr "SANDBOX. Posts e contas não são permanentes."
|
|
||||||
|
|
||||||
#: src/view/com/lightbox/Lightbox.tsx:132
|
#: src/view/com/lightbox/Lightbox.tsx:132
|
||||||
#: src/view/com/modals/CreateOrEditList.tsx:345
|
#: src/view/com/modals/CreateOrEditList.tsx:345
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
|
@ -3447,19 +3336,19 @@ msgstr "Pesquisar por \"{query}\""
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:145
|
#: src/components/TagMenu/index.tsx:145
|
||||||
msgid "Search for all posts by @{authorHandle} with tag {displayTag}"
|
msgid "Search for all posts by @{authorHandle} with tag {displayTag}"
|
||||||
msgstr ""
|
msgstr "Pesquisar por posts de @{authorHandle} com a tag {displayTag}"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:145
|
#: src/components/TagMenu/index.tsx:145
|
||||||
#~ msgid "Search for all posts by @{authorHandle} with tag {tag}"
|
#~ msgid "Search for all posts by @{authorHandle} with tag {tag}"
|
||||||
#~ msgstr ""
|
#~ msgstr "Pesquisar por posts de @{authorHandle} com a tag {tag}"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:94
|
#: src/components/TagMenu/index.tsx:94
|
||||||
msgid "Search for all posts with tag {displayTag}"
|
msgid "Search for all posts with tag {displayTag}"
|
||||||
msgstr ""
|
msgstr "Pesquisar por posts com a tag {displayTag}"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:90
|
#: src/components/TagMenu/index.tsx:90
|
||||||
#~ msgid "Search for all posts with tag {tag}"
|
#~ msgid "Search for all posts with tag {tag}"
|
||||||
#~ msgstr ""
|
#~ msgstr "Pesquisar por posts com a tag {tag}"
|
||||||
|
|
||||||
#: src/view/com/auth/LoggedOut.tsx:104
|
#: src/view/com/auth/LoggedOut.tsx:104
|
||||||
#: src/view/com/auth/LoggedOut.tsx:105
|
#: src/view/com/auth/LoggedOut.tsx:105
|
||||||
|
@ -3473,27 +3362,27 @@ msgstr "Passo de Segurança Necessário"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.web.tsx:66
|
#: src/components/TagMenu/index.web.tsx:66
|
||||||
msgid "See {truncatedTag} posts"
|
msgid "See {truncatedTag} posts"
|
||||||
msgstr ""
|
msgstr "Ver posts com {truncatedTag}"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.web.tsx:83
|
#: src/components/TagMenu/index.web.tsx:83
|
||||||
msgid "See {truncatedTag} posts by user"
|
msgid "See {truncatedTag} posts by user"
|
||||||
msgstr ""
|
msgstr "Ver posts com {truncatedTag} deste usuário"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:128
|
#: src/components/TagMenu/index.tsx:128
|
||||||
msgid "See <0>{displayTag}</0> posts"
|
msgid "See <0>{displayTag}</0> posts"
|
||||||
msgstr ""
|
msgstr "Ver posts com <0>{displayTag}</0>"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:187
|
#: src/components/TagMenu/index.tsx:187
|
||||||
msgid "See <0>{displayTag}</0> posts by this user"
|
msgid "See <0>{displayTag}</0> posts by this user"
|
||||||
msgstr ""
|
msgstr "Ver posts com <0>{displayTag}</0> deste usuário"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:128
|
#: src/components/TagMenu/index.tsx:128
|
||||||
#~ msgid "See <0>{tag}</0> posts"
|
#~ msgid "See <0>{tag}</0> posts"
|
||||||
#~ msgstr ""
|
#~ msgstr "Ver posts com <0>{tag}</0>"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:189
|
#: src/components/TagMenu/index.tsx:189
|
||||||
#~ msgid "See <0>{tag}</0> posts by this user"
|
#~ msgid "See <0>{tag}</0> posts by this user"
|
||||||
#~ msgstr ""
|
#~ msgstr "Ver posts com <0>{tag}</0> deste usuário"
|
||||||
|
|
||||||
#: src/view/screens/SavedFeeds.tsx:163
|
#: src/view/screens/SavedFeeds.tsx:163
|
||||||
msgid "See this guide"
|
msgid "See this guide"
|
||||||
|
@ -3507,10 +3396,6 @@ msgstr "Veja o que vem por aí"
|
||||||
msgid "Select {item}"
|
msgid "Select {item}"
|
||||||
msgstr "Selecionar {item}"
|
msgstr "Selecionar {item}"
|
||||||
|
|
||||||
#: src/view/com/modals/ServerInput.tsx:75
|
|
||||||
#~ msgid "Select Bluesky Social"
|
|
||||||
#~ msgstr "Selecionar Bluesky Social"
|
|
||||||
|
|
||||||
#: src/view/com/auth/login/Login.tsx:117
|
#: src/view/com/auth/login/Login.tsx:117
|
||||||
msgid "Select from an existing account"
|
msgid "Select from an existing account"
|
||||||
msgstr "Selecionar de uma conta existente"
|
msgstr "Selecionar de uma conta existente"
|
||||||
|
@ -3530,11 +3415,7 @@ msgstr "Selecione algumas contas para seguir"
|
||||||
|
|
||||||
#: src/view/com/auth/server-input/index.tsx:82
|
#: src/view/com/auth/server-input/index.tsx:82
|
||||||
msgid "Select the service that hosts your data."
|
msgid "Select the service that hosts your data."
|
||||||
msgstr ""
|
msgstr "Selecione o serviço que hospeda seus dados."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepModeration/index.tsx:49
|
|
||||||
#~ msgid "Select the types of content that you want to see (or not see), and we'll handle the rest."
|
|
||||||
#~ msgstr "Selecione os tipos de conteúdo que você quer (ou não) ver, e cuidaremos do resto."
|
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepTopicalFeeds.tsx:96
|
#: src/screens/Onboarding/StepTopicalFeeds.tsx:96
|
||||||
msgid "Select topical feeds to follow from the list below"
|
msgid "Select topical feeds to follow from the list below"
|
||||||
|
@ -3556,10 +3437,6 @@ msgstr "Selecione o idioma do seu aplicativo"
|
||||||
msgid "Select your interests from the options below"
|
msgid "Select your interests from the options below"
|
||||||
msgstr "Selecione seus interesses"
|
msgstr "Selecione seus interesses"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:155
|
|
||||||
#~ msgid "Select your phone's country"
|
|
||||||
#~ msgstr "Selecione o país do número de telefone"
|
|
||||||
|
|
||||||
#: src/view/screens/LanguageSettings.tsx:190
|
#: src/view/screens/LanguageSettings.tsx:190
|
||||||
msgid "Select your preferred language for translations in your feed."
|
msgid "Select your preferred language for translations in your feed."
|
||||||
msgstr "Selecione seu idioma preferido para as traduções no seu feed."
|
msgstr "Selecione seu idioma preferido para as traduções no seu feed."
|
||||||
|
@ -3601,7 +3478,7 @@ msgstr "Envia o e-mail com o código de confirmação para excluir a conta"
|
||||||
|
|
||||||
#: src/view/com/auth/server-input/index.tsx:110
|
#: src/view/com/auth/server-input/index.tsx:110
|
||||||
msgid "Server address"
|
msgid "Server address"
|
||||||
msgstr ""
|
msgstr "URL do servidor"
|
||||||
|
|
||||||
#: src/view/com/modals/ContentFilteringSettings.tsx:311
|
#: src/view/com/modals/ContentFilteringSettings.tsx:311
|
||||||
msgid "Set {value} for {labelGroup} content moderation policy"
|
msgid "Set {value} for {labelGroup} content moderation policy"
|
||||||
|
@ -3657,13 +3534,9 @@ msgstr "Defina esta configuração como \"Não\" para ocultar todos os reposts d
|
||||||
msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature."
|
msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature."
|
||||||
msgstr "Defina esta configuração como \"Sim\" para mostrar respostas em uma visualização de thread. Este é um recurso experimental."
|
msgstr "Defina esta configuração como \"Sim\" para mostrar respostas em uma visualização de thread. Este é um recurso experimental."
|
||||||
|
|
||||||
#: src/view/screens/PreferencesHomeFeed.tsx:261
|
|
||||||
#~ msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature."
|
|
||||||
#~ msgstr "Defina esta configuração como \"Sim\" para mostrar amostras de seus feeds salvos na sua página inicial. Este é um recurso experimental."
|
|
||||||
|
|
||||||
#: src/view/screens/PreferencesFollowingFeed.tsx:261
|
#: src/view/screens/PreferencesFollowingFeed.tsx:261
|
||||||
msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your Following feed. This is an experimental feature."
|
msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your Following feed. This is an experimental feature."
|
||||||
msgstr ""
|
msgstr "Defina esta configuração como \"Sim\" para exibir amostras de seus feeds salvos no seu feed inicial. Este é um recurso experimental."
|
||||||
|
|
||||||
#: src/screens/Onboarding/Layout.tsx:50
|
#: src/screens/Onboarding/Layout.tsx:50
|
||||||
msgid "Set up your account"
|
msgid "Set up your account"
|
||||||
|
@ -3893,10 +3766,6 @@ msgstr "Pular"
|
||||||
msgid "Skip this flow"
|
msgid "Skip this flow"
|
||||||
msgstr "Pular"
|
msgstr "Pular"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:82
|
|
||||||
#~ msgid "SMS verification"
|
|
||||||
#~ msgstr "Verificação por SMS"
|
|
||||||
|
|
||||||
#: src/screens/Onboarding/index.tsx:40
|
#: src/screens/Onboarding/index.tsx:40
|
||||||
msgid "Software Dev"
|
msgid "Software Dev"
|
||||||
msgstr "Desenvolvimento de software"
|
msgstr "Desenvolvimento de software"
|
||||||
|
@ -3907,7 +3776,7 @@ msgstr "Desenvolvimento de software"
|
||||||
|
|
||||||
#: src/components/Lists.tsx:203
|
#: src/components/Lists.tsx:203
|
||||||
msgid "Something went wrong!"
|
msgid "Something went wrong!"
|
||||||
msgstr ""
|
msgstr "Algo deu errado!"
|
||||||
|
|
||||||
#: src/view/com/modals/Waitlist.tsx:51
|
#: src/view/com/modals/Waitlist.tsx:51
|
||||||
#~ msgid "Something went wrong. Check your email and try again."
|
#~ msgid "Something went wrong. Check your email and try again."
|
||||||
|
@ -3933,10 +3802,6 @@ msgstr "Esportes"
|
||||||
msgid "Square"
|
msgid "Square"
|
||||||
msgstr "Quadrado"
|
msgstr "Quadrado"
|
||||||
|
|
||||||
#: src/view/com/modals/ServerInput.tsx:62
|
|
||||||
#~ msgid "Staging"
|
|
||||||
#~ msgstr "Staging"
|
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:871
|
#: src/view/screens/Settings/index.tsx:871
|
||||||
msgid "Status page"
|
msgid "Status page"
|
||||||
msgstr "Página de status"
|
msgstr "Página de status"
|
||||||
|
@ -3989,10 +3854,6 @@ msgstr "Sugestivo"
|
||||||
msgid "Support"
|
msgid "Support"
|
||||||
msgstr "Suporte"
|
msgstr "Suporte"
|
||||||
|
|
||||||
#: src/view/com/modals/ProfilePreview.tsx:110
|
|
||||||
#~ msgid "Swipe up to see more"
|
|
||||||
#~ msgstr "Deslize para cima para ver mais"
|
|
||||||
|
|
||||||
#: src/view/com/modals/SwitchAccount.tsx:117
|
#: src/view/com/modals/SwitchAccount.tsx:117
|
||||||
msgid "Switch Account"
|
msgid "Switch Account"
|
||||||
msgstr "Alterar Conta"
|
msgstr "Alterar Conta"
|
||||||
|
@ -4017,15 +3878,15 @@ msgstr "Log do sistema"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:337
|
#: src/components/dialogs/MutedWords.tsx:337
|
||||||
msgid "tag"
|
msgid "tag"
|
||||||
msgstr ""
|
msgstr "tag"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:78
|
#: src/components/TagMenu/index.tsx:78
|
||||||
msgid "Tag menu: {displayTag}"
|
msgid "Tag menu: {displayTag}"
|
||||||
msgstr ""
|
msgstr "Menu da tag: {displayTag}"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:74
|
#: src/components/TagMenu/index.tsx:74
|
||||||
#~ msgid "Tag menu: {tag}"
|
#~ msgid "Tag menu: {tag}"
|
||||||
#~ msgstr ""
|
#~ msgstr "Menu da tag: {tag}"
|
||||||
|
|
||||||
#: src/view/com/modals/crop-image/CropImage.web.tsx:112
|
#: src/view/com/modals/crop-image/CropImage.web.tsx:112
|
||||||
msgid "Tall"
|
msgid "Tall"
|
||||||
|
@ -4052,7 +3913,7 @@ msgstr "Termos de Serviço"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:337
|
#: src/components/dialogs/MutedWords.tsx:337
|
||||||
msgid "text"
|
msgid "text"
|
||||||
msgstr ""
|
msgstr "texto"
|
||||||
|
|
||||||
#: src/view/com/modals/AppealLabel.tsx:70
|
#: src/view/com/modals/AppealLabel.tsx:70
|
||||||
#: src/view/com/modals/report/InputIssueDetails.tsx:51
|
#: src/view/com/modals/report/InputIssueDetails.tsx:51
|
||||||
|
@ -4061,7 +3922,7 @@ msgstr "Campo de entrada de texto"
|
||||||
|
|
||||||
#: src/view/com/auth/create/CreateAccount.tsx:94
|
#: src/view/com/auth/create/CreateAccount.tsx:94
|
||||||
msgid "That handle is already taken."
|
msgid "That handle is already taken."
|
||||||
msgstr ""
|
msgstr "Este identificador de usuário já está sendo usado."
|
||||||
|
|
||||||
#: src/view/com/profile/ProfileHeader.tsx:263
|
#: src/view/com/profile/ProfileHeader.tsx:263
|
||||||
msgid "The account will be able to interact with you after unblocking."
|
msgid "The account will be able to interact with you after unblocking."
|
||||||
|
@ -4179,10 +4040,6 @@ msgstr "Houve um problema inesperado no aplicativo. Por favor, deixe-nos saber s
|
||||||
msgid "There's been a rush of new users to Bluesky! We'll activate your account as soon as we can."
|
msgid "There's been a rush of new users to Bluesky! We'll activate your account as soon as we can."
|
||||||
msgstr "Muitos usuários estão tentando acessar o Bluesky! Ativaremos sua conta assim que possível."
|
msgstr "Muitos usuários estão tentando acessar o Bluesky! Ativaremos sua conta assim que possível."
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:55
|
|
||||||
#~ msgid "There's something wrong with this number. Please choose your country and enter your full phone number!"
|
|
||||||
#~ msgstr "Houve um problema com este número. Por favor, escolha um país e digite seu número de telefone completo!"
|
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:138
|
#: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:138
|
||||||
msgid "These are popular accounts you might like:"
|
msgid "These are popular accounts you might like:"
|
||||||
msgstr "Estas são contas populares que talvez você goste:"
|
msgstr "Estas são contas populares que talvez você goste:"
|
||||||
|
@ -4209,7 +4066,7 @@ msgstr "Este conteúdo não é visível sem uma conta do Bluesky."
|
||||||
|
|
||||||
#: src/view/screens/Settings/ExportCarDialog.tsx:75
|
#: src/view/screens/Settings/ExportCarDialog.tsx:75
|
||||||
msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost.</0>"
|
msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost.</0>"
|
||||||
msgstr ""
|
msgstr "Esta funcionalidade está em beta. Você pode ler mais sobre exportação de repositórios <0>neste post</0> do nosso blog."
|
||||||
|
|
||||||
#: src/view/com/posts/FeedErrorMessage.tsx:114
|
#: src/view/com/posts/FeedErrorMessage.tsx:114
|
||||||
msgid "This feed is currently receiving high traffic and is temporarily unavailable. Please try again later."
|
msgid "This feed is currently receiving high traffic and is temporarily unavailable. Please try again later."
|
||||||
|
@ -4261,17 +4118,13 @@ msgstr "Este usuário está incluído na lista <0/>, que você bloqueou."
|
||||||
msgid "This user is included in the <0/> list which you have muted."
|
msgid "This user is included in the <0/> list which you have muted."
|
||||||
msgstr "Este usuário está incluído na lista <0/>, que você silenciou."
|
msgstr "Este usuário está incluído na lista <0/>, que você silenciou."
|
||||||
|
|
||||||
#: src/view/com/modals/ModerationDetails.tsx:74
|
|
||||||
#~ msgid "This user is included the <0/> list which you have muted."
|
|
||||||
#~ msgstr "Este usuário está incluído na lista <0/>, que você silenciou."
|
|
||||||
|
|
||||||
#: src/view/com/modals/SelfLabel.tsx:137
|
#: src/view/com/modals/SelfLabel.tsx:137
|
||||||
msgid "This warning is only available for posts with media attached."
|
msgid "This warning is only available for posts with media attached."
|
||||||
msgstr "Este aviso só está disponível para publicações com mídia anexada."
|
msgstr "Este aviso só está disponível para publicações com mídia anexada."
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:285
|
#: src/components/dialogs/MutedWords.tsx:285
|
||||||
msgid "This will delete {0} from your muted words. You can always add it back later."
|
msgid "This will delete {0} from your muted words. You can always add it back later."
|
||||||
msgstr ""
|
msgstr "Isso removerá {0} das suas palavras silenciadas. Você pode adicioná-la novamente depois."
|
||||||
|
|
||||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:282
|
#: src/view/com/util/forms/PostDropdownBtn.tsx:282
|
||||||
msgid "This will hide this post from your feeds."
|
msgid "This will hide this post from your feeds."
|
||||||
|
@ -4292,7 +4145,7 @@ msgstr "Preferências das Threads"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:113
|
#: src/components/dialogs/MutedWords.tsx:113
|
||||||
msgid "Toggle between muted word options."
|
msgid "Toggle between muted word options."
|
||||||
msgstr ""
|
msgstr "Alternar entre opções de uma palavra silenciada"
|
||||||
|
|
||||||
#: src/view/com/util/forms/DropdownButton.tsx:246
|
#: src/view/com/util/forms/DropdownButton.tsx:246
|
||||||
msgid "Toggle dropdown"
|
msgid "Toggle dropdown"
|
||||||
|
@ -4376,7 +4229,7 @@ msgstr "Dessilenciar"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.web.tsx:104
|
#: src/components/TagMenu/index.web.tsx:104
|
||||||
msgid "Unmute {truncatedTag}"
|
msgid "Unmute {truncatedTag}"
|
||||||
msgstr ""
|
msgstr "Dessilenciar {truncatedTag}"
|
||||||
|
|
||||||
#: src/view/com/profile/ProfileHeader.tsx:326
|
#: src/view/com/profile/ProfileHeader.tsx:326
|
||||||
msgid "Unmute Account"
|
msgid "Unmute Account"
|
||||||
|
@ -4384,11 +4237,11 @@ msgstr "Dessilenciar conta"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:208
|
#: src/components/TagMenu/index.tsx:208
|
||||||
msgid "Unmute all {displayTag} posts"
|
msgid "Unmute all {displayTag} posts"
|
||||||
msgstr ""
|
msgstr "Dessilenciar posts com {displayTag}"
|
||||||
|
|
||||||
#: src/components/TagMenu/index.tsx:210
|
#: src/components/TagMenu/index.tsx:210
|
||||||
#~ msgid "Unmute all {tag} posts"
|
#~ msgid "Unmute all {tag} posts"
|
||||||
#~ msgstr ""
|
#~ msgstr "Dessilenciar posts com {tag}"
|
||||||
|
|
||||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:251
|
#: src/view/com/util/forms/PostDropdownBtn.tsx:251
|
||||||
#: src/view/com/util/forms/PostDropdownBtn.tsx:256
|
#: src/view/com/util/forms/PostDropdownBtn.tsx:256
|
||||||
|
@ -4446,10 +4299,6 @@ msgstr "Usar o meu navegador padrão"
|
||||||
msgid "Use this to sign into the other app along with your handle."
|
msgid "Use this to sign into the other app along with your handle."
|
||||||
msgstr "Use esta senha para entrar no outro aplicativo juntamente com seu identificador."
|
msgstr "Use esta senha para entrar no outro aplicativo juntamente com seu identificador."
|
||||||
|
|
||||||
#: src/view/com/modals/ServerInput.tsx:105
|
|
||||||
#~ msgid "Use your domain as your Bluesky client service provider"
|
|
||||||
#~ msgstr "Use seu domínio como o provedor de serviço do Bluesky"
|
|
||||||
|
|
||||||
#: src/view/com/modals/InviteCodes.tsx:200
|
#: src/view/com/modals/InviteCodes.tsx:200
|
||||||
msgid "Used by:"
|
msgid "Used by:"
|
||||||
msgstr "Usado por:"
|
msgstr "Usado por:"
|
||||||
|
@ -4514,10 +4363,6 @@ msgstr "usuários seguidos por <0/>"
|
||||||
msgid "Users in \"{0}\""
|
msgid "Users in \"{0}\""
|
||||||
msgstr "Usuários em \"{0}\""
|
msgstr "Usuários em \"{0}\""
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:243
|
|
||||||
#~ msgid "Verification code"
|
|
||||||
#~ msgstr "Código de verificação"
|
|
||||||
|
|
||||||
#: src/view/screens/Settings/index.tsx:910
|
#: src/view/screens/Settings/index.tsx:910
|
||||||
msgid "Verify email"
|
msgid "Verify email"
|
||||||
msgstr "Verificar e-mail"
|
msgstr "Verificar e-mail"
|
||||||
|
@ -4578,7 +4423,7 @@ msgstr "Também recomendamos o \"For You\", do Skygaze:"
|
||||||
|
|
||||||
#: src/screens/Hashtag.tsx:132
|
#: src/screens/Hashtag.tsx:132
|
||||||
msgid "We couldn't find any results for that hashtag."
|
msgid "We couldn't find any results for that hashtag."
|
||||||
msgstr ""
|
msgstr "Não encontramos nenhum post com esta hashtag."
|
||||||
|
|
||||||
#: src/screens/Deactivated.tsx:133
|
#: src/screens/Deactivated.tsx:133
|
||||||
msgid "We estimate {estimatedTime} until your account is ready."
|
msgid "We estimate {estimatedTime} until your account is ready."
|
||||||
|
@ -4598,7 +4443,7 @@ msgstr "Não temos mais posts de quem você segue. Aqui estão os mais novos de
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:204
|
#: src/components/dialogs/MutedWords.tsx:204
|
||||||
msgid "We recommend avoiding common words that appear in many posts, since it can result in no posts being shown."
|
msgid "We recommend avoiding common words that appear in many posts, since it can result in no posts being shown."
|
||||||
msgstr ""
|
msgstr "Não recomendamos utilizar palavras comuns que aparecem em muitos posts, já que isso pode resultar em filtrar todos eles."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:124
|
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:124
|
||||||
msgid "We recommend our \"Discover\" feed:"
|
msgid "We recommend our \"Discover\" feed:"
|
||||||
|
@ -4630,7 +4475,7 @@ msgstr "Tivemos um problema ao exibir esta lista. Se continuar acontecendo, cont
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:230
|
#: src/components/dialogs/MutedWords.tsx:230
|
||||||
msgid "We're sorry, but we weren't able to load your muted words at this time. Please try again."
|
msgid "We're sorry, but we weren't able to load your muted words at this time. Please try again."
|
||||||
msgstr ""
|
msgstr "Não foi possível carregar sua lista de palavras silenciadas. Por favor, tente novamente."
|
||||||
|
|
||||||
#: src/view/screens/Search/Search.tsx:254
|
#: src/view/screens/Search/Search.tsx:254
|
||||||
msgid "We're sorry, but your search could not be completed. Please try again in a few minutes."
|
msgid "We're sorry, but your search could not be completed. Please try again in a few minutes."
|
||||||
|
@ -4688,10 +4533,6 @@ msgstr "Escreva sua resposta"
|
||||||
msgid "Writers"
|
msgid "Writers"
|
||||||
msgstr "Escritores"
|
msgstr "Escritores"
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step2.tsx:263
|
|
||||||
#~ msgid "XXXXXX"
|
|
||||||
#~ msgstr "XXXXXX"
|
|
||||||
|
|
||||||
#: src/view/com/composer/select-language/SuggestedLanguage.tsx:77
|
#: src/view/com/composer/select-language/SuggestedLanguage.tsx:77
|
||||||
#: src/view/screens/PreferencesFollowingFeed.tsx:129
|
#: src/view/screens/PreferencesFollowingFeed.tsx:129
|
||||||
#: src/view/screens/PreferencesFollowingFeed.tsx:201
|
#: src/view/screens/PreferencesFollowingFeed.tsx:201
|
||||||
|
@ -4702,10 +4543,6 @@ msgstr "Escritores"
|
||||||
msgid "Yes"
|
msgid "Yes"
|
||||||
msgstr "Sim"
|
msgstr "Sim"
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepModeration/index.tsx:46
|
|
||||||
#~ msgid "You are in control"
|
|
||||||
#~ msgstr "Você está no controle"
|
|
||||||
|
|
||||||
#: src/screens/Deactivated.tsx:130
|
#: src/screens/Deactivated.tsx:130
|
||||||
msgid "You are in line."
|
msgid "You are in line."
|
||||||
msgstr "Você está na fila."
|
msgstr "Você está na fila."
|
||||||
|
@ -4715,10 +4552,6 @@ msgstr "Você está na fila."
|
||||||
msgid "You can also discover new Custom Feeds to follow."
|
msgid "You can also discover new Custom Feeds to follow."
|
||||||
msgstr "Você também pode descobrir novos feeds para seguir."
|
msgstr "Você também pode descobrir novos feeds para seguir."
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:123
|
|
||||||
#~ msgid "You can also try our \"Discover\" algorithm:"
|
|
||||||
#~ msgstr "Você também pode tentar nosso algoritmo \"Discover\":"
|
|
||||||
|
|
||||||
#: src/screens/Onboarding/StepFollowingFeed.tsx:142
|
#: src/screens/Onboarding/StepFollowingFeed.tsx:142
|
||||||
msgid "You can change these settings later."
|
msgid "You can change these settings later."
|
||||||
msgstr "Você pode mudar estas configurações depois."
|
msgstr "Você pode mudar estas configurações depois."
|
||||||
|
@ -4786,7 +4619,7 @@ msgstr "Você ainda não silenciou nenhuma conta. Para silenciar uma conta, aces
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:250
|
#: src/components/dialogs/MutedWords.tsx:250
|
||||||
msgid "You haven't muted any words or tags yet"
|
msgid "You haven't muted any words or tags yet"
|
||||||
msgstr ""
|
msgstr "Você não silenciou nenhuma palavra ou tag ainda"
|
||||||
|
|
||||||
#: src/view/com/modals/ContentFilteringSettings.tsx:175
|
#: src/view/com/modals/ContentFilteringSettings.tsx:175
|
||||||
msgid "You must be 18 or older to enable adult content."
|
msgid "You must be 18 or older to enable adult content."
|
||||||
|
@ -4836,7 +4669,7 @@ msgstr "Sua conta foi excluída"
|
||||||
|
|
||||||
#: src/view/screens/Settings/ExportCarDialog.tsx:47
|
#: src/view/screens/Settings/ExportCarDialog.tsx:47
|
||||||
msgid "Your account repository, containing all public data records, can be downloaded as a \"CAR\" file. This file does not include media embeds, such as images, or your private data, which must be fetched separately."
|
msgid "Your account repository, containing all public data records, can be downloaded as a \"CAR\" file. This file does not include media embeds, such as images, or your private data, which must be fetched separately."
|
||||||
msgstr ""
|
msgstr "O repositório da sua conta, contendo todos os seus dados públicos, pode ser baixado como um arquivo \"CAR\". Este arquivo não inclui imagens ou dados privados, estes devem ser exportados separadamente."
|
||||||
|
|
||||||
#: src/view/com/auth/create/Step1.tsx:215
|
#: src/view/com/auth/create/Step1.tsx:215
|
||||||
msgid "Your birth date"
|
msgid "Your birth date"
|
||||||
|
@ -4888,7 +4721,7 @@ msgstr "Seu usuário completo será <0>@{0}</0>"
|
||||||
|
|
||||||
#: src/components/dialogs/MutedWords.tsx:221
|
#: src/components/dialogs/MutedWords.tsx:221
|
||||||
msgid "Your muted words"
|
msgid "Your muted words"
|
||||||
msgstr ""
|
msgstr "Suas palavras silenciadas"
|
||||||
|
|
||||||
#: src/view/com/modals/ChangePassword.tsx:155
|
#: src/view/com/modals/ChangePassword.tsx:155
|
||||||
msgid "Your password has been changed successfully!"
|
msgid "Your password has been changed successfully!"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -128,8 +128,8 @@ export default function HashtagScreen({
|
||||||
isError={isError}
|
isError={isError}
|
||||||
isEmpty={posts.length < 1}
|
isEmpty={posts.length < 1}
|
||||||
onRetry={refetch}
|
onRetry={refetch}
|
||||||
notFoundType="results"
|
emptyTitle="results"
|
||||||
empty={_(msg`We couldn't find any results for that hashtag.`)}
|
emptyMessage={_(msg`We couldn't find any results for that hashtag.`)}
|
||||||
/>
|
/>
|
||||||
{!isLoading && posts.length > 0 && (
|
{!isLoading && posts.length > 0 && (
|
||||||
<List<PostView>
|
<List<PostView>
|
||||||
|
|
|
@ -205,19 +205,111 @@ export function ModerationScreenInner({
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<ScrollView
|
||||||
<ScrollView
|
contentContainerStyle={[
|
||||||
contentContainerStyle={[
|
a.border_0,
|
||||||
a.border_0,
|
a.pt_2xl,
|
||||||
a.pt_2xl,
|
a.px_lg,
|
||||||
a.px_lg,
|
gtMobile && a.px_2xl,
|
||||||
gtMobile && a.px_2xl,
|
]}>
|
||||||
]}>
|
<Text
|
||||||
<Text
|
style={[a.text_md, a.font_bold, a.pb_md, t.atoms.text_contrast_high]}>
|
||||||
style={[a.text_md, a.font_bold, a.pb_md, t.atoms.text_contrast_high]}>
|
<Trans>Moderation tools</Trans>
|
||||||
<Trans>Moderation tools</Trans>
|
</Text>
|
||||||
</Text>
|
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.w_full,
|
||||||
|
a.rounded_md,
|
||||||
|
a.overflow_hidden,
|
||||||
|
t.atoms.bg_contrast_25,
|
||||||
|
]}>
|
||||||
|
<Button
|
||||||
|
testID="mutedWordsBtn"
|
||||||
|
label={_(msg`Open muted words and tags settings`)}
|
||||||
|
onPress={() => mutedWordsDialogControl.open()}>
|
||||||
|
{state => (
|
||||||
|
<SubItem
|
||||||
|
title={_(msg`Muted words & tags`)}
|
||||||
|
icon={Filter}
|
||||||
|
style={[
|
||||||
|
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<Divider />
|
||||||
|
<Link testID="moderationlistsBtn" to="/moderation/modlists">
|
||||||
|
{state => (
|
||||||
|
<SubItem
|
||||||
|
title={_(msg`Moderation lists`)}
|
||||||
|
icon={Group}
|
||||||
|
style={[
|
||||||
|
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
<Divider />
|
||||||
|
<Link testID="mutedAccountsBtn" to="/moderation/muted-accounts">
|
||||||
|
{state => (
|
||||||
|
<SubItem
|
||||||
|
title={_(msg`Muted accounts`)}
|
||||||
|
icon={Person}
|
||||||
|
style={[
|
||||||
|
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
<Divider />
|
||||||
|
<Link testID="blockedAccountsBtn" to="/moderation/blocked-accounts">
|
||||||
|
{state => (
|
||||||
|
<SubItem
|
||||||
|
title={_(msg`Blocked accounts`)}
|
||||||
|
icon={CircleBanSign}
|
||||||
|
style={[
|
||||||
|
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.pt_2xl,
|
||||||
|
a.pb_md,
|
||||||
|
a.text_md,
|
||||||
|
a.font_bold,
|
||||||
|
t.atoms.text_contrast_high,
|
||||||
|
]}>
|
||||||
|
<Trans>Content filters</Trans>
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<View style={[a.gap_md]}>
|
||||||
|
{ageNotSet && (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
label={_(msg`Confirm your birthdate`)}
|
||||||
|
size="small"
|
||||||
|
variant="solid"
|
||||||
|
color="secondary"
|
||||||
|
onPress={() => {
|
||||||
|
birthdateDialogControl.open()
|
||||||
|
}}
|
||||||
|
style={[a.justify_between, a.rounded_md, a.px_lg, a.py_lg]}>
|
||||||
|
<ButtonText>
|
||||||
|
<Trans>Confirm your age:</Trans>
|
||||||
|
</ButtonText>
|
||||||
|
<ButtonText>
|
||||||
|
<Trans>Set birthdate</Trans>
|
||||||
|
</ButtonText>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<BirthDateSettingsDialog control={birthdateDialogControl} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.w_full,
|
a.w_full,
|
||||||
|
@ -225,234 +317,137 @@ export function ModerationScreenInner({
|
||||||
a.overflow_hidden,
|
a.overflow_hidden,
|
||||||
t.atoms.bg_contrast_25,
|
t.atoms.bg_contrast_25,
|
||||||
]}>
|
]}>
|
||||||
<Button
|
{!ageNotSet && !isUnderage && (
|
||||||
testID="mutedWordsBtn"
|
|
||||||
label={_(msg`Open muted words and tags settings`)}
|
|
||||||
onPress={() => mutedWordsDialogControl.open()}>
|
|
||||||
{state => (
|
|
||||||
<SubItem
|
|
||||||
title={_(msg`Muted words & tags`)}
|
|
||||||
icon={Filter}
|
|
||||||
style={[
|
|
||||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
<Divider />
|
|
||||||
<Link testID="moderationlistsBtn" to="/moderation/modlists">
|
|
||||||
{state => (
|
|
||||||
<SubItem
|
|
||||||
title={_(msg`Moderation lists`)}
|
|
||||||
icon={Group}
|
|
||||||
style={[
|
|
||||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Link>
|
|
||||||
<Divider />
|
|
||||||
<Link testID="mutedAccountsBtn" to="/moderation/muted-accounts">
|
|
||||||
{state => (
|
|
||||||
<SubItem
|
|
||||||
title={_(msg`Muted accounts`)}
|
|
||||||
icon={Person}
|
|
||||||
style={[
|
|
||||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Link>
|
|
||||||
<Divider />
|
|
||||||
<Link testID="blockedAccountsBtn" to="/moderation/blocked-accounts">
|
|
||||||
{state => (
|
|
||||||
<SubItem
|
|
||||||
title={_(msg`Blocked accounts`)}
|
|
||||||
icon={CircleBanSign}
|
|
||||||
style={[
|
|
||||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Link>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
a.pt_2xl,
|
|
||||||
a.pb_md,
|
|
||||||
a.text_md,
|
|
||||||
a.font_bold,
|
|
||||||
t.atoms.text_contrast_high,
|
|
||||||
]}>
|
|
||||||
<Trans>Content filters</Trans>
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<View style={[a.gap_md]}>
|
|
||||||
{ageNotSet && (
|
|
||||||
<>
|
<>
|
||||||
<Button
|
<View
|
||||||
label={_(msg`Confirm your birthdate`)}
|
style={[
|
||||||
size="small"
|
a.py_lg,
|
||||||
variant="solid"
|
a.px_lg,
|
||||||
color="secondary"
|
a.flex_row,
|
||||||
onPress={() => {
|
a.align_center,
|
||||||
birthdateDialogControl.open()
|
a.justify_between,
|
||||||
}}
|
]}>
|
||||||
style={[a.justify_between, a.rounded_md, a.px_lg, a.py_lg]}>
|
<Text style={[a.font_semibold, t.atoms.text_contrast_high]}>
|
||||||
<ButtonText>
|
<Trans>Enable adult content</Trans>
|
||||||
<Trans>Confirm your age:</Trans>
|
</Text>
|
||||||
</ButtonText>
|
<Toggle.Item
|
||||||
<ButtonText>
|
label={_(msg`Toggle to enable or disable adult content`)}
|
||||||
<Trans>Set birthdate</Trans>
|
name="adultContent"
|
||||||
</ButtonText>
|
value={adultContentEnabled}
|
||||||
</Button>
|
onChange={onToggleAdultContentEnabled}>
|
||||||
|
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
||||||
<BirthDateSettingsDialog
|
<Text style={[t.atoms.text_contrast_medium]}>
|
||||||
control={birthdateDialogControl}
|
{adultContentEnabled ? (
|
||||||
preferences={preferences}
|
<Trans>Enabled</Trans>
|
||||||
/>
|
) : (
|
||||||
|
<Trans>Disabled</Trans>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
<Toggle.Switch />
|
||||||
|
</View>
|
||||||
|
</Toggle.Item>
|
||||||
|
</View>
|
||||||
|
<Divider />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<View
|
{!isUnderage && adultContentEnabled && (
|
||||||
style={[
|
<>
|
||||||
a.w_full,
|
<GlobalModerationLabelPref labelValueDefinition={LABELS.porn} />
|
||||||
a.rounded_md,
|
<Divider />
|
||||||
a.overflow_hidden,
|
<GlobalModerationLabelPref labelValueDefinition={LABELS.sexual} />
|
||||||
t.atoms.bg_contrast_25,
|
<Divider />
|
||||||
]}>
|
<GlobalModerationLabelPref
|
||||||
{!ageNotSet && !isUnderage && (
|
labelValueDefinition={LABELS['graphic-media']}
|
||||||
<>
|
/>
|
||||||
<View
|
<Divider />
|
||||||
style={[
|
</>
|
||||||
a.py_lg,
|
)}
|
||||||
a.px_lg,
|
<GlobalModerationLabelPref labelValueDefinition={LABELS.nudity} />
|
||||||
a.flex_row,
|
|
||||||
a.align_center,
|
|
||||||
a.justify_between,
|
|
||||||
]}>
|
|
||||||
<Text style={[a.font_semibold, t.atoms.text_contrast_high]}>
|
|
||||||
<Trans>Enable adult content</Trans>
|
|
||||||
</Text>
|
|
||||||
<Toggle.Item
|
|
||||||
label={_(msg`Toggle to enable or disable adult content`)}
|
|
||||||
name="adultContent"
|
|
||||||
value={adultContentEnabled}
|
|
||||||
onChange={onToggleAdultContentEnabled}>
|
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
|
||||||
<Text style={[t.atoms.text_contrast_medium]}>
|
|
||||||
{adultContentEnabled ? (
|
|
||||||
<Trans>Enabled</Trans>
|
|
||||||
) : (
|
|
||||||
<Trans>Disabled</Trans>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
<Toggle.Switch />
|
|
||||||
</View>
|
|
||||||
</Toggle.Item>
|
|
||||||
</View>
|
|
||||||
<Divider />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!isUnderage && adultContentEnabled && (
|
|
||||||
<>
|
|
||||||
<GlobalModerationLabelPref labelValueDefinition={LABELS.porn} />
|
|
||||||
<Divider />
|
|
||||||
<GlobalModerationLabelPref
|
|
||||||
labelValueDefinition={LABELS.sexual}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
<GlobalModerationLabelPref
|
|
||||||
labelValueDefinition={LABELS['graphic-media']}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<GlobalModerationLabelPref labelValueDefinition={LABELS.nudity} />
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
a.text_md,
|
a.text_md,
|
||||||
a.font_bold,
|
a.font_bold,
|
||||||
a.pt_2xl,
|
a.pt_2xl,
|
||||||
a.pb_md,
|
a.pb_md,
|
||||||
t.atoms.text_contrast_high,
|
t.atoms.text_contrast_high,
|
||||||
]}>
|
]}>
|
||||||
<Trans>Advanced</Trans>
|
<Trans>Advanced</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{isLabelersLoading ? (
|
{isLabelersLoading ? (
|
||||||
<Loader />
|
<View style={[a.w_full, a.align_center, a.p_lg]}>
|
||||||
) : labelersError || !labelers ? (
|
<Loader size="xl" />
|
||||||
<View style={[a.p_lg, a.rounded_sm, t.atoms.bg_contrast_25]}>
|
</View>
|
||||||
<Text>
|
) : labelersError || !labelers ? (
|
||||||
<Trans>
|
<View style={[a.p_lg, a.rounded_sm, t.atoms.bg_contrast_25]}>
|
||||||
We were unable to load your configured labelers at this time.
|
<Text>
|
||||||
</Trans>
|
<Trans>
|
||||||
</Text>
|
We were unable to load your configured labelers at this time.
|
||||||
</View>
|
</Trans>
|
||||||
) : (
|
</Text>
|
||||||
<View style={[a.rounded_sm, t.atoms.bg_contrast_25]}>
|
</View>
|
||||||
{labelers.map((labeler, i) => {
|
) : (
|
||||||
return (
|
<View style={[a.rounded_sm, t.atoms.bg_contrast_25]}>
|
||||||
<React.Fragment key={labeler.creator.did}>
|
{labelers.map((labeler, i) => {
|
||||||
{i !== 0 && <Divider />}
|
return (
|
||||||
<LabelingService.Link labeler={labeler}>
|
<React.Fragment key={labeler.creator.did}>
|
||||||
{state => (
|
{i !== 0 && <Divider />}
|
||||||
<LabelingService.Outer
|
<LabelingService.Link labeler={labeler}>
|
||||||
style={[
|
{state => (
|
||||||
i === 0 && {
|
<LabelingService.Outer
|
||||||
borderTopLeftRadius: a.rounded_sm.borderRadius,
|
style={[
|
||||||
borderTopRightRadius: a.rounded_sm.borderRadius,
|
i === 0 && {
|
||||||
},
|
borderTopLeftRadius: a.rounded_sm.borderRadius,
|
||||||
i === labelers.length - 1 && {
|
borderTopRightRadius: a.rounded_sm.borderRadius,
|
||||||
borderBottomLeftRadius: a.rounded_sm.borderRadius,
|
},
|
||||||
borderBottomRightRadius: a.rounded_sm.borderRadius,
|
i === labelers.length - 1 && {
|
||||||
},
|
borderBottomLeftRadius: a.rounded_sm.borderRadius,
|
||||||
(state.hovered || state.pressed) && [
|
borderBottomRightRadius: a.rounded_sm.borderRadius,
|
||||||
t.atoms.bg_contrast_50,
|
},
|
||||||
],
|
(state.hovered || state.pressed) && [
|
||||||
]}>
|
t.atoms.bg_contrast_50,
|
||||||
<LabelingService.Avatar />
|
],
|
||||||
<LabelingService.Content>
|
]}>
|
||||||
<LabelingService.Title
|
<LabelingService.Avatar avatar={labeler.creator.avatar} />
|
||||||
value={getLabelingServiceTitle({
|
<LabelingService.Content>
|
||||||
displayName: labeler.creator.displayName,
|
<LabelingService.Title
|
||||||
handle: labeler.creator.handle,
|
value={getLabelingServiceTitle({
|
||||||
})}
|
displayName: labeler.creator.displayName,
|
||||||
/>
|
handle: labeler.creator.handle,
|
||||||
<LabelingService.Description
|
})}
|
||||||
value={labeler.creator.description}
|
/>
|
||||||
handle={labeler.creator.handle}
|
<LabelingService.Description
|
||||||
/>
|
value={labeler.creator.description}
|
||||||
</LabelingService.Content>
|
handle={labeler.creator.handle}
|
||||||
</LabelingService.Outer>
|
/>
|
||||||
)}
|
</LabelingService.Content>
|
||||||
</LabelingService.Link>
|
</LabelingService.Outer>
|
||||||
</React.Fragment>
|
)}
|
||||||
)
|
</LabelingService.Link>
|
||||||
})}
|
</React.Fragment>
|
||||||
</View>
|
)
|
||||||
)}
|
})}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
a.text_md,
|
a.text_md,
|
||||||
a.font_bold,
|
a.font_bold,
|
||||||
a.pt_2xl,
|
a.pt_2xl,
|
||||||
a.pb_md,
|
a.pb_md,
|
||||||
t.atoms.text_contrast_high,
|
t.atoms.text_contrast_high,
|
||||||
]}>
|
]}>
|
||||||
<Trans>Logged-out visibility</Trans>
|
<Trans>Logged-out visibility</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<PwiOptOut />
|
<PwiOptOut />
|
||||||
|
|
||||||
<View style={{height: 200}} />
|
<View style={{height: 200}} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,11 +515,12 @@ function PwiOptOut() {
|
||||||
value={isOptedOut}
|
value={isOptedOut}
|
||||||
onChange={onToggleOptOut}
|
onChange={onToggleOptOut}
|
||||||
name="logged_out_visibility"
|
name="logged_out_visibility"
|
||||||
|
style={a.flex_1}
|
||||||
label={_(
|
label={_(
|
||||||
msg`Discourage apps from showing my account to logged-out users`,
|
msg`Discourage apps from showing my account to logged-out users`,
|
||||||
)}>
|
)}>
|
||||||
<Toggle.Switch />
|
<Toggle.Switch />
|
||||||
<Toggle.Label style={[a.text_md]}>
|
<Toggle.Label style={[a.text_md, a.flex_1]}>
|
||||||
<Trans>
|
<Trans>
|
||||||
Discourage apps from showing my account to logged-out users
|
Discourage apps from showing my account to logged-out users
|
||||||
</Trans>
|
</Trans>
|
||||||
|
|
|
@ -90,7 +90,9 @@ export function AdultContentEnabledPref({
|
||||||
a.align_center,
|
a.align_center,
|
||||||
a.py_md,
|
a.py_md,
|
||||||
]}>
|
]}>
|
||||||
<Text style={[a.font_bold]}>Enable Adult Content</Text>
|
<Text style={[a.font_bold]}>
|
||||||
|
<Trans>Enable Adult Content</Trans>
|
||||||
|
</Text>
|
||||||
<Toggle.Switch />
|
<Toggle.Switch />
|
||||||
</View>
|
</View>
|
||||||
</Toggle.Item>
|
</Toggle.Item>
|
||||||
|
@ -111,7 +113,9 @@ export function AdultContentEnabledPref({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Prompt.Outer control={prompt}>
|
<Prompt.Outer control={prompt}>
|
||||||
<Prompt.Title>Adult Content</Prompt.Title>
|
<Prompt.Title>
|
||||||
|
<Trans>Adult Content</Trans>
|
||||||
|
</Prompt.Title>
|
||||||
<Prompt.Description>
|
<Prompt.Description>
|
||||||
<Trans>
|
<Trans>
|
||||||
Due to Apple policies, adult content can only be enabled on the web
|
Due to Apple policies, adult content can only be enabled on the web
|
||||||
|
@ -119,7 +123,9 @@ export function AdultContentEnabledPref({
|
||||||
</Trans>
|
</Trans>
|
||||||
</Prompt.Description>
|
</Prompt.Description>
|
||||||
<Prompt.Actions>
|
<Prompt.Actions>
|
||||||
<Prompt.Action onPress={() => prompt.close()}>OK</Prompt.Action>
|
<Prompt.Action onPress={() => prompt.close()}>
|
||||||
|
<Trans>OK</Trans>
|
||||||
|
</Prompt.Action>
|
||||||
</Prompt.Actions>
|
</Prompt.Actions>
|
||||||
</Prompt.Outer>
|
</Prompt.Outer>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -48,7 +48,6 @@ export const ProfileLabelsSection = React.forwardRef<
|
||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const t = useTheme()
|
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {height: minHeight} = useSafeAreaFrame()
|
const {height: minHeight} = useSafeAreaFrame()
|
||||||
|
|
||||||
|
@ -66,37 +65,26 @@ export const ProfileLabelsSection = React.forwardRef<
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CenteredView>
|
<CenteredView style={{flex: 1, minHeight}} sideBorders>
|
||||||
<View
|
{isLabelerLoading ? (
|
||||||
style={[
|
<View style={[a.w_full, a.align_center]}>
|
||||||
a.border_l,
|
<Loader size="xl" />
|
||||||
a.border_r,
|
</View>
|
||||||
a.border_t,
|
) : labelerError || !labelerInfo ? (
|
||||||
t.atoms.border_contrast_low,
|
<ErrorState
|
||||||
{
|
error={
|
||||||
minHeight,
|
labelerError?.toString() ||
|
||||||
},
|
_(msg`Something went wrong, please try again.`)
|
||||||
]}>
|
}
|
||||||
{isLabelerLoading ? (
|
/>
|
||||||
<View style={[a.w_full, a.align_center]}>
|
) : (
|
||||||
<Loader size="xl" />
|
<ProfileLabelsSectionInner
|
||||||
</View>
|
moderationOpts={moderationOpts}
|
||||||
) : labelerError || !labelerInfo ? (
|
labelerInfo={labelerInfo}
|
||||||
<ErrorState
|
scrollElRef={scrollElRef}
|
||||||
error={
|
headerHeight={headerHeight}
|
||||||
labelerError?.toString() ||
|
/>
|
||||||
_(msg`Something went wrong, please try again.`)
|
)}
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ProfileLabelsSectionInner
|
|
||||||
moderationOpts={moderationOpts}
|
|
||||||
labelerInfo={labelerInfo}
|
|
||||||
scrollElRef={scrollElRef}
|
|
||||||
headerHeight={headerHeight}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</CenteredView>
|
</CenteredView>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -149,13 +137,7 @@ export function ProfileLabelsSectionInner({
|
||||||
}}
|
}}
|
||||||
contentOffset={{x: 0, y: headerHeight * -1}}
|
contentOffset={{x: 0, y: headerHeight * -1}}
|
||||||
onScroll={scrollHandler}>
|
onScroll={scrollHandler}>
|
||||||
<View
|
<View style={[a.pt_xl, a.px_lg, a.border_t, t.atoms.border_contrast_low]}>
|
||||||
style={[
|
|
||||||
a.pt_xl,
|
|
||||||
a.px_lg,
|
|
||||||
isNative && a.border_t,
|
|
||||||
t.atoms.border_contrast_low,
|
|
||||||
]}>
|
|
||||||
<View>
|
<View>
|
||||||
<Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_sm]}>
|
<Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_sm]}>
|
||||||
<Trans>
|
<Trans>
|
||||||
|
|
|
@ -101,13 +101,7 @@ function computeSuggestions(
|
||||||
}
|
}
|
||||||
for (const item of searched) {
|
for (const item of searched) {
|
||||||
if (!items.find(item2 => item2.handle === item.handle)) {
|
if (!items.find(item2 => item2.handle === item.handle)) {
|
||||||
items.push({
|
items.push(item)
|
||||||
did: item.did,
|
|
||||||
handle: item.handle,
|
|
||||||
displayName: item.displayName,
|
|
||||||
avatar: item.avatar,
|
|
||||||
labels: item.labels,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return items.filter(profile => {
|
return items.filter(profile => {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
BskyFeedViewPreference,
|
BskyFeedViewPreference,
|
||||||
ModerationOpts,
|
ModerationOpts,
|
||||||
AppBskyActorDefs,
|
AppBskyActorDefs,
|
||||||
|
BSKY_LABELER_DID,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
|
|
||||||
import {track} from '#/lib/analytics/analytics'
|
import {track} from '#/lib/analytics/analytics'
|
||||||
|
@ -19,6 +20,7 @@ import {
|
||||||
DEFAULT_THREAD_VIEW_PREFS,
|
DEFAULT_THREAD_VIEW_PREFS,
|
||||||
DEFAULT_LOGGED_OUT_PREFERENCES,
|
DEFAULT_LOGGED_OUT_PREFERENCES,
|
||||||
} from '#/state/queries/preferences/const'
|
} from '#/state/queries/preferences/const'
|
||||||
|
import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/moderation'
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
import {useHiddenPosts, useLabelDefinitions} from '#/state/preferences'
|
import {useHiddenPosts, useLabelDefinitions} from '#/state/preferences'
|
||||||
import {saveLabelers} from '#/state/session/agent-config'
|
import {saveLabelers} from '#/state/session/agent-config'
|
||||||
|
@ -95,7 +97,18 @@ export function useModerationOpts() {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
userDid: currentAccount?.did,
|
userDid: currentAccount?.did,
|
||||||
prefs: {...prefs.data.moderationPrefs, hiddenPosts: hiddenPosts || []},
|
prefs: {
|
||||||
|
...prefs.data.moderationPrefs,
|
||||||
|
labelers: prefs.data.moderationPrefs.labelers.length
|
||||||
|
? prefs.data.moderationPrefs.labelers
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
did: BSKY_LABELER_DID,
|
||||||
|
labels: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
hiddenPosts: hiddenPosts || [],
|
||||||
|
},
|
||||||
labelDefs,
|
labelDefs,
|
||||||
}
|
}
|
||||||
}, [override, currentAccount, labelDefs, prefs.data, hiddenPosts])
|
}, [override, currentAccount, labelDefs, prefs.data, hiddenPosts])
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
AppBskyEmbedRecord,
|
AppBskyEmbedRecord,
|
||||||
AppBskyRichtextFacet,
|
AppBskyRichtextFacet,
|
||||||
ModerationDecision,
|
ModerationDecision,
|
||||||
|
AppBskyActorDefs,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||||
|
|
||||||
|
@ -10,11 +11,7 @@ export interface ComposerOptsPostRef {
|
||||||
uri: string
|
uri: string
|
||||||
cid: string
|
cid: string
|
||||||
text: string
|
text: string
|
||||||
author: {
|
author: AppBskyActorDefs.ProfileViewBasic
|
||||||
handle: string
|
|
||||||
displayName?: string
|
|
||||||
avatar?: string
|
|
||||||
}
|
|
||||||
embed?: AppBskyEmbedRecord.ViewRecord['embed']
|
embed?: AppBskyEmbedRecord.ViewRecord['embed']
|
||||||
moderation?: ModerationDecision
|
moderation?: ModerationDecision
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,9 @@ export function HomeLoggedOutCTA() {
|
||||||
onPress={showCreateAccount}
|
onPress={showCreateAccount}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Create new account`)}
|
accessibilityLabel={_(msg`Create new account`)}
|
||||||
accessibilityHint="Opens flow to create a new Bluesky account">
|
accessibilityHint={_(
|
||||||
|
msg`Opens flow to create a new Bluesky account`,
|
||||||
|
)}>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
s.white,
|
s.white,
|
||||||
|
@ -68,7 +70,9 @@ export function HomeLoggedOutCTA() {
|
||||||
onPress={showSignIn}
|
onPress={showSignIn}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Sign in`)}
|
accessibilityLabel={_(msg`Sign in`)}
|
||||||
accessibilityHint="Opens flow to sign into your existing Bluesky account">
|
accessibilityHint={_(
|
||||||
|
msg`Opens flow to sign into your existing Bluesky account`,
|
||||||
|
)}>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
pal.text,
|
pal.text,
|
||||||
|
|
|
@ -66,7 +66,9 @@ export const SplashScreen = ({
|
||||||
onPress={onPressCreateAccount}
|
onPress={onPressCreateAccount}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Create new account`)}
|
accessibilityLabel={_(msg`Create new account`)}
|
||||||
accessibilityHint="Opens flow to create a new Bluesky account">
|
accessibilityHint={_(
|
||||||
|
msg`Opens flow to create a new Bluesky account`,
|
||||||
|
)}>
|
||||||
<Text style={[s.white, styles.btnLabel]}>
|
<Text style={[s.white, styles.btnLabel]}>
|
||||||
<Trans>Create a new account</Trans>
|
<Trans>Create a new account</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -77,7 +79,9 @@ export const SplashScreen = ({
|
||||||
onPress={onPressSignin}
|
onPress={onPressSignin}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Sign in`)}
|
accessibilityLabel={_(msg`Sign in`)}
|
||||||
accessibilityHint="Opens flow to sign into your existing Bluesky account">
|
accessibilityHint={_(
|
||||||
|
msg`Opens flow to sign into your existing Bluesky account`,
|
||||||
|
)}>
|
||||||
<Text style={[pal.text, styles.btnLabel]}>
|
<Text style={[pal.text, styles.btnLabel]}>
|
||||||
<Trans>Sign In</Trans>
|
<Trans>Sign In</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -9,6 +9,8 @@ import {TextLink} from '../../util/Link'
|
||||||
import {Text} from '../../util/text/Text'
|
import {Text} from '../../util/text/Text'
|
||||||
import {s, colors} from 'lib/styles'
|
import {s, colors} from 'lib/styles'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
import {Trans, msg} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
|
type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
|
||||||
|
|
||||||
|
@ -22,6 +24,7 @@ export const Policies = ({
|
||||||
under13: boolean
|
under13: boolean
|
||||||
}) => {
|
}) => {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
const {_} = useLingui()
|
||||||
if (!serviceDescription) {
|
if (!serviceDescription) {
|
||||||
return <View />
|
return <View />
|
||||||
}
|
}
|
||||||
|
@ -42,7 +45,9 @@ export const Policies = ({
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Text style={[pal.textLight, s.pl5, s.flex1]}>
|
<Text style={[pal.textLight, s.pl5, s.flex1]}>
|
||||||
This service has not provided terms of service or a privacy policy.
|
<Trans>
|
||||||
|
This service has not provided terms of service or a privacy policy.
|
||||||
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -53,7 +58,7 @@ export const Policies = ({
|
||||||
<TextLink
|
<TextLink
|
||||||
key="tos"
|
key="tos"
|
||||||
href={tos}
|
href={tos}
|
||||||
text="Terms of Service"
|
text={_(msg`Terms of Service`)}
|
||||||
style={[pal.link, s.underline]}
|
style={[pal.link, s.underline]}
|
||||||
onPress={() => Linking.openURL(tos)}
|
onPress={() => Linking.openURL(tos)}
|
||||||
/>,
|
/>,
|
||||||
|
@ -64,7 +69,7 @@ export const Policies = ({
|
||||||
<TextLink
|
<TextLink
|
||||||
key="pp"
|
key="pp"
|
||||||
href={pp}
|
href={pp}
|
||||||
text="Privacy Policy"
|
text={_(msg`Privacy Policy`)}
|
||||||
style={[pal.link, s.underline]}
|
style={[pal.link, s.underline]}
|
||||||
onPress={() => Linking.openURL(pp)}
|
onPress={() => Linking.openURL(pp)}
|
||||||
/>,
|
/>,
|
||||||
|
@ -83,7 +88,7 @@ export const Policies = ({
|
||||||
return (
|
return (
|
||||||
<View style={styles.policies}>
|
<View style={styles.policies}>
|
||||||
<Text style={pal.textLight}>
|
<Text style={pal.textLight}>
|
||||||
By creating an account you agree to the {els}.
|
<Trans>By creating an account you agree to the {els}.</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
{under13 ? (
|
{under13 ? (
|
||||||
<Text style={[pal.textLight, s.bold]}>
|
<Text style={[pal.textLight, s.bold]}>
|
||||||
|
@ -91,8 +96,10 @@ export const Policies = ({
|
||||||
</Text>
|
</Text>
|
||||||
) : needsGuardian ? (
|
) : needsGuardian ? (
|
||||||
<Text style={[pal.textLight, s.bold]}>
|
<Text style={[pal.textLight, s.bold]}>
|
||||||
If you are not yet an adult according to the laws of your country,
|
<Trans>
|
||||||
your parent or legal guardian must read these Terms on your behalf.
|
If you are not yet an adult according to the laws of your country,
|
||||||
|
your parent or legal guardian must read these Terms on your behalf.
|
||||||
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -11,7 +11,8 @@ import {Text} from 'view/com/util/text/Text'
|
||||||
import Animated, {FadeInRight} from 'react-native-reanimated'
|
import Animated, {FadeInRight} from 'react-native-reanimated'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {useAnalytics} from 'lib/analytics/analytics'
|
import {useAnalytics} from 'lib/analytics/analytics'
|
||||||
import {Trans} from '@lingui/macro'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {Trans, msg} from '@lingui/macro'
|
||||||
import {Shadow, useProfileShadow} from '#/state/cache/profile-shadow'
|
import {Shadow, useProfileShadow} from '#/state/cache/profile-shadow'
|
||||||
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
|
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
@ -70,6 +71,7 @@ function ProfileCard({
|
||||||
}) {
|
}) {
|
||||||
const {track} = useAnalytics()
|
const {track} = useAnalytics()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
const {_} = useLingui()
|
||||||
const [addingMoreSuggestions, setAddingMoreSuggestions] =
|
const [addingMoreSuggestions, setAddingMoreSuggestions] =
|
||||||
React.useState(false)
|
React.useState(false)
|
||||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
|
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
|
||||||
|
@ -136,7 +138,7 @@ function ProfileCard({
|
||||||
type={profile.viewer?.following ? 'default' : 'inverted'}
|
type={profile.viewer?.following ? 'default' : 'inverted'}
|
||||||
labelStyle={styles.followButton}
|
labelStyle={styles.followButton}
|
||||||
onPress={onToggleFollow}
|
onPress={onToggleFollow}
|
||||||
label={profile.viewer?.following ? 'Unfollow' : 'Follow'}
|
label={profile.viewer?.following ? _(msg`Unfollow`) : _(msg`Follow`)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{profile.description ? (
|
{profile.description ? (
|
||||||
|
|
|
@ -6,7 +6,8 @@ import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {Button} from 'view/com/util/forms/Button'
|
import {Button} from 'view/com/util/forms/Button'
|
||||||
import {ViewHeader} from 'view/com/util/ViewHeader'
|
import {ViewHeader} from 'view/com/util/ViewHeader'
|
||||||
import {Trans} from '@lingui/macro'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {Trans, msg} from '@lingui/macro'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
next: () => void
|
next: () => void
|
||||||
|
@ -15,6 +16,7 @@ type Props = {
|
||||||
|
|
||||||
export function WelcomeMobile({next, skip}: Props) {
|
export function WelcomeMobile({next, skip}: Props) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
const {_} = useLingui()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container]} testID="welcomeOnboarding">
|
<View style={[styles.container]} testID="welcomeOnboarding">
|
||||||
|
@ -91,7 +93,7 @@ export function WelcomeMobile({next, skip}: Props) {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onPress={next}
|
onPress={next}
|
||||||
label="Continue"
|
label={_(msg`Continue`)}
|
||||||
testID="continueBtn"
|
testID="continueBtn"
|
||||||
style={[styles.buttonContainer]}
|
style={[styles.buttonContainer]}
|
||||||
labelStyle={styles.buttonText}
|
labelStyle={styles.buttonText}
|
||||||
|
|
|
@ -115,7 +115,7 @@ export function ServerInputDialog({
|
||||||
testID="customServerTextInput"
|
testID="customServerTextInput"
|
||||||
value={customAddress}
|
value={customAddress}
|
||||||
onChangeText={setCustomAddress}
|
onChangeText={setCustomAddress}
|
||||||
label={_(msg`my-server.com`)}
|
label="my-server.com"
|
||||||
accessibilityLabelledBy="address-input-label"
|
accessibilityLabelledBy="address-input-label"
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
keyboardType="url"
|
keyboardType="url"
|
||||||
|
|
|
@ -415,7 +415,11 @@ export const ComposePost = observer(function ComposePost({
|
||||||
styles.textInputLayout,
|
styles.textInputLayout,
|
||||||
isNative && styles.textInputLayoutMobile,
|
isNative && styles.textInputLayoutMobile,
|
||||||
]}>
|
]}>
|
||||||
<UserAvatar avatar={currentProfile?.avatar} size={50} />
|
<UserAvatar
|
||||||
|
avatar={currentProfile?.avatar}
|
||||||
|
size={50}
|
||||||
|
type={currentProfile?.associated?.labeler ? 'labeler' : 'user'}
|
||||||
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
ref={textInput}
|
ref={textInput}
|
||||||
richtext={richtext}
|
richtext={richtext}
|
||||||
|
|
|
@ -87,6 +87,7 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
|
||||||
avatar={replyTo.author.avatar}
|
avatar={replyTo.author.avatar}
|
||||||
size={50}
|
size={50}
|
||||||
moderation={replyTo.moderation?.ui('avatar')}
|
moderation={replyTo.moderation?.ui('avatar')}
|
||||||
|
type={replyTo.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
<View style={styles.replyToPost}>
|
<View style={styles.replyToPost}>
|
||||||
<Text type="xl-medium" style={[pal.text]}>
|
<Text type="xl-medium" style={[pal.text]}>
|
||||||
|
|
|
@ -23,7 +23,11 @@ export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) {
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Compose reply`)}
|
accessibilityLabel={_(msg`Compose reply`)}
|
||||||
accessibilityHint={_(msg`Opens composer`)}>
|
accessibilityHint={_(msg`Opens composer`)}>
|
||||||
<UserAvatar avatar={profile?.avatar} size={38} />
|
<UserAvatar
|
||||||
|
avatar={profile?.avatar}
|
||||||
|
size={38}
|
||||||
|
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
||||||
|
/>
|
||||||
<Text
|
<Text
|
||||||
type="xl"
|
type="xl"
|
||||||
style={[
|
style={[
|
||||||
|
|
|
@ -78,7 +78,11 @@ export function Autocomplete({
|
||||||
accessibilityLabel={`Select ${item.handle}`}
|
accessibilityLabel={`Select ${item.handle}`}
|
||||||
accessibilityHint="">
|
accessibilityHint="">
|
||||||
<View style={styles.avatarAndHandle}>
|
<View style={styles.avatarAndHandle}>
|
||||||
<UserAvatar avatar={item.avatar ?? null} size={24} />
|
<UserAvatar
|
||||||
|
avatar={item.avatar ?? null}
|
||||||
|
size={24}
|
||||||
|
type={item.associated?.labeler ? 'labeler' : 'user'}
|
||||||
|
/>
|
||||||
<Text type="md-medium" style={pal.text}>
|
<Text type="md-medium" style={pal.text}>
|
||||||
{displayName}
|
{displayName}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -175,7 +175,11 @@ const MentionList = forwardRef<MentionListRef, SuggestionProps>(
|
||||||
}}
|
}}
|
||||||
accessibilityRole="button">
|
accessibilityRole="button">
|
||||||
<View style={styles.avatarAndDisplayName}>
|
<View style={styles.avatarAndDisplayName}>
|
||||||
<UserAvatar avatar={item.avatar ?? null} size={26} />
|
<UserAvatar
|
||||||
|
avatar={item.avatar ?? null}
|
||||||
|
size={26}
|
||||||
|
type={item.associated?.labeler ? 'labeler' : 'user'}
|
||||||
|
/>
|
||||||
<Text style={pal.text} numberOfLines={1}>
|
<Text style={pal.text} numberOfLines={1}>
|
||||||
{displayName}
|
{displayName}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -78,9 +78,9 @@ function LightboxFooter({imageIndex}: {imageIndex: number}) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await saveImageToMediaLibrary({uri})
|
await saveImageToMediaLibrary({uri})
|
||||||
Toast.show('Saved to your camera roll.')
|
Toast.show(_(msg`Saved to your camera roll.`))
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
Toast.show(`Failed to save image: ${String(e)}`)
|
Toast.show(_(msg`Failed to save image: ${String(e)}`))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[permissionResponse, requestPermission, _],
|
[permissionResponse, requestPermission, _],
|
||||||
|
|
|
@ -150,7 +150,7 @@ export function Inner({
|
||||||
accessibilityHint={_(msg`Exits handle change process`)}
|
accessibilityHint={_(msg`Exits handle change process`)}
|
||||||
onAccessibilityEscape={onPressCancel}>
|
onAccessibilityEscape={onPressCancel}>
|
||||||
<Text type="lg" style={pal.textLight}>
|
<Text type="lg" style={pal.textLight}>
|
||||||
Cancel
|
<Trans>Cancel</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
@ -254,7 +254,7 @@ function ProvidedHandleForm({
|
||||||
<TextInput
|
<TextInput
|
||||||
testID="setHandleInput"
|
testID="setHandleInput"
|
||||||
style={[pal.text, styles.textInput]}
|
style={[pal.text, styles.textInput]}
|
||||||
placeholder="e.g. alice"
|
placeholder={_(msg`e.g. alice`)}
|
||||||
placeholderTextColor={pal.colors.textLight}
|
placeholderTextColor={pal.colors.textLight}
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
keyboardAppearance={theme.colorScheme}
|
keyboardAppearance={theme.colorScheme}
|
||||||
|
@ -277,8 +277,8 @@ function ProvidedHandleForm({
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={onToggleCustom}
|
onPress={onToggleCustom}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityHint="Hosting provider"
|
accessibilityLabel={_(msg`Hosting provider`)}
|
||||||
accessibilityLabel={_(msg`Opens modal for using custom domain`)}>
|
accessibilityHint={_(msg`Opens modal for using custom domain`)}>
|
||||||
<Text type="md-medium" style={[pal.link, s.pl10, s.pt5]}>
|
<Text type="md-medium" style={[pal.link, s.pl10, s.pt5]}>
|
||||||
<Trans>I have my own domain</Trans>
|
<Trans>I have my own domain</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -324,8 +324,8 @@ function CustomHandleForm({
|
||||||
Clipboard.setString(
|
Clipboard.setString(
|
||||||
isDNSForm ? `did=${currentAccount.did}` : currentAccount.did,
|
isDNSForm ? `did=${currentAccount.did}` : currentAccount.did,
|
||||||
)
|
)
|
||||||
Toast.show('Copied to clipboard')
|
Toast.show(_(msg`Copied to clipboard`))
|
||||||
}, [currentAccount, isDNSForm])
|
}, [currentAccount, isDNSForm, _])
|
||||||
const onChangeHandle = React.useCallback(
|
const onChangeHandle = React.useCallback(
|
||||||
(v: string) => {
|
(v: string) => {
|
||||||
setHandle(v)
|
setHandle(v)
|
||||||
|
@ -378,7 +378,7 @@ function CustomHandleForm({
|
||||||
<TextInput
|
<TextInput
|
||||||
testID="setHandleInput"
|
testID="setHandleInput"
|
||||||
style={[pal.text, styles.textInput]}
|
style={[pal.text, styles.textInput]}
|
||||||
placeholder="e.g. alice.com"
|
placeholder={_(msg`e.g. alice.com`)}
|
||||||
placeholderTextColor={pal.colors.textLight}
|
placeholderTextColor={pal.colors.textLight}
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
keyboardAppearance={theme.colorScheme}
|
keyboardAppearance={theme.colorScheme}
|
||||||
|
@ -387,7 +387,7 @@ function CustomHandleForm({
|
||||||
editable={!isProcessing}
|
editable={!isProcessing}
|
||||||
accessibilityLabelledBy="customDomain"
|
accessibilityLabelledBy="customDomain"
|
||||||
accessibilityLabel={_(msg`Custom domain`)}
|
accessibilityLabel={_(msg`Custom domain`)}
|
||||||
accessibilityHint="Input your preferred hosting provider"
|
accessibilityHint={_(msg`Input your preferred hosting provider`)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.spacer} />
|
<View style={styles.spacer} />
|
||||||
|
@ -395,18 +395,18 @@ function CustomHandleForm({
|
||||||
<View style={[styles.selectableBtns]}>
|
<View style={[styles.selectableBtns]}>
|
||||||
<SelectableBtn
|
<SelectableBtn
|
||||||
selected={isDNSForm}
|
selected={isDNSForm}
|
||||||
label="DNS Panel"
|
label={_(msg`DNS Panel`)}
|
||||||
left
|
left
|
||||||
onSelect={() => setDNSForm(true)}
|
onSelect={() => setDNSForm(true)}
|
||||||
accessibilityHint="Use the DNS panel"
|
accessibilityHint={_(msg`Use the DNS panel`)}
|
||||||
style={s.flex1}
|
style={s.flex1}
|
||||||
/>
|
/>
|
||||||
<SelectableBtn
|
<SelectableBtn
|
||||||
selected={!isDNSForm}
|
selected={!isDNSForm}
|
||||||
label="No DNS Panel"
|
label={_(msg`No DNS Panel`)}
|
||||||
right
|
right
|
||||||
onSelect={() => setDNSForm(false)}
|
onSelect={() => setDNSForm(false)}
|
||||||
accessibilityHint="Use a file on your server"
|
accessibilityHint={_(msg`Use a file on your server`)}
|
||||||
style={s.flex1}
|
style={s.flex1}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
@ -418,7 +418,7 @@ function CustomHandleForm({
|
||||||
</Text>
|
</Text>
|
||||||
<View style={[styles.dnsTable, pal.btn]}>
|
<View style={[styles.dnsTable, pal.btn]}>
|
||||||
<Text type="md-medium" style={[styles.dnsLabel, pal.text]}>
|
<Text type="md-medium" style={[styles.dnsLabel, pal.text]}>
|
||||||
Host:
|
<Trans>Host:</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<View style={[styles.dnsValue]}>
|
<View style={[styles.dnsValue]}>
|
||||||
<Text type="mono" style={[styles.monoText, pal.text]}>
|
<Text type="mono" style={[styles.monoText, pal.text]}>
|
||||||
|
@ -426,7 +426,7 @@ function CustomHandleForm({
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text type="md-medium" style={[styles.dnsLabel, pal.text]}>
|
<Text type="md-medium" style={[styles.dnsLabel, pal.text]}>
|
||||||
Type:
|
<Trans>Type:</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<View style={[styles.dnsValue]}>
|
<View style={[styles.dnsValue]}>
|
||||||
<Text type="mono" style={[styles.monoText, pal.text]}>
|
<Text type="mono" style={[styles.monoText, pal.text]}>
|
||||||
|
@ -434,7 +434,7 @@ function CustomHandleForm({
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text type="md-medium" style={[styles.dnsLabel, pal.text]}>
|
<Text type="md-medium" style={[styles.dnsLabel, pal.text]}>
|
||||||
Value:
|
<Trans>Value:</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<View style={[styles.dnsValue]}>
|
<View style={[styles.dnsValue]}>
|
||||||
<Text type="mono" style={[styles.monoText, pal.text]}>
|
<Text type="mono" style={[styles.monoText, pal.text]}>
|
||||||
|
@ -443,7 +443,7 @@ function CustomHandleForm({
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Text type="md" style={[pal.text, s.pt20, s.pl5]}>
|
<Text type="md" style={[pal.text, s.pt20, s.pl5]}>
|
||||||
This should create a domain record at:{' '}
|
<Trans>This should create a domain record at:</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Text type="mono" style={[styles.monoText, pal.text, s.pt5, s.pl5]}>
|
<Text type="mono" style={[styles.monoText, pal.text, s.pt5, s.pl5]}>
|
||||||
_atproto.{handle}
|
_atproto.{handle}
|
||||||
|
@ -463,7 +463,7 @@ function CustomHandleForm({
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.spacer} />
|
<View style={styles.spacer} />
|
||||||
<Text type="md" style={[pal.text, s.pb5, s.pl5]}>
|
<Text type="md" style={[pal.text, s.pb5, s.pl5]}>
|
||||||
That contains the following:
|
<Trans>That contains the following:</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<View style={[styles.valueContainer, pal.btn]}>
|
<View style={[styles.valueContainer, pal.btn]}>
|
||||||
<View style={[styles.dnsValue]}>
|
<View style={[styles.dnsValue]}>
|
||||||
|
@ -478,7 +478,9 @@ function CustomHandleForm({
|
||||||
<View style={styles.spacer} />
|
<View style={styles.spacer} />
|
||||||
<Button type="default" style={[s.p20, s.mb10]} onPress={onPressCopy}>
|
<Button type="default" style={[s.p20, s.mb10]} onPress={onPressCopy}>
|
||||||
<Text type="xl" style={[pal.link, s.textCenter]}>
|
<Text type="xl" style={[pal.link, s.textCenter]}>
|
||||||
Copy {isDNSForm ? 'Domain Value' : 'File Contents'}
|
<Trans>
|
||||||
|
Copy {isDNSForm ? _(msg`Domain Value`) : _(msg`File Contents`)}
|
||||||
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
{canSave === true && (
|
{canSave === true && (
|
||||||
|
@ -504,8 +506,8 @@ function CustomHandleForm({
|
||||||
) : (
|
) : (
|
||||||
<Text type="xl-medium" style={[s.white, s.textCenter]}>
|
<Text type="xl-medium" style={[s.white, s.textCenter]}>
|
||||||
{canSave
|
{canSave
|
||||||
? `Update to ${handle}`
|
? _(msg`Update to ${handle}`)
|
||||||
: `Verify ${isDNSForm ? 'DNS Record' : 'Text File'}`}
|
: _(msg`Verify ${isDNSForm ? 'DNS Record' : 'Text File'}`)}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -513,9 +515,9 @@ function CustomHandleForm({
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={onToggleCustom}
|
onPress={onToggleCustom}
|
||||||
accessibilityLabel={_(msg`Use default provider`)}
|
accessibilityLabel={_(msg`Use default provider`)}
|
||||||
accessibilityHint="Use bsky.social as hosting provider">
|
accessibilityHint={_(msg`Use bsky.social as hosting provider`)}>
|
||||||
<Text type="md-medium" style={[pal.link, s.pl10, s.pt5]}>
|
<Text type="md-medium" style={[pal.link, s.pl10, s.pt5]}>
|
||||||
Nevermind, create a handle for me
|
<Trans>Nevermind, create a handle for me</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -137,7 +137,9 @@ export function Component() {
|
||||||
<View>
|
<View>
|
||||||
<View style={styles.titleSection}>
|
<View style={styles.titleSection}>
|
||||||
<Text type="title-lg" style={[pal.text, styles.title]}>
|
<Text type="title-lg" style={[pal.text, styles.title]}>
|
||||||
{stage !== Stages.Done ? 'Change Password' : 'Password Changed'}
|
{stage !== Stages.Done
|
||||||
|
? _(msg`Change Password`)
|
||||||
|
: _(msg`Password Changed`)}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
@ -180,7 +182,7 @@ export function Component() {
|
||||||
<TextInput
|
<TextInput
|
||||||
testID="codeInput"
|
testID="codeInput"
|
||||||
style={[pal.text, styles.textInput]}
|
style={[pal.text, styles.textInput]}
|
||||||
placeholder="Reset code"
|
placeholder={_(msg`Reset code`)}
|
||||||
placeholderTextColor={pal.colors.textLight}
|
placeholderTextColor={pal.colors.textLight}
|
||||||
value={resetCode}
|
value={resetCode}
|
||||||
onChangeText={setResetCode}
|
onChangeText={setResetCode}
|
||||||
|
@ -207,7 +209,7 @@ export function Component() {
|
||||||
<TextInput
|
<TextInput
|
||||||
testID="codeInput"
|
testID="codeInput"
|
||||||
style={[pal.text, styles.textInput]}
|
style={[pal.text, styles.textInput]}
|
||||||
placeholder="New password"
|
placeholder={_(msg`New password`)}
|
||||||
placeholderTextColor={pal.colors.textLight}
|
placeholderTextColor={pal.colors.textLight}
|
||||||
onChangeText={setNewPassword}
|
onChangeText={setNewPassword}
|
||||||
secureTextEntry
|
secureTextEntry
|
||||||
|
|
|
@ -173,7 +173,7 @@ export function Component({}: {}) {
|
||||||
</Text>
|
</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.textInput, pal.borderDark, pal.text, styles.mb20]}
|
style={[styles.textInput, pal.borderDark, pal.text, styles.mb20]}
|
||||||
placeholder="Confirmation code"
|
placeholder={_(msg`Confirmation code`)}
|
||||||
placeholderTextColor={pal.textLight.color}
|
placeholderTextColor={pal.textLight.color}
|
||||||
keyboardAppearance={theme.colorScheme}
|
keyboardAppearance={theme.colorScheme}
|
||||||
value={confirmCode}
|
value={confirmCode}
|
||||||
|
@ -192,7 +192,7 @@ export function Component({}: {}) {
|
||||||
</Text>
|
</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.textInput, pal.borderDark, pal.text]}
|
style={[styles.textInput, pal.borderDark, pal.text]}
|
||||||
placeholder="Password"
|
placeholder={_(msg`Password`)}
|
||||||
placeholderTextColor={pal.textLight.color}
|
placeholderTextColor={pal.textLight.color}
|
||||||
keyboardAppearance={theme.colorScheme}
|
keyboardAppearance={theme.colorScheme}
|
||||||
secureTextEntry
|
secureTextEntry
|
||||||
|
@ -228,7 +228,7 @@ export function Component({}: {}) {
|
||||||
onPress={onCancel}
|
onPress={onCancel}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Cancel account deletion`)}
|
accessibilityLabel={_(msg`Cancel account deletion`)}
|
||||||
accessibilityHint="Exits account deletion process"
|
accessibilityHint={_(msg`Exits account deletion process`)}
|
||||||
onAccessibilityEscape={onCancel}>
|
onAccessibilityEscape={onCancel}>
|
||||||
<Text type="button-lg" style={pal.textLight}>
|
<Text type="button-lg" style={pal.textLight}>
|
||||||
<Trans context="action">Cancel</Trans>
|
<Trans context="action">Cancel</Trans>
|
||||||
|
|
|
@ -77,7 +77,7 @@ export function Component({href}: {href: string}) {
|
||||||
}}
|
}}
|
||||||
accessibilityLabel={_(msg`Cancel`)}
|
accessibilityLabel={_(msg`Cancel`)}
|
||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
label="Cancel"
|
label={_(msg`Cancel`)}
|
||||||
labelContainerStyle={{justifyContent: 'center', padding: 8}}
|
labelContainerStyle={{justifyContent: 'center', padding: 8}}
|
||||||
labelStyle={[s.f18]}
|
labelStyle={[s.f18]}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -73,8 +73,8 @@ export function Component({text, href}: {text: string; href: string}) {
|
||||||
type="primary"
|
type="primary"
|
||||||
onPress={onPressVisit}
|
onPress={onPressVisit}
|
||||||
accessibilityLabel={_(msg`Visit Site`)}
|
accessibilityLabel={_(msg`Visit Site`)}
|
||||||
accessibilityHint=""
|
accessibilityHint={_(msg`Opens the linked website`)}
|
||||||
label="Visit Site"
|
label={_(msg`Visit Site`)}
|
||||||
labelContainerStyle={{justifyContent: 'center', padding: 4}}
|
labelContainerStyle={{justifyContent: 'center', padding: 4}}
|
||||||
labelStyle={[s.f18]}
|
labelStyle={[s.f18]}
|
||||||
/>
|
/>
|
||||||
|
@ -85,8 +85,8 @@ export function Component({text, href}: {text: string; href: string}) {
|
||||||
closeModal()
|
closeModal()
|
||||||
}}
|
}}
|
||||||
accessibilityLabel={_(msg`Cancel`)}
|
accessibilityLabel={_(msg`Cancel`)}
|
||||||
accessibilityHint=""
|
accessibilityHint={_(msg`Cancels opening the linked website`)}
|
||||||
label="Cancel"
|
label={_(msg`Cancel`)}
|
||||||
labelContainerStyle={{justifyContent: 'center', padding: 4}}
|
labelContainerStyle={{justifyContent: 'center', padding: 4}}
|
||||||
labelStyle={[s.f18]}
|
labelStyle={[s.f18]}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -231,7 +231,11 @@ function UserResult({
|
||||||
width: 54,
|
width: 54,
|
||||||
paddingLeft: 4,
|
paddingLeft: 4,
|
||||||
}}>
|
}}>
|
||||||
<UserAvatar size={40} avatar={profile.avatar} />
|
<UserAvatar
|
||||||
|
size={40}
|
||||||
|
avatar={profile.avatar}
|
||||||
|
type={profile.associated?.labeler ? 'labeler' : 'user'}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
|
|
|
@ -45,7 +45,11 @@ function SwitchAccountCard({account}: {account: SessionAccount}) {
|
||||||
const contents = (
|
const contents = (
|
||||||
<View style={[pal.view, styles.linkCard]}>
|
<View style={[pal.view, styles.linkCard]}>
|
||||||
<View style={styles.avi}>
|
<View style={styles.avi}>
|
||||||
<UserAvatar size={40} avatar={profile?.avatar} />
|
<UserAvatar
|
||||||
|
size={40}
|
||||||
|
avatar={profile?.avatar}
|
||||||
|
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={[s.flex1]}>
|
<View style={[s.flex1]}>
|
||||||
<Text type="md-bold" style={pal.text} numberOfLines={1}>
|
<Text type="md-bold" style={pal.text} numberOfLines={1}>
|
||||||
|
|
|
@ -180,7 +180,7 @@ function ListItem({
|
||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
<View style={styles.listItemAvi}>
|
<View style={styles.listItemAvi}>
|
||||||
<UserAvatar size={40} avatar={list.avatar} />
|
<UserAvatar size={40} avatar={list.avatar} type="list" />
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.listItemContent}>
|
<View style={styles.listItemContent}>
|
||||||
<Text
|
<Text
|
||||||
|
|
|
@ -149,7 +149,7 @@ export function Component({showReminder}: {showReminder?: boolean}) {
|
||||||
onPress={onEmailIncorrect}
|
onPress={onEmailIncorrect}
|
||||||
style={styles.changeEmailLink}>
|
style={styles.changeEmailLink}>
|
||||||
<Text type="lg" style={pal.link}>
|
<Text type="lg" style={pal.link}>
|
||||||
Change
|
<Trans>Change</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -100,7 +100,7 @@ export function Component({
|
||||||
onPress={doSetAs(AspectRatio.Wide)}
|
onPress={doSetAs(AspectRatio.Wide)}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Wide`)}
|
accessibilityLabel={_(msg`Wide`)}
|
||||||
accessibilityHint="Sets image aspect ratio to wide">
|
accessibilityHint={_(msg`Sets image aspect ratio to wide`)}>
|
||||||
<RectWideIcon
|
<RectWideIcon
|
||||||
size={24}
|
size={24}
|
||||||
style={as === AspectRatio.Wide ? s.blue3 : pal.text}
|
style={as === AspectRatio.Wide ? s.blue3 : pal.text}
|
||||||
|
@ -110,7 +110,7 @@ export function Component({
|
||||||
onPress={doSetAs(AspectRatio.Tall)}
|
onPress={doSetAs(AspectRatio.Tall)}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Tall`)}
|
accessibilityLabel={_(msg`Tall`)}
|
||||||
accessibilityHint="Sets image aspect ratio to tall">
|
accessibilityHint={_(msg`Sets image aspect ratio to tall`)}>
|
||||||
<RectTallIcon
|
<RectTallIcon
|
||||||
size={24}
|
size={24}
|
||||||
style={as === AspectRatio.Tall ? s.blue3 : pal.text}
|
style={as === AspectRatio.Tall ? s.blue3 : pal.text}
|
||||||
|
@ -120,7 +120,7 @@ export function Component({
|
||||||
onPress={doSetAs(AspectRatio.Square)}
|
onPress={doSetAs(AspectRatio.Square)}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Square`)}
|
accessibilityLabel={_(msg`Square`)}
|
||||||
accessibilityHint="Sets image aspect ratio to square">
|
accessibilityHint={_(msg`Sets image aspect ratio to square`)}>
|
||||||
<SquareIcon
|
<SquareIcon
|
||||||
size={24}
|
size={24}
|
||||||
style={as === AspectRatio.Square ? s.blue3 : pal.text}
|
style={as === AspectRatio.Square ? s.blue3 : pal.text}
|
||||||
|
@ -132,9 +132,9 @@ export function Component({
|
||||||
onPress={onPressCancel}
|
onPress={onPressCancel}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Cancel image crop`)}
|
accessibilityLabel={_(msg`Cancel image crop`)}
|
||||||
accessibilityHint="Exits image cropping process">
|
accessibilityHint={_(msg`Exits image cropping process`)}>
|
||||||
<Text type="xl" style={pal.link}>
|
<Text type="xl" style={pal.link}>
|
||||||
Cancel
|
<Trans>Cancel</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<View style={s.flex1} />
|
<View style={s.flex1} />
|
||||||
|
@ -142,7 +142,7 @@ export function Component({
|
||||||
onPress={onPressDone}
|
onPress={onPressDone}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Save image crop`)}
|
accessibilityLabel={_(msg`Save image crop`)}
|
||||||
accessibilityHint="Saves image crop settings">
|
accessibilityHint={_(msg`Saves image crop settings`)}>
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={[gradients.blueLight.start, gradients.blueLight.end]}
|
colors={[gradients.blueLight.start, gradients.blueLight.end]}
|
||||||
start={{x: 0, y: 0}}
|
start={{x: 0, y: 0}}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
ModerationDecision,
|
ModerationDecision,
|
||||||
moderateProfile,
|
moderateProfile,
|
||||||
AppBskyEmbedRecordWithMedia,
|
AppBskyEmbedRecordWithMedia,
|
||||||
|
AppBskyActorDefs,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {AtUri} from '@atproto/api'
|
import {AtUri} from '@atproto/api'
|
||||||
import {
|
import {
|
||||||
|
@ -55,6 +56,7 @@ interface Author {
|
||||||
displayName?: string
|
displayName?: string
|
||||||
avatar?: string
|
avatar?: string
|
||||||
moderation: ModerationDecision
|
moderation: ModerationDecision
|
||||||
|
associated?: AppBskyActorDefs.ProfileAssociated
|
||||||
}
|
}
|
||||||
|
|
||||||
let FeedItem = ({
|
let FeedItem = ({
|
||||||
|
@ -100,6 +102,7 @@ let FeedItem = ({
|
||||||
displayName: item.notification.author.displayName,
|
displayName: item.notification.author.displayName,
|
||||||
avatar: item.notification.author.avatar,
|
avatar: item.notification.author.avatar,
|
||||||
moderation: moderateProfile(item.notification.author, moderationOpts),
|
moderation: moderateProfile(item.notification.author, moderationOpts),
|
||||||
|
associated: item.notification.author.associated,
|
||||||
},
|
},
|
||||||
...(item.additional?.map(({author}) => {
|
...(item.additional?.map(({author}) => {
|
||||||
return {
|
return {
|
||||||
|
@ -109,6 +112,7 @@ let FeedItem = ({
|
||||||
displayName: author.displayName,
|
displayName: author.displayName,
|
||||||
avatar: author.avatar,
|
avatar: author.avatar,
|
||||||
moderation: moderateProfile(author, moderationOpts),
|
moderation: moderateProfile(author, moderationOpts),
|
||||||
|
associated: author.associated,
|
||||||
}
|
}
|
||||||
}) || []),
|
}) || []),
|
||||||
]
|
]
|
||||||
|
@ -337,6 +341,7 @@ function CondensedAuthorsList({
|
||||||
handle={authors[0].handle}
|
handle={authors[0].handle}
|
||||||
avatar={authors[0].avatar}
|
avatar={authors[0].avatar}
|
||||||
moderation={authors[0].moderation.ui('avatar')}
|
moderation={authors[0].moderation.ui('avatar')}
|
||||||
|
type={authors[0].associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -355,6 +360,7 @@ function CondensedAuthorsList({
|
||||||
size={35}
|
size={35}
|
||||||
avatar={author.avatar}
|
avatar={author.avatar}
|
||||||
moderation={author.moderation.ui('avatar')}
|
moderation={author.moderation.ui('avatar')}
|
||||||
|
type={author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
@ -413,6 +419,7 @@ function ExpandedAuthorsList({
|
||||||
size={35}
|
size={35}
|
||||||
avatar={author.avatar}
|
avatar={author.avatar}
|
||||||
moderation={author.moderation.ui('avatar')}
|
moderation={author.moderation.ui('avatar')}
|
||||||
|
type={author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={s.flex1}>
|
<View style={s.flex1}>
|
||||||
|
|
|
@ -1,25 +1,14 @@
|
||||||
import React, {useEffect, useRef} from 'react'
|
import React, {useEffect, useRef} from 'react'
|
||||||
import {
|
import {StyleSheet, useWindowDimensions, View} from 'react-native'
|
||||||
ActivityIndicator,
|
|
||||||
Pressable,
|
|
||||||
StyleSheet,
|
|
||||||
TouchableOpacity,
|
|
||||||
View,
|
|
||||||
} from 'react-native'
|
|
||||||
import {AppBskyFeedDefs} from '@atproto/api'
|
import {AppBskyFeedDefs} from '@atproto/api'
|
||||||
import {CenteredView} from '../util/Views'
|
import {Trans, msg} from '@lingui/macro'
|
||||||
import {LoadingScreen} from '../util/LoadingScreen'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {List, ListMethods} from '../util/List'
|
import {List, ListMethods} from '../util/List'
|
||||||
import {
|
|
||||||
FontAwesomeIcon,
|
|
||||||
FontAwesomeIconStyle,
|
|
||||||
} from '@fortawesome/react-native-fontawesome'
|
|
||||||
import {PostThreadItem} from './PostThreadItem'
|
import {PostThreadItem} from './PostThreadItem'
|
||||||
import {ComposePrompt} from '../composer/Prompt'
|
import {ComposePrompt} from '../composer/Prompt'
|
||||||
import {ViewHeader} from '../util/ViewHeader'
|
import {ViewHeader} from '../util/ViewHeader'
|
||||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {s} from 'lib/styles'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||||
import {
|
import {
|
||||||
|
@ -30,21 +19,18 @@ import {
|
||||||
usePostThreadQuery,
|
usePostThreadQuery,
|
||||||
sortThread,
|
sortThread,
|
||||||
} from '#/state/queries/post-thread'
|
} from '#/state/queries/post-thread'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
|
||||||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
|
||||||
import {Trans, msg} from '@lingui/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {
|
import {
|
||||||
UsePreferencesQueryResponse,
|
|
||||||
useModerationOpts,
|
useModerationOpts,
|
||||||
usePreferencesQuery,
|
usePreferencesQuery,
|
||||||
} from '#/state/queries/preferences'
|
} from '#/state/queries/preferences'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {isAndroid, isNative, isWeb} from '#/platform/detection'
|
import {isAndroid, isNative, isWeb} from '#/platform/detection'
|
||||||
import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
|
import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
|
||||||
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
|
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
|
||||||
|
import {cleanError} from 'lib/strings/errors'
|
||||||
|
|
||||||
// FlatList maintainVisibleContentPosition breaks if too many items
|
// FlatList maintainVisibleContentPosition breaks if too many items
|
||||||
// are prepended. This seems to be an optimal number based on *shrug*.
|
// are prepended. This seems to be an optimal number based on *shrug*.
|
||||||
|
@ -58,9 +44,7 @@ const MAINTAIN_VISIBLE_CONTENT_POSITION = {
|
||||||
|
|
||||||
const TOP_COMPONENT = {_reactKey: '__top_component__'}
|
const TOP_COMPONENT = {_reactKey: '__top_component__'}
|
||||||
const REPLY_PROMPT = {_reactKey: '__reply__'}
|
const REPLY_PROMPT = {_reactKey: '__reply__'}
|
||||||
const CHILD_SPINNER = {_reactKey: '__child_spinner__'}
|
|
||||||
const LOAD_MORE = {_reactKey: '__load_more__'}
|
const LOAD_MORE = {_reactKey: '__load_more__'}
|
||||||
const BOTTOM_COMPONENT = {_reactKey: '__bottom_component__'}
|
|
||||||
|
|
||||||
type YieldedItem = ThreadPost | ThreadBlocked | ThreadNotFound
|
type YieldedItem = ThreadPost | ThreadBlocked | ThreadNotFound
|
||||||
type RowItem =
|
type RowItem =
|
||||||
|
@ -68,9 +52,7 @@ type RowItem =
|
||||||
// TODO: TS doesn't actually enforce it's one of these, it only enforces matching shape.
|
// TODO: TS doesn't actually enforce it's one of these, it only enforces matching shape.
|
||||||
| typeof TOP_COMPONENT
|
| typeof TOP_COMPONENT
|
||||||
| typeof REPLY_PROMPT
|
| typeof REPLY_PROMPT
|
||||||
| typeof CHILD_SPINNER
|
|
||||||
| typeof LOAD_MORE
|
| typeof LOAD_MORE
|
||||||
| typeof BOTTOM_COMPONENT
|
|
||||||
|
|
||||||
type ThreadSkeletonParts = {
|
type ThreadSkeletonParts = {
|
||||||
parents: YieldedItem[]
|
parents: YieldedItem[]
|
||||||
|
@ -78,6 +60,10 @@ type ThreadSkeletonParts = {
|
||||||
replies: YieldedItem[]
|
replies: YieldedItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const keyExtractor = (item: RowItem) => {
|
||||||
|
return item._reactKey
|
||||||
|
}
|
||||||
|
|
||||||
export function PostThread({
|
export function PostThread({
|
||||||
uri,
|
uri,
|
||||||
onCanReply,
|
onCanReply,
|
||||||
|
@ -85,17 +71,30 @@ export function PostThread({
|
||||||
}: {
|
}: {
|
||||||
uri: string | undefined
|
uri: string | undefined
|
||||||
onCanReply: (canReply: boolean) => void
|
onCanReply: (canReply: boolean) => void
|
||||||
onPressReply: () => void
|
onPressReply: () => unknown
|
||||||
}) {
|
}) {
|
||||||
|
const {hasSession} = useSession()
|
||||||
|
const {_} = useLingui()
|
||||||
|
const pal = usePalette('default')
|
||||||
|
const {isMobile, isTabletOrMobile} = useWebMediaQueries()
|
||||||
|
const initialNumToRender = useInitialNumToRender()
|
||||||
|
const {height: windowHeight} = useWindowDimensions()
|
||||||
|
|
||||||
|
const {data: preferences} = usePreferencesQuery()
|
||||||
const {
|
const {
|
||||||
isLoading,
|
isFetching,
|
||||||
isError,
|
isError: isThreadError,
|
||||||
error,
|
error: threadError,
|
||||||
refetch,
|
refetch,
|
||||||
data: thread,
|
data: thread,
|
||||||
} = usePostThreadQuery(uri)
|
} = usePostThreadQuery(uri)
|
||||||
const {data: preferences} = usePreferencesQuery()
|
|
||||||
|
|
||||||
|
const treeView = React.useMemo(
|
||||||
|
() =>
|
||||||
|
!!preferences?.threadViewPrefs?.lab_treeViewEnabled &&
|
||||||
|
hasBranchingReplies(thread),
|
||||||
|
[preferences?.threadViewPrefs, thread],
|
||||||
|
)
|
||||||
const rootPost = thread?.type === 'post' ? thread.post : undefined
|
const rootPost = thread?.type === 'post' ? thread.post : undefined
|
||||||
const rootPostRecord = thread?.type === 'post' ? thread.record : undefined
|
const rootPostRecord = thread?.type === 'post' ? thread.record : undefined
|
||||||
|
|
||||||
|
@ -105,7 +104,6 @@ export function PostThread({
|
||||||
rootPost && moderationOpts
|
rootPost && moderationOpts
|
||||||
? moderatePost(rootPost, moderationOpts)
|
? moderatePost(rootPost, moderationOpts)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
return !!mod
|
return !!mod
|
||||||
?.ui('contentList')
|
?.ui('contentList')
|
||||||
.blurs.find(
|
.blurs.find(
|
||||||
|
@ -114,6 +112,14 @@ export function PostThread({
|
||||||
)
|
)
|
||||||
}, [rootPost, moderationOpts])
|
}, [rootPost, moderationOpts])
|
||||||
|
|
||||||
|
// Values used for proper rendering of parents
|
||||||
|
const ref = useRef<ListMethods>(null)
|
||||||
|
const highlightedPostRef = useRef<View | null>(null)
|
||||||
|
const [maxParents, setMaxParents] = React.useState(
|
||||||
|
isWeb ? Infinity : PARENTS_CHUNK_SIZE,
|
||||||
|
)
|
||||||
|
const [maxReplies, setMaxReplies] = React.useState(50)
|
||||||
|
|
||||||
useSetTitle(
|
useSetTitle(
|
||||||
rootPost && !isNoPwi
|
rootPost && !isNoPwi
|
||||||
? `${sanitizeDisplayName(
|
? `${sanitizeDisplayName(
|
||||||
|
@ -121,62 +127,6 @@ export function PostThread({
|
||||||
)}: "${rootPostRecord!.text}"`
|
)}: "${rootPostRecord!.text}"`
|
||||||
: '',
|
: '',
|
||||||
)
|
)
|
||||||
useEffect(() => {
|
|
||||||
if (rootPost) {
|
|
||||||
onCanReply(!rootPost.viewer?.replyDisabled)
|
|
||||||
}
|
|
||||||
}, [rootPost, onCanReply])
|
|
||||||
|
|
||||||
if (isError || AppBskyFeedDefs.isNotFoundPost(thread)) {
|
|
||||||
return (
|
|
||||||
<PostThreadError
|
|
||||||
error={error}
|
|
||||||
notFound={AppBskyFeedDefs.isNotFoundPost(thread)}
|
|
||||||
onRefresh={refetch}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (AppBskyFeedDefs.isBlockedPost(thread)) {
|
|
||||||
return <PostThreadBlocked />
|
|
||||||
}
|
|
||||||
if (!thread || isLoading || !preferences) {
|
|
||||||
return <LoadingScreen />
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<PostThreadLoaded
|
|
||||||
thread={thread}
|
|
||||||
threadViewPrefs={preferences.threadViewPrefs}
|
|
||||||
onRefresh={refetch}
|
|
||||||
onPressReply={onPressReply}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function PostThreadLoaded({
|
|
||||||
thread,
|
|
||||||
threadViewPrefs,
|
|
||||||
onRefresh,
|
|
||||||
onPressReply,
|
|
||||||
}: {
|
|
||||||
thread: ThreadNode
|
|
||||||
threadViewPrefs: UsePreferencesQueryResponse['threadViewPrefs']
|
|
||||||
onRefresh: () => void
|
|
||||||
onPressReply: () => void
|
|
||||||
}) {
|
|
||||||
const {hasSession} = useSession()
|
|
||||||
const {_} = useLingui()
|
|
||||||
const pal = usePalette('default')
|
|
||||||
const {isMobile, isTabletOrMobile} = useWebMediaQueries()
|
|
||||||
const ref = useRef<ListMethods>(null)
|
|
||||||
const highlightedPostRef = useRef<View | null>(null)
|
|
||||||
const [maxParents, setMaxParents] = React.useState(
|
|
||||||
isWeb ? Infinity : PARENTS_CHUNK_SIZE,
|
|
||||||
)
|
|
||||||
const [maxReplies, setMaxReplies] = React.useState(100)
|
|
||||||
const treeView = React.useMemo(
|
|
||||||
() => !!threadViewPrefs.lab_treeViewEnabled && hasBranchingReplies(thread),
|
|
||||||
[threadViewPrefs, thread],
|
|
||||||
)
|
|
||||||
|
|
||||||
// On native, this is going to start out `true`. We'll toggle it to `false` after the initial render if flushed.
|
// On native, this is going to start out `true`. We'll toggle it to `false` after the initial render if flushed.
|
||||||
// This ensures that the first render contains no parents--even if they are already available in the cache.
|
// This ensures that the first render contains no parents--even if they are already available in the cache.
|
||||||
|
@ -184,18 +134,56 @@ function PostThreadLoaded({
|
||||||
// On the web this is not necessary because we can synchronously adjust the scroll in onContentSizeChange instead.
|
// On the web this is not necessary because we can synchronously adjust the scroll in onContentSizeChange instead.
|
||||||
const [deferParents, setDeferParents] = React.useState(isNative)
|
const [deferParents, setDeferParents] = React.useState(isNative)
|
||||||
|
|
||||||
const skeleton = React.useMemo(
|
const skeleton = React.useMemo(() => {
|
||||||
() =>
|
const threadViewPrefs = preferences?.threadViewPrefs
|
||||||
createThreadSkeleton(
|
if (!threadViewPrefs || !thread) return null
|
||||||
sortThread(thread, threadViewPrefs),
|
|
||||||
hasSession,
|
return createThreadSkeleton(
|
||||||
treeView,
|
sortThread(thread, threadViewPrefs),
|
||||||
),
|
hasSession,
|
||||||
[thread, threadViewPrefs, hasSession, treeView],
|
treeView,
|
||||||
)
|
)
|
||||||
|
}, [thread, preferences?.threadViewPrefs, hasSession, treeView])
|
||||||
|
|
||||||
|
const error = React.useMemo(() => {
|
||||||
|
if (AppBskyFeedDefs.isNotFoundPost(thread)) {
|
||||||
|
return {
|
||||||
|
title: _(msg`Post not found`),
|
||||||
|
message: _(msg`The post may have been deleted.`),
|
||||||
|
}
|
||||||
|
} else if (skeleton?.highlightedPost.type === 'blocked') {
|
||||||
|
return {
|
||||||
|
title: _(msg`Post hidden`),
|
||||||
|
message: _(
|
||||||
|
msg`You have blocked the author or you have been blocked by the author.`,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
} else if (threadError?.message.startsWith('Post not found')) {
|
||||||
|
return {
|
||||||
|
title: _(msg`Post not found`),
|
||||||
|
message: _(msg`The post may have been deleted.`),
|
||||||
|
}
|
||||||
|
} else if (isThreadError) {
|
||||||
|
return {
|
||||||
|
message: threadError ? cleanError(threadError) : undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}, [thread, skeleton?.highlightedPost, isThreadError, _, threadError])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (error) {
|
||||||
|
onCanReply(false)
|
||||||
|
} else if (rootPost) {
|
||||||
|
onCanReply(!rootPost.viewer?.replyDisabled)
|
||||||
|
}
|
||||||
|
}, [rootPost, onCanReply, error])
|
||||||
|
|
||||||
// construct content
|
// construct content
|
||||||
const posts = React.useMemo(() => {
|
const posts = React.useMemo(() => {
|
||||||
|
if (!skeleton) return []
|
||||||
|
|
||||||
const {parents, highlightedPost, replies} = skeleton
|
const {parents, highlightedPost, replies} = skeleton
|
||||||
let arr: RowItem[] = []
|
let arr: RowItem[] = []
|
||||||
if (highlightedPost.type === 'post') {
|
if (highlightedPost.type === 'post') {
|
||||||
|
@ -231,17 +219,11 @@ function PostThreadLoaded({
|
||||||
if (!highlightedPost.post.viewer?.replyDisabled) {
|
if (!highlightedPost.post.viewer?.replyDisabled) {
|
||||||
arr.push(REPLY_PROMPT)
|
arr.push(REPLY_PROMPT)
|
||||||
}
|
}
|
||||||
if (highlightedPost.ctx.isChildLoading) {
|
for (let i = 0; i < replies.length; i++) {
|
||||||
arr.push(CHILD_SPINNER)
|
arr.push(replies[i])
|
||||||
} else {
|
if (i === maxReplies) {
|
||||||
for (let i = 0; i < replies.length; i++) {
|
break
|
||||||
arr.push(replies[i])
|
|
||||||
if (i === maxReplies) {
|
|
||||||
arr.push(LOAD_MORE)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
arr.push(BOTTOM_COMPONENT)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return arr
|
return arr
|
||||||
|
@ -256,7 +238,7 @@ function PostThreadLoaded({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// wait for loading to finish
|
// wait for loading to finish
|
||||||
if (thread.type === 'post' && !!thread.parent) {
|
if (thread?.type === 'post' && !!thread.parent) {
|
||||||
function onMeasure(pageY: number) {
|
function onMeasure(pageY: number) {
|
||||||
ref.current?.scrollToOffset({
|
ref.current?.scrollToOffset({
|
||||||
animated: false,
|
animated: false,
|
||||||
|
@ -280,10 +262,10 @@ function PostThreadLoaded({
|
||||||
// To work around this, we prepend rows after scroll bumps against the top and rests.
|
// To work around this, we prepend rows after scroll bumps against the top and rests.
|
||||||
const needsBumpMaxParents = React.useRef(false)
|
const needsBumpMaxParents = React.useRef(false)
|
||||||
const onStartReached = React.useCallback(() => {
|
const onStartReached = React.useCallback(() => {
|
||||||
if (maxParents < skeleton.parents.length) {
|
if (skeleton?.parents && maxParents < skeleton.parents.length) {
|
||||||
needsBumpMaxParents.current = true
|
needsBumpMaxParents.current = true
|
||||||
}
|
}
|
||||||
}, [maxParents, skeleton.parents.length])
|
}, [maxParents, skeleton?.parents])
|
||||||
const bumpMaxParentsIfNeeded = React.useCallback(() => {
|
const bumpMaxParentsIfNeeded = React.useCallback(() => {
|
||||||
if (!isNative) {
|
if (!isNative) {
|
||||||
return
|
return
|
||||||
|
@ -296,6 +278,11 @@ function PostThreadLoaded({
|
||||||
const onMomentumScrollEnd = bumpMaxParentsIfNeeded
|
const onMomentumScrollEnd = bumpMaxParentsIfNeeded
|
||||||
const onScrollToTop = bumpMaxParentsIfNeeded
|
const onScrollToTop = bumpMaxParentsIfNeeded
|
||||||
|
|
||||||
|
const onEndReached = React.useCallback(() => {
|
||||||
|
if (isFetching || posts.length < maxReplies) return
|
||||||
|
setMaxReplies(prev => prev + 50)
|
||||||
|
}, [isFetching, maxReplies, posts.length])
|
||||||
|
|
||||||
const renderItem = React.useCallback(
|
const renderItem = React.useCallback(
|
||||||
({item, index}: {item: RowItem; index: number}) => {
|
({item, index}: {item: RowItem; index: number}) => {
|
||||||
if (item === TOP_COMPONENT) {
|
if (item === TOP_COMPONENT) {
|
||||||
|
@ -326,46 +313,6 @@ function PostThreadLoaded({
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
} else if (item === LOAD_MORE) {
|
|
||||||
return (
|
|
||||||
<Pressable
|
|
||||||
onPress={() => setMaxReplies(n => n + 50)}
|
|
||||||
style={[pal.border, pal.view, styles.itemContainer]}
|
|
||||||
accessibilityLabel={_(msg`Load more posts`)}
|
|
||||||
accessibilityHint="">
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pal.viewLight,
|
|
||||||
{paddingHorizontal: 18, paddingVertical: 14, borderRadius: 6},
|
|
||||||
]}>
|
|
||||||
<Text type="lg-medium" style={pal.text}>
|
|
||||||
<Trans>Load more posts</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</Pressable>
|
|
||||||
)
|
|
||||||
} else if (item === BOTTOM_COMPONENT) {
|
|
||||||
// HACK
|
|
||||||
// due to some complexities with how flatlist works, this is the easiest way
|
|
||||||
// I could find to get a border positioned directly under the last item
|
|
||||||
// -prf
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
// @ts-ignore web-only
|
|
||||||
style={{
|
|
||||||
// Leave enough space below that the scroll doesn't jump
|
|
||||||
height: isNative ? 600 : '100vh',
|
|
||||||
borderTopWidth: 1,
|
|
||||||
borderColor: pal.colors.border,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
} else if (item === CHILD_SPINNER) {
|
|
||||||
return (
|
|
||||||
<View style={[pal.border, styles.childSpinner]}>
|
|
||||||
<ActivityIndicator />
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
} else if (isThreadPost(item)) {
|
} else if (isThreadPost(item)) {
|
||||||
const prev = isThreadPost(posts[index - 1])
|
const prev = isThreadPost(posts[index - 1])
|
||||||
? (posts[index - 1] as ThreadPost)
|
? (posts[index - 1] as ThreadPost)
|
||||||
|
@ -374,7 +321,9 @@ function PostThreadLoaded({
|
||||||
? (posts[index - 1] as ThreadPost)
|
? (posts[index - 1] as ThreadPost)
|
||||||
: undefined
|
: undefined
|
||||||
const hasUnrevealedParents =
|
const hasUnrevealedParents =
|
||||||
index === 0 && maxParents < skeleton.parents.length
|
index === 0 &&
|
||||||
|
skeleton?.parents &&
|
||||||
|
maxParents < skeleton.parents.length
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
ref={item.ctx.isHighlightedPost ? highlightedPostRef : undefined}
|
ref={item.ctx.isHighlightedPost ? highlightedPostRef : undefined}
|
||||||
|
@ -391,9 +340,9 @@ function PostThreadLoaded({
|
||||||
showChildReplyLine={item.ctx.showChildReplyLine}
|
showChildReplyLine={item.ctx.showChildReplyLine}
|
||||||
showParentReplyLine={item.ctx.showParentReplyLine}
|
showParentReplyLine={item.ctx.showParentReplyLine}
|
||||||
hasPrecedingItem={
|
hasPrecedingItem={
|
||||||
!!prev?.ctx.showChildReplyLine || hasUnrevealedParents
|
!!prev?.ctx.showChildReplyLine || !!hasUnrevealedParents
|
||||||
}
|
}
|
||||||
onPostReply={onRefresh}
|
onPostReply={refetch}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -403,142 +352,62 @@ function PostThreadLoaded({
|
||||||
[
|
[
|
||||||
hasSession,
|
hasSession,
|
||||||
isTabletOrMobile,
|
isTabletOrMobile,
|
||||||
|
_,
|
||||||
isMobile,
|
isMobile,
|
||||||
onPressReply,
|
onPressReply,
|
||||||
pal.border,
|
pal.border,
|
||||||
pal.viewLight,
|
pal.viewLight,
|
||||||
pal.textLight,
|
pal.textLight,
|
||||||
pal.view,
|
|
||||||
pal.text,
|
|
||||||
pal.colors.border,
|
|
||||||
posts,
|
posts,
|
||||||
onRefresh,
|
skeleton?.parents,
|
||||||
|
maxParents,
|
||||||
deferParents,
|
deferParents,
|
||||||
treeView,
|
treeView,
|
||||||
skeleton.parents.length,
|
refetch,
|
||||||
maxParents,
|
|
||||||
_,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List
|
<>
|
||||||
ref={ref}
|
<ListMaybePlaceholder
|
||||||
data={posts}
|
isLoading={!preferences || !thread}
|
||||||
keyExtractor={item => item._reactKey}
|
isError={!!error}
|
||||||
renderItem={renderItem}
|
onRetry={refetch}
|
||||||
onContentSizeChange={isNative ? undefined : onContentSizeChangeWeb}
|
errorTitle={error?.title}
|
||||||
onStartReached={onStartReached}
|
errorMessage={error?.message}
|
||||||
onMomentumScrollEnd={onMomentumScrollEnd}
|
/>
|
||||||
onScrollToTop={onScrollToTop}
|
{!error && thread && (
|
||||||
maintainVisibleContentPosition={
|
<List
|
||||||
isNative ? MAINTAIN_VISIBLE_CONTENT_POSITION : undefined
|
ref={ref}
|
||||||
}
|
data={posts}
|
||||||
style={s.hContentRegion}
|
renderItem={renderItem}
|
||||||
// @ts-ignore our .web version only -prf
|
keyExtractor={keyExtractor}
|
||||||
desktopFixedHeight
|
onContentSizeChange={isNative ? undefined : onContentSizeChangeWeb}
|
||||||
removeClippedSubviews={isAndroid ? false : undefined}
|
onStartReached={onStartReached}
|
||||||
windowSize={11}
|
onEndReached={onEndReached}
|
||||||
/>
|
onEndReachedThreshold={2}
|
||||||
)
|
onMomentumScrollEnd={onMomentumScrollEnd}
|
||||||
}
|
onScrollToTop={onScrollToTop}
|
||||||
|
maintainVisibleContentPosition={
|
||||||
function PostThreadBlocked() {
|
isNative ? MAINTAIN_VISIBLE_CONTENT_POSITION : undefined
|
||||||
const {_} = useLingui()
|
}
|
||||||
const pal = usePalette('default')
|
// @ts-ignore our .web version only -prf
|
||||||
const navigation = useNavigation<NavigationProp>()
|
desktopFixedHeight
|
||||||
|
removeClippedSubviews={isAndroid ? false : undefined}
|
||||||
const onPressBack = React.useCallback(() => {
|
ListFooterComponent={
|
||||||
if (navigation.canGoBack()) {
|
<ListFooter
|
||||||
navigation.goBack()
|
isFetching={isFetching}
|
||||||
} else {
|
onRetry={refetch}
|
||||||
navigation.navigate('Home')
|
// 300 is based on the minimum height of a post. This is enough extra height for the `maintainVisPos` to
|
||||||
}
|
// work without causing weird jumps on web or glitches on native
|
||||||
}, [navigation])
|
height={windowHeight - 200}
|
||||||
|
|
||||||
return (
|
|
||||||
<CenteredView>
|
|
||||||
<View style={[pal.view, pal.border, styles.notFoundContainer]}>
|
|
||||||
<Text type="title-lg" style={[pal.text, s.mb5]}>
|
|
||||||
<Trans>Post hidden</Trans>
|
|
||||||
</Text>
|
|
||||||
<Text type="md" style={[pal.text, s.mb10]}>
|
|
||||||
<Trans>
|
|
||||||
You have blocked the author or you have been blocked by the author.
|
|
||||||
</Trans>
|
|
||||||
</Text>
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={onPressBack}
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel={_(msg`Back`)}
|
|
||||||
accessibilityHint="">
|
|
||||||
<Text type="2xl" style={pal.link}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon="angle-left"
|
|
||||||
style={[pal.link as FontAwesomeIconStyle, s.mr5]}
|
|
||||||
size={14}
|
|
||||||
/>
|
/>
|
||||||
<Trans context="action">Back</Trans>
|
}
|
||||||
</Text>
|
initialNumToRender={initialNumToRender}
|
||||||
</TouchableOpacity>
|
windowSize={11}
|
||||||
</View>
|
/>
|
||||||
</CenteredView>
|
)}
|
||||||
)
|
</>
|
||||||
}
|
|
||||||
|
|
||||||
function PostThreadError({
|
|
||||||
onRefresh,
|
|
||||||
notFound,
|
|
||||||
error,
|
|
||||||
}: {
|
|
||||||
onRefresh: () => void
|
|
||||||
notFound: boolean
|
|
||||||
error: Error | null
|
|
||||||
}) {
|
|
||||||
const {_} = useLingui()
|
|
||||||
const pal = usePalette('default')
|
|
||||||
const navigation = useNavigation<NavigationProp>()
|
|
||||||
|
|
||||||
const onPressBack = React.useCallback(() => {
|
|
||||||
if (navigation.canGoBack()) {
|
|
||||||
navigation.goBack()
|
|
||||||
} else {
|
|
||||||
navigation.navigate('Home')
|
|
||||||
}
|
|
||||||
}, [navigation])
|
|
||||||
|
|
||||||
if (notFound) {
|
|
||||||
return (
|
|
||||||
<CenteredView>
|
|
||||||
<View style={[pal.view, pal.border, styles.notFoundContainer]}>
|
|
||||||
<Text type="title-lg" style={[pal.text, s.mb5]}>
|
|
||||||
<Trans>Post not found</Trans>
|
|
||||||
</Text>
|
|
||||||
<Text type="md" style={[pal.text, s.mb10]}>
|
|
||||||
<Trans>The post may have been deleted.</Trans>
|
|
||||||
</Text>
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={onPressBack}
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel={_(msg`Back`)}
|
|
||||||
accessibilityHint="">
|
|
||||||
<Text type="2xl" style={pal.link}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon="angle-left"
|
|
||||||
style={[pal.link as FontAwesomeIconStyle, s.mr5]}
|
|
||||||
size={14}
|
|
||||||
/>
|
|
||||||
<Trans>Back</Trans>
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</CenteredView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<CenteredView>
|
|
||||||
<ErrorMessage message={cleanError(error)} onPressTryAgain={onRefresh} />
|
|
||||||
</CenteredView>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,7 +427,9 @@ function createThreadSkeleton(
|
||||||
node: ThreadNode,
|
node: ThreadNode,
|
||||||
hasSession: boolean,
|
hasSession: boolean,
|
||||||
treeView: boolean,
|
treeView: boolean,
|
||||||
): ThreadSkeletonParts {
|
): ThreadSkeletonParts | null {
|
||||||
|
if (!node) return null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
parents: Array.from(flattenThreadParents(node, hasSession)),
|
parents: Array.from(flattenThreadParents(node, hasSession)),
|
||||||
highlightedPost: node,
|
highlightedPost: node,
|
||||||
|
@ -615,7 +486,10 @@ function hasPwiOptOut(node: ThreadPost) {
|
||||||
return !!node.post.author.labels?.find(l => l.val === '!no-unauthenticated')
|
return !!node.post.author.labels?.find(l => l.val === '!no-unauthenticated')
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasBranchingReplies(node: ThreadNode) {
|
function hasBranchingReplies(node?: ThreadNode) {
|
||||||
|
if (!node) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if (node.type !== 'post') {
|
if (node.type !== 'post') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -629,20 +503,9 @@ function hasBranchingReplies(node: ThreadNode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
notFoundContainer: {
|
|
||||||
margin: 10,
|
|
||||||
paddingHorizontal: 18,
|
|
||||||
paddingVertical: 14,
|
|
||||||
borderRadius: 6,
|
|
||||||
},
|
|
||||||
itemContainer: {
|
itemContainer: {
|
||||||
borderTopWidth: 1,
|
borderTopWidth: 1,
|
||||||
paddingHorizontal: 18,
|
paddingHorizontal: 18,
|
||||||
paddingVertical: 18,
|
paddingVertical: 18,
|
||||||
},
|
},
|
||||||
childSpinner: {
|
|
||||||
borderTopWidth: 1,
|
|
||||||
paddingTop: 40,
|
|
||||||
paddingBottom: 200,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -205,11 +205,7 @@ let PostThreadItemLoaded = ({
|
||||||
uri: post.uri,
|
uri: post.uri,
|
||||||
cid: post.cid,
|
cid: post.cid,
|
||||||
text: record.text,
|
text: record.text,
|
||||||
author: {
|
author: post.author,
|
||||||
handle: post.author.handle,
|
|
||||||
displayName: post.author.displayName,
|
|
||||||
avatar: post.author.avatar,
|
|
||||||
},
|
|
||||||
embed: post.embed,
|
embed: post.embed,
|
||||||
moderation,
|
moderation,
|
||||||
},
|
},
|
||||||
|
@ -256,6 +252,7 @@ let PostThreadItemLoaded = ({
|
||||||
handle={post.author.handle}
|
handle={post.author.handle}
|
||||||
avatar={post.author.avatar}
|
avatar={post.author.avatar}
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
|
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.layoutContent}>
|
<View style={styles.layoutContent}>
|
||||||
|
@ -452,6 +449,7 @@ let PostThreadItemLoaded = ({
|
||||||
handle={post.author.handle}
|
handle={post.author.handle}
|
||||||
avatar={post.author.avatar}
|
avatar={post.author.avatar}
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
|
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{showChildReplyLine && (
|
{showChildReplyLine && (
|
||||||
|
@ -540,7 +538,7 @@ let PostThreadItemLoaded = ({
|
||||||
title={itemTitle}
|
title={itemTitle}
|
||||||
noFeedback>
|
noFeedback>
|
||||||
<Text type="sm-medium" style={pal.textLight}>
|
<Text type="sm-medium" style={pal.textLight}>
|
||||||
More
|
<Trans>More</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="angle-right"
|
icon="angle-right"
|
||||||
|
|
|
@ -118,11 +118,7 @@ function PostInner({
|
||||||
uri: post.uri,
|
uri: post.uri,
|
||||||
cid: post.cid,
|
cid: post.cid,
|
||||||
text: record.text,
|
text: record.text,
|
||||||
author: {
|
author: post.author,
|
||||||
handle: post.author.handle,
|
|
||||||
displayName: post.author.displayName,
|
|
||||||
avatar: post.author.avatar,
|
|
||||||
},
|
|
||||||
embed: post.embed,
|
embed: post.embed,
|
||||||
moderation,
|
moderation,
|
||||||
},
|
},
|
||||||
|
@ -144,6 +140,7 @@ function PostInner({
|
||||||
handle={post.author.handle}
|
handle={post.author.handle}
|
||||||
avatar={post.author.avatar}
|
avatar={post.author.avatar}
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
|
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.layoutContent}>
|
<View style={styles.layoutContent}>
|
||||||
|
|
|
@ -126,11 +126,7 @@ let FeedItemInner = ({
|
||||||
uri: post.uri,
|
uri: post.uri,
|
||||||
cid: post.cid,
|
cid: post.cid,
|
||||||
text: record.text || '',
|
text: record.text || '',
|
||||||
author: {
|
author: post.author,
|
||||||
handle: post.author.handle,
|
|
||||||
displayName: post.author.displayName,
|
|
||||||
avatar: post.author.avatar,
|
|
||||||
},
|
|
||||||
embed: post.embed,
|
embed: post.embed,
|
||||||
moderation,
|
moderation,
|
||||||
},
|
},
|
||||||
|
@ -243,6 +239,7 @@ let FeedItemInner = ({
|
||||||
handle={post.author.handle}
|
handle={post.author.handle}
|
||||||
avatar={post.author.avatar}
|
avatar={post.author.avatar}
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
|
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
{isThreadParent && (
|
{isThreadParent && (
|
||||||
<View
|
<View
|
||||||
|
|
|
@ -49,6 +49,7 @@ export function ProfileCard({
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const profile = useProfileShadow(profileUnshadowed)
|
const profile = useProfileShadow(profileUnshadowed)
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
|
const isLabeler = profile?.associated?.labeler
|
||||||
if (!moderationOpts) {
|
if (!moderationOpts) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -79,6 +80,7 @@ export function ProfileCard({
|
||||||
size={40}
|
size={40}
|
||||||
avatar={profile.avatar}
|
avatar={profile.avatar}
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
|
type={isLabeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.layoutContent}>
|
<View style={styles.layoutContent}>
|
||||||
|
@ -101,7 +103,7 @@ export function ProfileCard({
|
||||||
/>
|
/>
|
||||||
{!!profile.viewer?.followedBy && <View style={s.flexRow} />}
|
{!!profile.viewer?.followedBy && <View style={s.flexRow} />}
|
||||||
</View>
|
</View>
|
||||||
{renderButton ? (
|
{renderButton && !isLabeler ? (
|
||||||
<View style={styles.layoutButton}>{renderButton(profile)}</View>
|
<View style={styles.layoutButton}>{renderButton(profile)}</View>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</View>
|
</View>
|
||||||
|
@ -223,6 +225,7 @@ function FollowersList({
|
||||||
avatar={f.avatar}
|
avatar={f.avatar}
|
||||||
size={32}
|
size={32}
|
||||||
moderation={mod.ui('avatar')}
|
moderation={mod.ui('avatar')}
|
||||||
|
type={f.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -1,39 +1,66 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {ActivityIndicator, StyleSheet, View} from 'react-native'
|
|
||||||
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||||
import {CenteredView} from '../util/Views'
|
|
||||||
import {LoadingScreen} from '../util/LoadingScreen'
|
|
||||||
import {List} from '../util/List'
|
import {List} from '../util/List'
|
||||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
|
||||||
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||||
import {useProfileFollowersQuery} from '#/state/queries/profile-followers'
|
import {useProfileFollowersQuery} from '#/state/queries/profile-followers'
|
||||||
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
|
import {
|
||||||
|
ListFooter,
|
||||||
|
ListHeaderDesktop,
|
||||||
|
ListMaybePlaceholder,
|
||||||
|
} from '#/components/Lists'
|
||||||
|
import {msg} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useSession} from 'state/session'
|
||||||
|
import {View} from 'react-native'
|
||||||
|
|
||||||
|
function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) {
|
||||||
|
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyExtractor(item: ActorDefs.ProfileViewBasic) {
|
||||||
|
return item.did
|
||||||
|
}
|
||||||
|
|
||||||
export function ProfileFollowers({name}: {name: string}) {
|
export function ProfileFollowers({name}: {name: string}) {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const initialNumToRender = useInitialNumToRender()
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||||
const {
|
const {
|
||||||
data: resolvedDid,
|
data: resolvedDid,
|
||||||
|
isLoading: isDidLoading,
|
||||||
error: resolveError,
|
error: resolveError,
|
||||||
isFetching: isFetchingDid,
|
|
||||||
} = useResolveDidQuery(name)
|
} = useResolveDidQuery(name)
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
|
isLoading: isFollowersLoading,
|
||||||
isFetching,
|
isFetching,
|
||||||
isFetched,
|
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
hasNextPage,
|
hasNextPage,
|
||||||
fetchNextPage,
|
fetchNextPage,
|
||||||
isError,
|
|
||||||
error,
|
error,
|
||||||
refetch,
|
refetch,
|
||||||
} = useProfileFollowersQuery(resolvedDid)
|
} = useProfileFollowersQuery(resolvedDid)
|
||||||
|
|
||||||
|
const isError = React.useMemo(
|
||||||
|
() => !!resolveError || !!error,
|
||||||
|
[resolveError, error],
|
||||||
|
)
|
||||||
|
|
||||||
|
const isMe = React.useMemo(() => {
|
||||||
|
return resolvedDid === currentAccount?.did
|
||||||
|
}, [resolvedDid, currentAccount?.did])
|
||||||
|
|
||||||
const followers = React.useMemo(() => {
|
const followers = React.useMemo(() => {
|
||||||
if (data?.pages) {
|
if (data?.pages) {
|
||||||
return data.pages.flatMap(page => page.followers)
|
return data.pages.flatMap(page => page.followers)
|
||||||
}
|
}
|
||||||
|
return []
|
||||||
}, [data])
|
}, [data])
|
||||||
|
|
||||||
const onRefresh = React.useCallback(async () => {
|
const onRefresh = React.useCallback(async () => {
|
||||||
|
@ -47,7 +74,7 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||||
}, [refetch, setIsPTRing])
|
}, [refetch, setIsPTRing])
|
||||||
|
|
||||||
const onEndReached = async () => {
|
const onEndReached = async () => {
|
||||||
if (isFetching || !hasNextPage || isError) return
|
if (isFetching || !hasNextPage || !!error) return
|
||||||
try {
|
try {
|
||||||
await fetchNextPage()
|
await fetchNextPage()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -55,57 +82,38 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderItem = React.useCallback(
|
|
||||||
({item}: {item: ActorDefs.ProfileViewBasic}) => (
|
|
||||||
<ProfileCardWithFollowBtn key={item.did} profile={item} />
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isFetchingDid || !isFetched) {
|
|
||||||
return <LoadingScreen />
|
|
||||||
}
|
|
||||||
|
|
||||||
// error
|
|
||||||
// =
|
|
||||||
if (resolveError || isError) {
|
|
||||||
return (
|
|
||||||
<CenteredView>
|
|
||||||
<ErrorMessage
|
|
||||||
message={cleanError(resolveError || error)}
|
|
||||||
onPressTryAgain={onRefresh}
|
|
||||||
/>
|
|
||||||
</CenteredView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// loaded
|
|
||||||
// =
|
|
||||||
return (
|
return (
|
||||||
<List
|
<View style={{flex: 1}}>
|
||||||
data={followers}
|
<ListMaybePlaceholder
|
||||||
keyExtractor={item => item.did}
|
isLoading={isDidLoading || isFollowersLoading}
|
||||||
refreshing={isPTRing}
|
isEmpty={followers.length < 1}
|
||||||
onRefresh={onRefresh}
|
isError={isError}
|
||||||
onEndReached={onEndReached}
|
emptyType="results"
|
||||||
renderItem={renderItem}
|
emptyMessage={
|
||||||
initialNumToRender={15}
|
isMe
|
||||||
// FIXME(dan)
|
? _(msg`You do not have any followers.`)
|
||||||
// eslint-disable-next-line react/no-unstable-nested-components
|
: _(msg`This user doesn't have any followers.`)
|
||||||
ListFooterComponent={() => (
|
}
|
||||||
<View style={styles.footer}>
|
errorMessage={cleanError(resolveError || error)}
|
||||||
{(isFetching || isFetchingNextPage) && <ActivityIndicator />}
|
onRetry={isError ? refetch : undefined}
|
||||||
</View>
|
/>
|
||||||
|
{followers.length > 0 && (
|
||||||
|
<List
|
||||||
|
data={followers}
|
||||||
|
renderItem={renderItem}
|
||||||
|
keyExtractor={keyExtractor}
|
||||||
|
refreshing={isPTRing}
|
||||||
|
onRefresh={onRefresh}
|
||||||
|
onEndReached={onEndReached}
|
||||||
|
onEndReachedThreshold={4}
|
||||||
|
ListHeaderComponent={<ListHeaderDesktop title={_(msg`Followers`)} />}
|
||||||
|
ListFooterComponent={<ListFooter isFetching={isFetchingNextPage} />}
|
||||||
|
// @ts-ignore our .web version only -prf
|
||||||
|
desktopFixedHeight
|
||||||
|
initialNumToRender={initialNumToRender}
|
||||||
|
windowSize={11}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
// @ts-ignore our .web version only -prf
|
</View>
|
||||||
desktopFixedHeight
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
footer: {
|
|
||||||
height: 200,
|
|
||||||
paddingTop: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,39 +1,65 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {ActivityIndicator, StyleSheet, View} from 'react-native'
|
|
||||||
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||||
import {CenteredView} from '../util/Views'
|
|
||||||
import {LoadingScreen} from '../util/LoadingScreen'
|
|
||||||
import {List} from '../util/List'
|
import {List} from '../util/List'
|
||||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
|
||||||
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||||
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
||||||
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
|
import {
|
||||||
|
ListFooter,
|
||||||
|
ListHeaderDesktop,
|
||||||
|
ListMaybePlaceholder,
|
||||||
|
} from '#/components/Lists'
|
||||||
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
|
import {useSession} from 'state/session'
|
||||||
|
import {msg} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
|
function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) {
|
||||||
|
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyExtractor(item: ActorDefs.ProfileViewBasic) {
|
||||||
|
return item.did
|
||||||
|
}
|
||||||
|
|
||||||
export function ProfileFollows({name}: {name: string}) {
|
export function ProfileFollows({name}: {name: string}) {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const initialNumToRender = useInitialNumToRender()
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||||
const {
|
const {
|
||||||
data: resolvedDid,
|
data: resolvedDid,
|
||||||
|
isLoading: isDidLoading,
|
||||||
error: resolveError,
|
error: resolveError,
|
||||||
isFetching: isFetchingDid,
|
|
||||||
} = useResolveDidQuery(name)
|
} = useResolveDidQuery(name)
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
|
isLoading: isFollowsLoading,
|
||||||
isFetching,
|
isFetching,
|
||||||
isFetched,
|
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
hasNextPage,
|
hasNextPage,
|
||||||
fetchNextPage,
|
fetchNextPage,
|
||||||
isError,
|
|
||||||
error,
|
error,
|
||||||
refetch,
|
refetch,
|
||||||
} = useProfileFollowsQuery(resolvedDid)
|
} = useProfileFollowsQuery(resolvedDid)
|
||||||
|
|
||||||
|
const isError = React.useMemo(
|
||||||
|
() => !!resolveError || !!error,
|
||||||
|
[resolveError, error],
|
||||||
|
)
|
||||||
|
|
||||||
|
const isMe = React.useMemo(() => {
|
||||||
|
return resolvedDid === currentAccount?.did
|
||||||
|
}, [resolvedDid, currentAccount?.did])
|
||||||
|
|
||||||
const follows = React.useMemo(() => {
|
const follows = React.useMemo(() => {
|
||||||
if (data?.pages) {
|
if (data?.pages) {
|
||||||
return data.pages.flatMap(page => page.follows)
|
return data.pages.flatMap(page => page.follows)
|
||||||
}
|
}
|
||||||
|
return []
|
||||||
}, [data])
|
}, [data])
|
||||||
|
|
||||||
const onRefresh = React.useCallback(async () => {
|
const onRefresh = React.useCallback(async () => {
|
||||||
|
@ -47,7 +73,7 @@ export function ProfileFollows({name}: {name: string}) {
|
||||||
}, [refetch, setIsPTRing])
|
}, [refetch, setIsPTRing])
|
||||||
|
|
||||||
const onEndReached = async () => {
|
const onEndReached = async () => {
|
||||||
if (isFetching || !hasNextPage || isError) return
|
if (isFetching || !hasNextPage || !!error) return
|
||||||
try {
|
try {
|
||||||
await fetchNextPage()
|
await fetchNextPage()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -55,57 +81,38 @@ export function ProfileFollows({name}: {name: string}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderItem = React.useCallback(
|
|
||||||
({item}: {item: ActorDefs.ProfileViewBasic}) => (
|
|
||||||
<ProfileCardWithFollowBtn key={item.did} profile={item} />
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isFetchingDid || !isFetched) {
|
|
||||||
return <LoadingScreen />
|
|
||||||
}
|
|
||||||
|
|
||||||
// error
|
|
||||||
// =
|
|
||||||
if (resolveError || isError) {
|
|
||||||
return (
|
|
||||||
<CenteredView>
|
|
||||||
<ErrorMessage
|
|
||||||
message={cleanError(resolveError || error)}
|
|
||||||
onPressTryAgain={onRefresh}
|
|
||||||
/>
|
|
||||||
</CenteredView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// loaded
|
|
||||||
// =
|
|
||||||
return (
|
return (
|
||||||
<List
|
<>
|
||||||
data={follows}
|
<ListMaybePlaceholder
|
||||||
keyExtractor={item => item.did}
|
isLoading={isDidLoading || isFollowsLoading}
|
||||||
refreshing={isPTRing}
|
isEmpty={follows.length < 1}
|
||||||
onRefresh={onRefresh}
|
isError={isError}
|
||||||
onEndReached={onEndReached}
|
emptyType="results"
|
||||||
renderItem={renderItem}
|
emptyMessage={
|
||||||
initialNumToRender={15}
|
isMe
|
||||||
// FIXME(dan)
|
? _(msg`You are not following anyone.`)
|
||||||
// eslint-disable-next-line react/no-unstable-nested-components
|
: _(msg`This user isn't following anyone.`)
|
||||||
ListFooterComponent={() => (
|
}
|
||||||
<View style={styles.footer}>
|
errorMessage={cleanError(resolveError || error)}
|
||||||
{(isFetching || isFetchingNextPage) && <ActivityIndicator />}
|
onRetry={isError ? refetch : undefined}
|
||||||
</View>
|
/>
|
||||||
|
{follows.length > 0 && (
|
||||||
|
<List
|
||||||
|
data={follows}
|
||||||
|
renderItem={renderItem}
|
||||||
|
keyExtractor={keyExtractor}
|
||||||
|
refreshing={isPTRing}
|
||||||
|
onRefresh={onRefresh}
|
||||||
|
onEndReached={onEndReached}
|
||||||
|
onEndReachedThreshold={4}
|
||||||
|
ListHeaderComponent={<ListHeaderDesktop title={_(msg`Following`)} />}
|
||||||
|
ListFooterComponent={<ListFooter isFetching={isFetchingNextPage} />}
|
||||||
|
// @ts-ignore our .web version only -prf
|
||||||
|
desktopFixedHeight
|
||||||
|
initialNumToRender={initialNumToRender}
|
||||||
|
windowSize={11}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
// @ts-ignore our .web version only -prf
|
</>
|
||||||
desktopFixedHeight
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
footer: {
|
|
||||||
height: 200,
|
|
||||||
paddingTop: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ import {useModerationOpts} from '#/state/queries/preferences'
|
||||||
import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows'
|
import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows'
|
||||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||||
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
|
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
|
||||||
import {Trans} from '@lingui/macro'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {Trans, msg} from '@lingui/macro'
|
||||||
|
|
||||||
const OUTER_PADDING = 10
|
const OUTER_PADDING = 10
|
||||||
const INNER_PADDING = 14
|
const INNER_PADDING = 14
|
||||||
|
@ -98,9 +99,11 @@ export function ProfileHeaderSuggestedFollows({
|
||||||
<SuggestedFollowSkeleton />
|
<SuggestedFollowSkeleton />
|
||||||
</>
|
</>
|
||||||
) : data ? (
|
) : data ? (
|
||||||
data.suggestions.map(profile => (
|
data.suggestions
|
||||||
<SuggestedFollow key={profile.did} profile={profile} />
|
.filter(s => (s.associated?.labeler ? false : true))
|
||||||
))
|
.map(profile => (
|
||||||
|
<SuggestedFollow key={profile.did} profile={profile} />
|
||||||
|
))
|
||||||
) : (
|
) : (
|
||||||
<View />
|
<View />
|
||||||
)}
|
)}
|
||||||
|
@ -168,6 +171,7 @@ function SuggestedFollow({
|
||||||
}) {
|
}) {
|
||||||
const {track} = useAnalytics()
|
const {track} = useAnalytics()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
const {_} = useLingui()
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const profile = useProfileShadow(profileUnshadowed)
|
const profile = useProfileShadow(profileUnshadowed)
|
||||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
|
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
|
||||||
|
@ -181,20 +185,20 @@ function SuggestedFollow({
|
||||||
await queueFollow()
|
await queueFollow()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e?.name !== 'AbortError') {
|
if (e?.name !== 'AbortError') {
|
||||||
Toast.show('An issue occurred, please try again.')
|
Toast.show(_(msg`An issue occurred, please try again.`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [queueFollow, track])
|
}, [queueFollow, track, _])
|
||||||
|
|
||||||
const onPressUnfollow = React.useCallback(async () => {
|
const onPressUnfollow = React.useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
await queueUnfollow()
|
await queueUnfollow()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e?.name !== 'AbortError') {
|
if (e?.name !== 'AbortError') {
|
||||||
Toast.show('An issue occurred, please try again.')
|
Toast.show(_(msg`An issue occurred, please try again.`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [queueUnfollow])
|
}, [queueUnfollow, _])
|
||||||
|
|
||||||
if (!moderationOpts) {
|
if (!moderationOpts) {
|
||||||
return null
|
return null
|
||||||
|
@ -239,7 +243,7 @@ function SuggestedFollow({
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={following ? 'Unfollow' : 'Follow'}
|
label={following ? _(msg`Unfollow`) : _(msg`Follow`)}
|
||||||
type="inverted"
|
type="inverted"
|
||||||
labelStyle={{textAlign: 'center'}}
|
labelStyle={{textAlign: 'center'}}
|
||||||
onPress={following ? onPressUnfollow : onPressFollow}
|
onPress={following ? onPressUnfollow : onPressFollow}
|
||||||
|
|
|
@ -11,16 +11,11 @@ import {sanitizeHandle} from 'lib/strings/handles'
|
||||||
import {isAndroid, isWeb} from 'platform/detection'
|
import {isAndroid, isWeb} from 'platform/detection'
|
||||||
import {TimeElapsed} from './TimeElapsed'
|
import {TimeElapsed} from './TimeElapsed'
|
||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {ModerationDecision, ModerationUI} from '@atproto/api'
|
import {AppBskyActorDefs, ModerationDecision, ModerationUI} from '@atproto/api'
|
||||||
import {usePrefetchProfileQuery} from '#/state/queries/profile'
|
import {usePrefetchProfileQuery} from '#/state/queries/profile'
|
||||||
|
|
||||||
interface PostMetaOpts {
|
interface PostMetaOpts {
|
||||||
author: {
|
author: AppBskyActorDefs.ProfileViewBasic
|
||||||
avatar?: string
|
|
||||||
did: string
|
|
||||||
handle: string
|
|
||||||
displayName?: string | undefined
|
|
||||||
}
|
|
||||||
moderation: ModerationDecision | undefined
|
moderation: ModerationDecision | undefined
|
||||||
authorHasWarning: boolean
|
authorHasWarning: boolean
|
||||||
postHref: string
|
postHref: string
|
||||||
|
@ -47,6 +42,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
|
||||||
avatar={opts.author.avatar}
|
avatar={opts.author.avatar}
|
||||||
size={opts.avatarSize || 16}
|
size={opts.avatarSize || 16}
|
||||||
moderation={opts.avatarModeration}
|
moderation={opts.avatarModeration}
|
||||||
|
type={opts.author.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -97,7 +97,7 @@ export function LanguageSettingsScreen(_props: Props) {
|
||||||
<Text style={[pal.text, s.pb10]}>
|
<Text style={[pal.text, s.pb10]}>
|
||||||
<Trans>
|
<Trans>
|
||||||
Select your app language for the default text to display in the
|
Select your app language for the default text to display in the
|
||||||
app
|
app.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
@ -296,7 +296,7 @@ export function LanguageSettingsScreen(_props: Props) {
|
||||||
type="button"
|
type="button"
|
||||||
style={[pal.text, {flexShrink: 1, overflow: 'hidden'}]}
|
style={[pal.text, {flexShrink: 1, overflow: 'hidden'}]}
|
||||||
numberOfLines={1}>
|
numberOfLines={1}>
|
||||||
{myLanguages.length ? myLanguages : 'Select languages'}
|
{myLanguages.length ? myLanguages : _(msg`Select languages`)}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -51,7 +51,13 @@ export const NotFoundScreen = () => {
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
label={canGoBack ? 'Go back' : 'Go home'}
|
label={canGoBack ? _(msg`Go Back`) : _(msg`Go Home`)}
|
||||||
|
accessibilityLabel={canGoBack ? _(msg`Go back`) : _(msg`Go home`)}
|
||||||
|
accessibilityHint={
|
||||||
|
canGoBack
|
||||||
|
? _(msg`Returns to previous page`)
|
||||||
|
: _(msg`Returns to home page`)
|
||||||
|
}
|
||||||
onPress={onPressHome}
|
onPress={onPressHome}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -59,11 +59,7 @@ export function PostThreadScreen({route}: Props) {
|
||||||
uri: thread.post.uri,
|
uri: thread.post.uri,
|
||||||
cid: thread.post.cid,
|
cid: thread.post.cid,
|
||||||
text: thread.record.text,
|
text: thread.record.text,
|
||||||
author: {
|
author: thread.post.author,
|
||||||
handle: thread.post.author.handle,
|
|
||||||
displayName: thread.post.author.displayName,
|
|
||||||
avatar: thread.post.author.avatar,
|
|
||||||
},
|
|
||||||
embed: thread.post.embed,
|
embed: thread.post.embed,
|
||||||
},
|
},
|
||||||
onPost: () =>
|
onPost: () =>
|
||||||
|
|
|
@ -108,8 +108,8 @@ export function ProfileFeedScreen(props: Props) {
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={{flexDirection: 'row'}}>
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
accessibilityLabel={_(msg`Go Back`)}
|
accessibilityLabel={_(msg`Go back`)}
|
||||||
accessibilityHint="Return to previous page"
|
accessibilityHint={_(msg`Returns to previous page`)}
|
||||||
onPress={onPressBack}
|
onPress={onPressBack}
|
||||||
style={{flexShrink: 1}}>
|
style={{flexShrink: 1}}>
|
||||||
<Text type="button" style={pal.text}>
|
<Text type="button" style={pal.text}>
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const ProfileFollowersScreen = ({route}: Props) => {
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View style={{flex: 1}}>
|
||||||
<ViewHeader title={_(msg`Followers`)} />
|
<ViewHeader title={_(msg`Followers`)} />
|
||||||
<ProfileFollowersComponent name={name} />
|
<ProfileFollowersComponent name={name} />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const ProfileFollowsScreen = ({route}: Props) => {
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View style={{flex: 1}}>
|
||||||
<ViewHeader title={_(msg`Following`)} />
|
<ViewHeader title={_(msg`Following`)} />
|
||||||
<ProfileFollowsComponent name={name} />
|
<ProfileFollowsComponent name={name} />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -913,7 +913,7 @@ function ErrorScreen({error}: {error: string}) {
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={{flexDirection: 'row'}}>
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
accessibilityLabel={_(msg`Go Back`)}
|
accessibilityLabel={_(msg`Go back`)}
|
||||||
accessibilityHint={_(msg`Return to previous page`)}
|
accessibilityHint={_(msg`Return to previous page`)}
|
||||||
onPress={onPressBack}
|
onPress={onPressBack}
|
||||||
style={{flexShrink: 1}}>
|
style={{flexShrink: 1}}>
|
||||||
|
|
|
@ -141,6 +141,7 @@ function SearchScreenSuggestedFollows() {
|
||||||
friends.slice(0, 4).map(friend =>
|
friends.slice(0, 4).map(friend =>
|
||||||
getSuggestedFollowsByActor(friend.did).then(foafsRes => {
|
getSuggestedFollowsByActor(friend.did).then(foafsRes => {
|
||||||
for (const user of foafsRes.suggestions) {
|
for (const user of foafsRes.suggestions) {
|
||||||
|
if (user.associated?.labeler) continue
|
||||||
friendsOfFriends.set(user.did, user)
|
friendsOfFriends.set(user.did, user)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
@ -772,7 +773,7 @@ export function SearchScreen(
|
||||||
{searchHistory.length > 0 && (
|
{searchHistory.length > 0 && (
|
||||||
<View style={styles.searchHistoryContent}>
|
<View style={styles.searchHistoryContent}>
|
||||||
<Text style={[pal.text, styles.searchHistoryTitle]}>
|
<Text style={[pal.text, styles.searchHistoryTitle]}>
|
||||||
Recent Searches
|
<Trans>Recent Searches</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
{searchHistory.map((historyItem, index) => (
|
{searchHistory.map((historyItem, index) => (
|
||||||
<View key={index} style={styles.historyItemContainer}>
|
<View key={index} style={styles.historyItemContainer}>
|
||||||
|
|
|
@ -78,8 +78,9 @@ export function ExportCarDialog({
|
||||||
<InlineLink
|
<InlineLink
|
||||||
to="https://docs.bsky.app/blog/repo-export"
|
to="https://docs.bsky.app/blog/repo-export"
|
||||||
style={[a.text_sm]}>
|
style={[a.text_sm]}>
|
||||||
this blogpost.
|
this blogpost
|
||||||
</InlineLink>
|
</InlineLink>
|
||||||
|
.
|
||||||
</Trans>
|
</Trans>
|
||||||
</P>
|
</P>
|
||||||
|
|
||||||
|
|
|
@ -40,10 +40,7 @@ import {
|
||||||
} from '#/state/preferences'
|
} from '#/state/preferences'
|
||||||
import {useSession, useSessionApi, SessionAccount} from '#/state/session'
|
import {useSession, useSessionApi, SessionAccount} from '#/state/session'
|
||||||
import {useProfileQuery} from '#/state/queries/profile'
|
import {useProfileQuery} from '#/state/queries/profile'
|
||||||
import {
|
import {useClearPreferencesMutation} from '#/state/queries/preferences'
|
||||||
useClearPreferencesMutation,
|
|
||||||
usePreferencesQuery,
|
|
||||||
} from '#/state/queries/preferences'
|
|
||||||
// TODO import {useInviteCodesQuery} from '#/state/queries/invites'
|
// TODO import {useInviteCodesQuery} from '#/state/queries/invites'
|
||||||
import {clear as clearStorage} from '#/state/persisted/store'
|
import {clear as clearStorage} from '#/state/persisted/store'
|
||||||
import {clearLegacyStorage} from '#/state/persisted/legacy'
|
import {clearLegacyStorage} from '#/state/persisted/legacy'
|
||||||
|
@ -85,7 +82,11 @@ function SettingsAccountCard({account}: {account: SessionAccount}) {
|
||||||
const contents = (
|
const contents = (
|
||||||
<View style={[pal.view, styles.linkCard]}>
|
<View style={[pal.view, styles.linkCard]}>
|
||||||
<View style={styles.avi}>
|
<View style={styles.avi}>
|
||||||
<UserAvatar size={40} avatar={profile?.avatar} />
|
<UserAvatar
|
||||||
|
size={40}
|
||||||
|
avatar={profile?.avatar}
|
||||||
|
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={[s.flex1]}>
|
<View style={[s.flex1]}>
|
||||||
<Text type="md-bold" style={pal.text}>
|
<Text type="md-bold" style={pal.text}>
|
||||||
|
@ -156,7 +157,6 @@ export function SettingsScreen({}: Props) {
|
||||||
const {screen, track} = useAnalytics()
|
const {screen, track} = useAnalytics()
|
||||||
const {openModal} = useModalControls()
|
const {openModal} = useModalControls()
|
||||||
const {isSwitchingAccounts, accounts, currentAccount} = useSession()
|
const {isSwitchingAccounts, accounts, currentAccount} = useSession()
|
||||||
const {data: preferences} = usePreferencesQuery()
|
|
||||||
const {mutate: clearPreferences} = useClearPreferencesMutation()
|
const {mutate: clearPreferences} = useClearPreferencesMutation()
|
||||||
// TODO
|
// TODO
|
||||||
// const {data: invites} = useInviteCodesQuery()
|
// const {data: invites} = useInviteCodesQuery()
|
||||||
|
@ -295,10 +295,7 @@ export function SettingsScreen({}: Props) {
|
||||||
return (
|
return (
|
||||||
<View style={s.hContentRegion} testID="settingsScreen">
|
<View style={s.hContentRegion} testID="settingsScreen">
|
||||||
<ExportCarDialog control={exportCarControl} />
|
<ExportCarDialog control={exportCarControl} />
|
||||||
<BirthDateSettingsDialog
|
<BirthDateSettingsDialog control={birthdayControl} />
|
||||||
control={birthdayControl}
|
|
||||||
preferences={preferences}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SimpleViewHeader
|
<SimpleViewHeader
|
||||||
showBackButton={isMobile}
|
showBackButton={isMobile}
|
||||||
|
@ -490,20 +487,20 @@ export function SettingsScreen({}: Props) {
|
||||||
label={_(msg`System`)}
|
label={_(msg`System`)}
|
||||||
left
|
left
|
||||||
onSelect={() => setColorMode('system')}
|
onSelect={() => setColorMode('system')}
|
||||||
accessibilityHint={_(msg`Set color theme to system setting`)}
|
accessibilityHint={_(msg`Sets color theme to system setting`)}
|
||||||
/>
|
/>
|
||||||
<SelectableBtn
|
<SelectableBtn
|
||||||
selected={colorMode === 'light'}
|
selected={colorMode === 'light'}
|
||||||
label={_(msg`Light`)}
|
label={_(msg`Light`)}
|
||||||
onSelect={() => setColorMode('light')}
|
onSelect={() => setColorMode('light')}
|
||||||
accessibilityHint={_(msg`Set color theme to light`)}
|
accessibilityHint={_(msg`Sets color theme to light`)}
|
||||||
/>
|
/>
|
||||||
<SelectableBtn
|
<SelectableBtn
|
||||||
selected={colorMode === 'dark'}
|
selected={colorMode === 'dark'}
|
||||||
label={_(msg`Dark`)}
|
label={_(msg`Dark`)}
|
||||||
right
|
right
|
||||||
onSelect={() => setColorMode('dark')}
|
onSelect={() => setColorMode('dark')}
|
||||||
accessibilityHint={_(msg`Set color theme to dark`)}
|
accessibilityHint={_(msg`Sets color theme to dark`)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -522,14 +519,14 @@ export function SettingsScreen({}: Props) {
|
||||||
label={_(msg`Dim`)}
|
label={_(msg`Dim`)}
|
||||||
left
|
left
|
||||||
onSelect={() => setDarkTheme('dim')}
|
onSelect={() => setDarkTheme('dim')}
|
||||||
accessibilityHint={_(msg`Set dark theme to the dim theme`)}
|
accessibilityHint={_(msg`Sets dark theme to the dim theme`)}
|
||||||
/>
|
/>
|
||||||
<SelectableBtn
|
<SelectableBtn
|
||||||
selected={darkTheme === 'dark'}
|
selected={darkTheme === 'dark'}
|
||||||
label={_(msg`Dark`)}
|
label={_(msg`Dark`)}
|
||||||
right
|
right
|
||||||
onSelect={() => setDarkTheme('dark')}
|
onSelect={() => setDarkTheme('dark')}
|
||||||
accessibilityHint={_(msg`Set dark theme to the dark theme`)}
|
accessibilityHint={_(msg`Sets dark theme to the dark theme`)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -549,8 +546,8 @@ export function SettingsScreen({}: Props) {
|
||||||
]}
|
]}
|
||||||
onPress={openFollowingFeedPreferences}
|
onPress={openFollowingFeedPreferences}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityHint=""
|
accessibilityLabel={_(msg`Following feed preferences`)}
|
||||||
accessibilityLabel={_(msg`Opens the home feed preferences`)}>
|
accessibilityHint={_(msg`Opens the Following feed preferences`)}>
|
||||||
<View style={[styles.iconContainer, pal.btn]}>
|
<View style={[styles.iconContainer, pal.btn]}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="sliders"
|
icon="sliders"
|
||||||
|
@ -570,8 +567,8 @@ export function SettingsScreen({}: Props) {
|
||||||
]}
|
]}
|
||||||
onPress={openThreadsPreferences}
|
onPress={openThreadsPreferences}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityHint=""
|
accessibilityLabel={_(msg`Thread preferences`)}
|
||||||
accessibilityLabel={_(msg`Opens the threads preferences`)}>
|
accessibilityHint={_(msg`Opens the threads preferences`)}>
|
||||||
<View style={[styles.iconContainer, pal.btn]}>
|
<View style={[styles.iconContainer, pal.btn]}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={['far', 'comments']}
|
icon={['far', 'comments']}
|
||||||
|
@ -590,9 +587,10 @@ export function SettingsScreen({}: Props) {
|
||||||
pal.view,
|
pal.view,
|
||||||
isSwitchingAccounts && styles.dimmed,
|
isSwitchingAccounts && styles.dimmed,
|
||||||
]}
|
]}
|
||||||
accessibilityHint="My Saved Feeds"
|
onPress={onPressSavedFeeds}
|
||||||
accessibilityLabel={_(msg`Opens screen with all saved feeds`)}
|
accessibilityRole="button"
|
||||||
onPress={onPressSavedFeeds}>
|
accessibilityLabel={_(msg`My saved feeds`)}
|
||||||
|
accessibilityHint={_(msg`Opens screen with all saved feeds`)}>
|
||||||
<View style={[styles.iconContainer, pal.btn]}>
|
<View style={[styles.iconContainer, pal.btn]}>
|
||||||
<HashtagIcon style={pal.text} size={18} strokeWidth={3} />
|
<HashtagIcon style={pal.text} size={18} strokeWidth={3} />
|
||||||
</View>
|
</View>
|
||||||
|
@ -691,7 +689,7 @@ export function SettingsScreen({}: Props) {
|
||||||
onPress={onPressAppPasswords}
|
onPress={onPressAppPasswords}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`App password settings`)}
|
accessibilityLabel={_(msg`App password settings`)}
|
||||||
accessibilityHint={_(msg`Opens the app password settings page`)}>
|
accessibilityHint={_(msg`Opens the app password settings`)}>
|
||||||
<View style={[styles.iconContainer, pal.btn]}>
|
<View style={[styles.iconContainer, pal.btn]}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="lock"
|
icon="lock"
|
||||||
|
@ -712,7 +710,9 @@ export function SettingsScreen({}: Props) {
|
||||||
onPress={isSwitchingAccounts ? undefined : onPressChangeHandle}
|
onPress={isSwitchingAccounts ? undefined : onPressChangeHandle}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Change handle`)}
|
accessibilityLabel={_(msg`Change handle`)}
|
||||||
accessibilityHint={_(msg`Choose a new Bluesky username or create`)}>
|
accessibilityHint={_(
|
||||||
|
msg`Opens modal for choosing or creating a new Bluesky username`,
|
||||||
|
)}>
|
||||||
<View style={[styles.iconContainer, pal.btn]}>
|
<View style={[styles.iconContainer, pal.btn]}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="at"
|
icon="at"
|
||||||
|
@ -748,7 +748,9 @@ export function SettingsScreen({}: Props) {
|
||||||
onPress={() => openModal({name: 'change-password'})}
|
onPress={() => openModal({name: 'change-password'})}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Change password`)}
|
accessibilityLabel={_(msg`Change password`)}
|
||||||
accessibilityHint={_(msg`Change your Bluesky password`)}>
|
accessibilityHint={_(
|
||||||
|
msg`Opens modal for changing your Bluesky password`,
|
||||||
|
)}>
|
||||||
<View style={[styles.iconContainer, pal.btn]}>
|
<View style={[styles.iconContainer, pal.btn]}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="lock"
|
icon="lock"
|
||||||
|
@ -770,7 +772,7 @@ export function SettingsScreen({}: Props) {
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Export my data`)}
|
accessibilityLabel={_(msg`Export my data`)}
|
||||||
accessibilityHint={_(
|
accessibilityHint={_(
|
||||||
msg`Download Bluesky account data (repository)`,
|
msg`Opens modal for downloading Bluesky account data (repository)`,
|
||||||
)}>
|
)}>
|
||||||
<View style={[styles.iconContainer, pal.btn]}>
|
<View style={[styles.iconContainer, pal.btn]}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
|
@ -789,7 +791,7 @@ export function SettingsScreen({}: Props) {
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Delete account`)}
|
accessibilityLabel={_(msg`Delete account`)}
|
||||||
accessibilityHint={_(
|
accessibilityHint={_(
|
||||||
msg`Opens modal for account deletion confirmation. Requires email code.`,
|
msg`Opens modal for account deletion confirmation. Requires email code`,
|
||||||
)}>
|
)}>
|
||||||
<View style={[styles.iconContainer, dangerBg]}>
|
<View style={[styles.iconContainer, dangerBg]}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
|
@ -807,8 +809,8 @@ export function SettingsScreen({}: Props) {
|
||||||
style={[pal.view, styles.linkCardNoIcon]}
|
style={[pal.view, styles.linkCardNoIcon]}
|
||||||
onPress={onPressSystemLog}
|
onPress={onPressSystemLog}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityHint="Open system log"
|
accessibilityLabel={_(msg`Open system log`)}
|
||||||
accessibilityLabel={_(msg`Opens the system log page`)}>
|
accessibilityHint={_(msg`Opens the system log page`)}>
|
||||||
<Text type="lg" style={pal.text}>
|
<Text type="lg" style={pal.text}>
|
||||||
<Trans>System log</Trans>
|
<Trans>System log</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -839,7 +841,7 @@ export function SettingsScreen({}: Props) {
|
||||||
style={[pal.view, styles.linkCardNoIcon]}
|
style={[pal.view, styles.linkCardNoIcon]}
|
||||||
onPress={onPressResetPreferences}
|
onPress={onPressResetPreferences}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Reset preferences`)}
|
accessibilityLabel={_(msg`Reset preferences state`)}
|
||||||
accessibilityHint={_(msg`Resets the preferences state`)}>
|
accessibilityHint={_(msg`Resets the preferences state`)}>
|
||||||
<Text type="lg" style={pal.text}>
|
<Text type="lg" style={pal.text}>
|
||||||
<Trans>Reset preferences state</Trans>
|
<Trans>Reset preferences state</Trans>
|
||||||
|
@ -849,7 +851,7 @@ export function SettingsScreen({}: Props) {
|
||||||
style={[pal.view, styles.linkCardNoIcon]}
|
style={[pal.view, styles.linkCardNoIcon]}
|
||||||
onPress={onPressResetOnboarding}
|
onPress={onPressResetOnboarding}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Reset onboarding`)}
|
accessibilityLabel={_(msg`Reset onboarding state`)}
|
||||||
accessibilityHint={_(msg`Resets the onboarding state`)}>
|
accessibilityHint={_(msg`Resets the onboarding state`)}>
|
||||||
<Text type="lg" style={pal.text}>
|
<Text type="lg" style={pal.text}>
|
||||||
<Trans>Reset onboarding state</Trans>
|
<Trans>Reset onboarding state</Trans>
|
||||||
|
@ -860,7 +862,7 @@ export function SettingsScreen({}: Props) {
|
||||||
onPress={clearAllLegacyStorage}
|
onPress={clearAllLegacyStorage}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Clear all legacy storage data`)}
|
accessibilityLabel={_(msg`Clear all legacy storage data`)}
|
||||||
accessibilityHint={_(msg`Clear all legacy storage data`)}>
|
accessibilityHint={_(msg`Clears all legacy storage data`)}>
|
||||||
<Text type="lg" style={pal.text}>
|
<Text type="lg" style={pal.text}>
|
||||||
<Trans>
|
<Trans>
|
||||||
Clear all legacy storage data (restart after this)
|
Clear all legacy storage data (restart after this)
|
||||||
|
@ -872,7 +874,7 @@ export function SettingsScreen({}: Props) {
|
||||||
onPress={clearAllStorage}
|
onPress={clearAllStorage}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Clear all storage data`)}
|
accessibilityLabel={_(msg`Clear all storage data`)}
|
||||||
accessibilityHint={_(msg`Clear all storage data`)}>
|
accessibilityHint={_(msg`Clears all storage data`)}>
|
||||||
<Text type="lg" style={pal.text}>
|
<Text type="lg" style={pal.text}>
|
||||||
<Trans>Clear all storage data (restart after this)</Trans>
|
<Trans>Clear all storage data (restart after this)</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -961,7 +963,7 @@ function EmailConfirmationNotice() {
|
||||||
]}
|
]}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Verify my email`)}
|
accessibilityLabel={_(msg`Verify my email`)}
|
||||||
accessibilityHint=""
|
accessibilityHint={_(msg`Opens modal for email verification`)}
|
||||||
onPress={() => openModal({name: 'verify-email'})}>
|
onPress={() => openModal({name: 'verify-email'})}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="envelope"
|
icon="envelope"
|
||||||
|
|
|
@ -75,6 +75,7 @@ let DrawerProfileCard = ({
|
||||||
avatar={profile?.avatar}
|
avatar={profile?.avatar}
|
||||||
// See https://github.com/bluesky-social/social-app/pull/1801:
|
// See https://github.com/bluesky-social/social-app/pull/1801:
|
||||||
usePlainRNImage={true}
|
usePlainRNImage={true}
|
||||||
|
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
<Text
|
<Text
|
||||||
type="title-lg"
|
type="title-lg"
|
||||||
|
@ -93,10 +94,12 @@ let DrawerProfileCard = ({
|
||||||
{formatCountShortOnly(profile?.followersCount ?? 0)}
|
{formatCountShortOnly(profile?.followersCount ?? 0)}
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
{pluralize(profile?.followersCount || 0, 'follower')} ·{' '}
|
{pluralize(profile?.followersCount || 0, 'follower')} ·{' '}
|
||||||
<Text type="xl-medium" style={pal.text}>
|
<Trans>
|
||||||
{formatCountShortOnly(profile?.followsCount ?? 0)}
|
<Text type="xl-medium" style={pal.text}>
|
||||||
</Text>{' '}
|
{formatCountShortOnly(profile?.followsCount ?? 0)}
|
||||||
following
|
</Text>{' '}
|
||||||
|
following
|
||||||
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)
|
)
|
||||||
|
|
|
@ -58,7 +58,7 @@ let NavSignupCard = ({}: {}): React.ReactNode => {
|
||||||
accessibilityHint={_(msg`Sign in`)}
|
accessibilityHint={_(msg`Sign in`)}
|
||||||
accessibilityLabel={_(msg`Sign in`)}>
|
accessibilityLabel={_(msg`Sign in`)}>
|
||||||
<Text type="md" style={[pal.text, s.bold]}>
|
<Text type="md" style={[pal.text, s.bold]}>
|
||||||
Sign in
|
<Trans>Sign in</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -229,6 +229,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
|
||||||
size={27}
|
size={27}
|
||||||
// See https://github.com/bluesky-social/social-app/pull/1801:
|
// See https://github.com/bluesky-social/social-app/pull/1801:
|
||||||
usePlainRNImage={true}
|
usePlainRNImage={true}
|
||||||
|
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
|
@ -238,6 +239,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
|
||||||
size={28}
|
size={28}
|
||||||
// See https://github.com/bluesky-social/social-app/pull/1801:
|
// See https://github.com/bluesky-social/social-app/pull/1801:
|
||||||
usePlainRNImage={true}
|
usePlainRNImage={true}
|
||||||
|
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -64,7 +64,11 @@ function ProfileCard() {
|
||||||
style={[styles.profileCard, !isDesktop && styles.profileCardTablet]}
|
style={[styles.profileCard, !isDesktop && styles.profileCardTablet]}
|
||||||
title={_(msg`My Profile`)}
|
title={_(msg`My Profile`)}
|
||||||
asAnchor>
|
asAnchor>
|
||||||
<UserAvatar avatar={profile.avatar} size={size} />
|
<UserAvatar
|
||||||
|
avatar={profile.avatar}
|
||||||
|
size={size}
|
||||||
|
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<View style={[styles.profileCard, !isDesktop && styles.profileCardTablet]}>
|
<View style={[styles.profileCard, !isDesktop && styles.profileCardTablet]}>
|
||||||
|
|
|
@ -112,6 +112,7 @@ export function SearchProfileCard({
|
||||||
size={40}
|
size={40}
|
||||||
avatar={profile.avatar}
|
avatar={profile.avatar}
|
||||||
moderation={moderation.ui('avatar')}
|
moderation={moderation.ui('avatar')}
|
||||||
|
type={profile.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
<View style={{flex: 1}}>
|
<View style={{flex: 1}}>
|
||||||
<Text
|
<Text
|
||||||
|
|
|
@ -34,10 +34,10 @@
|
||||||
jsonpointer "^5.0.0"
|
jsonpointer "^5.0.0"
|
||||||
leven "^3.1.0"
|
leven "^3.1.0"
|
||||||
|
|
||||||
"@atproto/api@^0.12.0":
|
"@atproto/api@^0.12.1":
|
||||||
version "0.12.0"
|
version "0.12.1"
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.0.tgz#69e52f8761dc7d76c675fa7284bd49240bb0df64"
|
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.1.tgz#3340cbbd6a51a8c2f3248dae55a01415ab71084e"
|
||||||
integrity sha512-nSWiad1Z6IC/oVFSVxD5gZLhkD+J4EW2CFqAqIhklJNc0cjFKdmf8D56Pac6Ktm1sJoM6TVZ8GEeuEG6bJS/aQ==
|
integrity sha512-Grigs9neuQxytXr2yHq/IfNlgXQVptWDO9KTQr5FDmgMY4Zly2X7Sa99u9c1CW9auwUTbcd+yRFBNEtbA3n3qg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@atproto/common-web" "^0.3.0"
|
"@atproto/common-web" "^0.3.0"
|
||||||
"@atproto/lexicon" "^0.4.0"
|
"@atproto/lexicon" "^0.4.0"
|
||||||
|
|
Loading…
Reference in New Issue