Merge remote-tracking branch 'upstream/main' into invite-code-warning
commit
b164f151cc
|
@ -4,7 +4,7 @@ import {openApp, loginAsAlice, createServer} from '../util'
|
|||
|
||||
describe('Home screen', () => {
|
||||
beforeAll(async () => {
|
||||
await createServer('?users&follows&posts')
|
||||
await createServer('?users&follows&posts&feeds')
|
||||
await openApp({permissions: {notifications: 'YES'}})
|
||||
})
|
||||
|
||||
|
@ -13,6 +13,23 @@ describe('Home screen', () => {
|
|||
await element(by.id('homeScreenFeedTabs-Following')).tap()
|
||||
})
|
||||
|
||||
it('Can go to feeds page using feeds button in tab bar', async () => {
|
||||
await element(by.id('homeScreenFeedTabs-Feeds ✨')).tap()
|
||||
await expect(element(by.text('Discover new feeds'))).toBeVisible()
|
||||
})
|
||||
|
||||
it('Feeds button disappears after pinning a feed', async () => {
|
||||
await element(by.id('bottomBarProfileBtn')).tap()
|
||||
await element(by.id('profilePager-selector')).swipe('left')
|
||||
await element(by.id('profilePager-selector-4')).tap()
|
||||
await element(by.id('feed-alice-favs')).tap()
|
||||
await element(by.id('pinBtn')).tap()
|
||||
await element(by.id('bottomBarHomeBtn')).tap()
|
||||
await expect(
|
||||
element(by.id('homeScreenFeedTabs-Feeds ✨')),
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
it('Can like posts', async () => {
|
||||
const carlaPosts = by.id('feedItem-by-carla.test')
|
||||
await expect(
|
||||
|
@ -65,14 +82,14 @@ describe('Home screen', () => {
|
|||
|
||||
it('Can swipe between feeds', async () => {
|
||||
await element(by.id('homeScreen')).swipe('left', 'fast', 0.75)
|
||||
await expect(element(by.id('whatshotFeedPage'))).toBeVisible()
|
||||
await expect(element(by.id('customFeedPage'))).toBeVisible()
|
||||
await element(by.id('homeScreen')).swipe('right', 'fast', 0.75)
|
||||
await expect(element(by.id('followingFeedPage'))).toBeVisible()
|
||||
})
|
||||
|
||||
it('Can tap between feeds', async () => {
|
||||
await element(by.id("homeScreenFeedTabs-What's hot")).tap()
|
||||
await expect(element(by.id('whatshotFeedPage'))).toBeVisible()
|
||||
await element(by.id('homeScreenFeedTabs-alice-favs')).tap()
|
||||
await expect(element(by.id('customFeedPage'))).toBeVisible()
|
||||
await element(by.id('homeScreenFeedTabs-Following')).tap()
|
||||
await expect(element(by.id('followingFeedPage'))).toBeVisible()
|
||||
})
|
||||
|
|
|
@ -1,12 +1,41 @@
|
|||
const pkg = require('./package.json')
|
||||
|
||||
module.exports = function () {
|
||||
const hasSentryToken = !!process.env.SENTRY_AUTH_TOKEN
|
||||
/**
|
||||
* App version number. Should be incremented as part of a release cycle.
|
||||
*/
|
||||
const VERSION = pkg.version
|
||||
|
||||
/**
|
||||
* iOS build number. Must be incremented for each TestFlight version.
|
||||
*/
|
||||
const IOS_BUILD_NUMBER = '5'
|
||||
|
||||
/**
|
||||
* Android build number. Must be incremented for each release.
|
||||
*/
|
||||
const ANDROID_VERSION_CODE = 46
|
||||
|
||||
/**
|
||||
* Uses built-in Expo env vars
|
||||
*
|
||||
* @see https://docs.expo.dev/build-reference/variables/#built-in-environment-variables
|
||||
*/
|
||||
const PLATFORM = process.env.EAS_BUILD_PLATFORM
|
||||
|
||||
/**
|
||||
* Additional granularity for the `dist` field
|
||||
*/
|
||||
const DIST_BUILD_NUMBER =
|
||||
PLATFORM === 'android' ? ANDROID_VERSION_CODE : IOS_BUILD_NUMBER
|
||||
|
||||
return {
|
||||
expo: {
|
||||
version: VERSION,
|
||||
name: 'Bluesky',
|
||||
slug: 'bluesky',
|
||||
scheme: 'bluesky',
|
||||
owner: 'blueskysocial',
|
||||
version: '1.57.0',
|
||||
runtimeVersion: {
|
||||
policy: 'appVersion',
|
||||
},
|
||||
|
@ -19,7 +48,7 @@ module.exports = function () {
|
|||
backgroundColor: '#ffffff',
|
||||
},
|
||||
ios: {
|
||||
buildNumber: '4',
|
||||
buildNumber: IOS_BUILD_NUMBER,
|
||||
supportsTablet: false,
|
||||
bundleIdentifier: 'xyz.blueskyweb.app',
|
||||
config: {
|
||||
|
@ -43,7 +72,7 @@ module.exports = function () {
|
|||
backgroundColor: '#ffffff',
|
||||
},
|
||||
android: {
|
||||
versionCode: 46,
|
||||
versionCode: ANDROID_VERSION_CODE,
|
||||
adaptiveIcon: {
|
||||
foregroundImage: './assets/adaptive-icon.png',
|
||||
backgroundColor: '#ffffff',
|
||||
|
@ -74,7 +103,7 @@ module.exports = function () {
|
|||
},
|
||||
plugins: [
|
||||
'expo-localization',
|
||||
hasSentryToken && 'sentry-expo',
|
||||
Boolean(process.env.SENTRY_AUTH_TOKEN) && 'sentry-expo',
|
||||
[
|
||||
'expo-build-properties',
|
||||
{
|
||||
|
@ -100,11 +129,16 @@ module.exports = function () {
|
|||
},
|
||||
hooks: {
|
||||
postPublish: [
|
||||
/*
|
||||
* @see https://docs.expo.dev/guides/using-sentry/#app-configuration
|
||||
*/
|
||||
{
|
||||
file: 'sentry-expo/upload-sourcemaps',
|
||||
config: {
|
||||
organization: 'blueskyweb',
|
||||
project: 'react-native',
|
||||
release: VERSION,
|
||||
dist: `${PLATFORM}.${VERSION}.${DIST_BUILD_NUMBER}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
25
eas.json
25
eas.json
|
@ -11,28 +11,19 @@
|
|||
"extends": "base",
|
||||
"developmentClient": true,
|
||||
"distribution": "internal",
|
||||
"channel": "development",
|
||||
"ios": {
|
||||
"simulator": true,
|
||||
"resourceClass": "large"
|
||||
},
|
||||
"channel": "development"
|
||||
},
|
||||
"development-device": {
|
||||
"extends": "base",
|
||||
"developmentClient": true,
|
||||
"distribution": "internal",
|
||||
"ios": {
|
||||
"resourceClass": "large"
|
||||
},
|
||||
"channel": "development"
|
||||
}
|
||||
},
|
||||
"preview": {
|
||||
"extends": "base",
|
||||
"distribution": "internal",
|
||||
"channel": "preview",
|
||||
"ios": {
|
||||
"resourceClass": "large"
|
||||
},
|
||||
"channel": "preview"
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"extends": "base",
|
||||
|
@ -40,14 +31,6 @@
|
|||
"resourceClass": "large"
|
||||
},
|
||||
"channel": "production"
|
||||
},
|
||||
"dev-android-apk": {
|
||||
"extends": "base",
|
||||
"developmentClient": true,
|
||||
"android": {
|
||||
"buildType": "apk",
|
||||
"gradleCommand": ":app:assembleRelease"
|
||||
}
|
||||
}
|
||||
},
|
||||
"submit": {
|
||||
|
|
6
index.js
6
index.js
|
@ -3,10 +3,12 @@ import 'react-native-gesture-handler' // must be first
|
|||
import {LogBox} from 'react-native'
|
||||
LogBox.ignoreLogs(['Require cycle:']) // suppress require-cycle warnings, it's fine
|
||||
|
||||
import 'platform/polyfills'
|
||||
import '#/platform/polyfills'
|
||||
import {registerRootComponent} from 'expo'
|
||||
import {doPolyfill} from '#/lib/api/api-polyfill'
|
||||
doPolyfill()
|
||||
|
||||
import App from './src/App'
|
||||
import App from '#/App'
|
||||
|
||||
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
||||
// It also ensures that whether you load the app in Expo Go or in a native build,
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import 'platform/polyfills'
|
||||
import '#/platform/polyfills'
|
||||
import {registerRootComponent} from 'expo'
|
||||
import App from './src/App'
|
||||
import {doPolyfill} from '#/lib/api/api-polyfill'
|
||||
import App from '#/App'
|
||||
|
||||
doPolyfill()
|
||||
registerRootComponent(App)
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
"perf:test:measure": "NODE_ENV=test flashlight test --bundleId xyz.blueskyweb.app --testCommand 'yarn perf:test' --duration 150000 --resultsFilePath .perf/results.json",
|
||||
"perf:test:results": "NODE_ENV=test flashlight report .perf/results.json",
|
||||
"perf:measure": "NODE_ENV=test flashlight measure",
|
||||
"build:apk": "eas build -p android --profile dev-android-apk",
|
||||
"intl:extract": "lingui extract",
|
||||
"intl:compile": "lingui compile"
|
||||
},
|
||||
|
|
|
@ -16,14 +16,7 @@ export type FeedTunerFn = (
|
|||
export class FeedViewPostsSlice {
|
||||
isFlattenedReply = false
|
||||
|
||||
constructor(public items: FeedViewPost[] = []) {}
|
||||
|
||||
get _reactKey() {
|
||||
const rootItem = this.isFlattenedReply ? this.items[1] : this.items[0]
|
||||
return `slice-${rootItem.post.uri}-${
|
||||
rootItem.reason?.indexedAt || rootItem.post.indexedAt
|
||||
}`
|
||||
}
|
||||
constructor(public items: FeedViewPost[], public _reactKey: string) {}
|
||||
|
||||
get uri() {
|
||||
if (this.isFlattenedReply) {
|
||||
|
@ -118,28 +111,34 @@ export class FeedViewPostsSlice {
|
|||
}
|
||||
|
||||
export class NoopFeedTuner {
|
||||
reset() {}
|
||||
private keyCounter = 0
|
||||
|
||||
reset() {
|
||||
this.keyCounter = 0
|
||||
}
|
||||
tune(
|
||||
feed: FeedViewPost[],
|
||||
_tunerFns: FeedTunerFn[] = [],
|
||||
_opts?: {dryRun: boolean; maintainOrder: boolean},
|
||||
): FeedViewPostsSlice[] {
|
||||
return feed.map(item => new FeedViewPostsSlice([item]))
|
||||
return feed.map(
|
||||
item => new FeedViewPostsSlice([item], `slice-${this.keyCounter++}`),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class FeedTuner {
|
||||
private keyCounter = 0
|
||||
seenUris: Set<string> = new Set()
|
||||
|
||||
constructor() {}
|
||||
constructor(public tunerFns: FeedTunerFn[]) {}
|
||||
|
||||
reset() {
|
||||
this.keyCounter = 0
|
||||
this.seenUris.clear()
|
||||
}
|
||||
|
||||
tune(
|
||||
feed: FeedViewPost[],
|
||||
tunerFns: FeedTunerFn[] = [],
|
||||
{dryRun, maintainOrder}: {dryRun: boolean; maintainOrder: boolean} = {
|
||||
dryRun: false,
|
||||
maintainOrder: false,
|
||||
|
@ -148,7 +147,9 @@ export class FeedTuner {
|
|||
let slices: FeedViewPostsSlice[] = []
|
||||
|
||||
if (maintainOrder) {
|
||||
slices = feed.map(item => new FeedViewPostsSlice([item]))
|
||||
slices = feed.map(
|
||||
item => new FeedViewPostsSlice([item], `slice-${this.keyCounter++}`),
|
||||
)
|
||||
} else {
|
||||
// arrange the posts into thread slices
|
||||
for (let i = feed.length - 1; i >= 0; i--) {
|
||||
|
@ -164,12 +165,14 @@ export class FeedTuner {
|
|||
continue
|
||||
}
|
||||
}
|
||||
slices.unshift(new FeedViewPostsSlice([item]))
|
||||
slices.unshift(
|
||||
new FeedViewPostsSlice([item], `slice-${this.keyCounter++}`),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// run the custom tuners
|
||||
for (const tunerFn of tunerFns) {
|
||||
for (const tunerFn of this.tunerFns) {
|
||||
slices = tunerFn(this, slices.slice())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
import {
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedGetAuthorFeed as GetAuthorFeed,
|
||||
BskyAgent,
|
||||
} from '@atproto/api'
|
||||
import {FeedAPI, FeedAPIResponse} from './types'
|
||||
import {getAgent} from '#/state/session'
|
||||
|
||||
export class AuthorFeedAPI implements FeedAPI {
|
||||
constructor(
|
||||
public agent: BskyAgent,
|
||||
public params: GetAuthorFeed.QueryParams,
|
||||
) {}
|
||||
constructor(public params: GetAuthorFeed.QueryParams) {}
|
||||
|
||||
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
|
||||
const res = await this.agent.getAuthorFeed({
|
||||
const res = await getAgent().getAuthorFeed({
|
||||
...this.params,
|
||||
limit: 1,
|
||||
})
|
||||
|
@ -26,7 +23,7 @@ export class AuthorFeedAPI implements FeedAPI {
|
|||
cursor: string | undefined
|
||||
limit: number
|
||||
}): Promise<FeedAPIResponse> {
|
||||
const res = await this.agent.getAuthorFeed({
|
||||
const res = await getAgent().getAuthorFeed({
|
||||
...this.params,
|
||||
cursor,
|
||||
limit,
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
import {
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedGetFeed as GetCustomFeed,
|
||||
BskyAgent,
|
||||
} from '@atproto/api'
|
||||
import {FeedAPI, FeedAPIResponse} from './types'
|
||||
import {getAgent} from '#/state/session'
|
||||
|
||||
export class CustomFeedAPI implements FeedAPI {
|
||||
constructor(
|
||||
public agent: BskyAgent,
|
||||
public params: GetCustomFeed.QueryParams,
|
||||
) {}
|
||||
constructor(public params: GetCustomFeed.QueryParams) {}
|
||||
|
||||
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
|
||||
const res = await this.agent.app.bsky.feed.getFeed({
|
||||
const res = await getAgent().app.bsky.feed.getFeed({
|
||||
...this.params,
|
||||
limit: 1,
|
||||
})
|
||||
|
@ -26,7 +23,7 @@ export class CustomFeedAPI implements FeedAPI {
|
|||
cursor: string | undefined
|
||||
limit: number
|
||||
}): Promise<FeedAPIResponse> {
|
||||
const res = await this.agent.app.bsky.feed.getFeed({
|
||||
const res = await getAgent().app.bsky.feed.getFeed({
|
||||
...this.params,
|
||||
cursor,
|
||||
limit,
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import {AppBskyFeedDefs, BskyAgent} from '@atproto/api'
|
||||
import {AppBskyFeedDefs} from '@atproto/api'
|
||||
import {FeedAPI, FeedAPIResponse} from './types'
|
||||
import {getAgent} from '#/state/session'
|
||||
|
||||
export class FollowingFeedAPI implements FeedAPI {
|
||||
constructor(public agent: BskyAgent) {}
|
||||
constructor() {}
|
||||
|
||||
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
|
||||
const res = await this.agent.getTimeline({
|
||||
const res = await getAgent().getTimeline({
|
||||
limit: 1,
|
||||
})
|
||||
return res.data.feed[0]
|
||||
|
@ -18,7 +19,7 @@ export class FollowingFeedAPI implements FeedAPI {
|
|||
cursor: string | undefined
|
||||
limit: number
|
||||
}): Promise<FeedAPIResponse> {
|
||||
const res = await this.agent.getTimeline({
|
||||
const res = await getAgent().getTimeline({
|
||||
cursor,
|
||||
limit,
|
||||
})
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
import {
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedGetActorLikes as GetActorLikes,
|
||||
BskyAgent,
|
||||
} from '@atproto/api'
|
||||
import {FeedAPI, FeedAPIResponse} from './types'
|
||||
import {getAgent} from '#/state/session'
|
||||
|
||||
export class LikesFeedAPI implements FeedAPI {
|
||||
constructor(
|
||||
public agent: BskyAgent,
|
||||
public params: GetActorLikes.QueryParams,
|
||||
) {}
|
||||
constructor(public params: GetActorLikes.QueryParams) {}
|
||||
|
||||
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
|
||||
const res = await this.agent.getActorLikes({
|
||||
const res = await getAgent().getActorLikes({
|
||||
...this.params,
|
||||
limit: 1,
|
||||
})
|
||||
|
@ -26,7 +23,7 @@ export class LikesFeedAPI implements FeedAPI {
|
|||
cursor: string | undefined
|
||||
limit: number
|
||||
}): Promise<FeedAPIResponse> {
|
||||
const res = await this.agent.getActorLikes({
|
||||
const res = await getAgent().getActorLikes({
|
||||
...this.params,
|
||||
cursor,
|
||||
limit,
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
import {
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedGetListFeed as GetListFeed,
|
||||
BskyAgent,
|
||||
} from '@atproto/api'
|
||||
import {FeedAPI, FeedAPIResponse} from './types'
|
||||
import {getAgent} from '#/state/session'
|
||||
|
||||
export class ListFeedAPI implements FeedAPI {
|
||||
constructor(
|
||||
public agent: BskyAgent,
|
||||
public params: GetListFeed.QueryParams,
|
||||
) {}
|
||||
constructor(public params: GetListFeed.QueryParams) {}
|
||||
|
||||
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
|
||||
const res = await this.agent.app.bsky.feed.getListFeed({
|
||||
const res = await getAgent().app.bsky.feed.getListFeed({
|
||||
...this.params,
|
||||
limit: 1,
|
||||
})
|
||||
|
@ -26,7 +23,7 @@ export class ListFeedAPI implements FeedAPI {
|
|||
cursor: string | undefined
|
||||
limit: number
|
||||
}): Promise<FeedAPIResponse> {
|
||||
const res = await this.agent.app.bsky.feed.getListFeed({
|
||||
const res = await getAgent().app.bsky.feed.getListFeed({
|
||||
...this.params,
|
||||
cursor,
|
||||
limit,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {AppBskyFeedDefs, AppBskyFeedGetTimeline, BskyAgent} from '@atproto/api'
|
||||
import {AppBskyFeedDefs, AppBskyFeedGetTimeline} from '@atproto/api'
|
||||
import shuffle from 'lodash.shuffle'
|
||||
import {timeout} from 'lib/async/timeout'
|
||||
import {bundleAsync} from 'lib/async/bundle'
|
||||
|
@ -7,6 +7,7 @@ import {FeedTuner} from '../feed-manip'
|
|||
import {FeedAPI, FeedAPIResponse, ReasonFeedSource} from './types'
|
||||
import {FeedParams} from '#/state/queries/post-feed'
|
||||
import {FeedTunerFn} from '../feed-manip'
|
||||
import {getAgent} from '#/state/session'
|
||||
|
||||
const REQUEST_WAIT_MS = 500 // 500ms
|
||||
const POST_AGE_CUTOFF = 60e3 * 60 * 24 // 24hours
|
||||
|
@ -18,16 +19,12 @@ export class MergeFeedAPI implements FeedAPI {
|
|||
itemCursor = 0
|
||||
sampleCursor = 0
|
||||
|
||||
constructor(
|
||||
public agent: BskyAgent,
|
||||
public params: FeedParams,
|
||||
public feedTuners: FeedTunerFn[],
|
||||
) {
|
||||
this.following = new MergeFeedSource_Following(this.agent, this.feedTuners)
|
||||
constructor(public params: FeedParams, public feedTuners: FeedTunerFn[]) {
|
||||
this.following = new MergeFeedSource_Following(this.feedTuners)
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.following = new MergeFeedSource_Following(this.agent, this.feedTuners)
|
||||
this.following = new MergeFeedSource_Following(this.feedTuners)
|
||||
this.customFeeds = [] // just empty the array, they will be captured in _fetchNext()
|
||||
this.feedCursor = 0
|
||||
this.itemCursor = 0
|
||||
|
@ -35,8 +32,7 @@ export class MergeFeedAPI implements FeedAPI {
|
|||
if (this.params.mergeFeedEnabled && this.params.mergeFeedSources) {
|
||||
this.customFeeds = shuffle(
|
||||
this.params.mergeFeedSources.map(
|
||||
feedUri =>
|
||||
new MergeFeedSource_Custom(this.agent, feedUri, this.feedTuners),
|
||||
feedUri => new MergeFeedSource_Custom(feedUri, this.feedTuners),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
|
@ -45,7 +41,7 @@ export class MergeFeedAPI implements FeedAPI {
|
|||
}
|
||||
|
||||
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
|
||||
const res = await this.agent.getTimeline({
|
||||
const res = await getAgent().getTimeline({
|
||||
limit: 1,
|
||||
})
|
||||
return res.data.feed[0]
|
||||
|
@ -137,7 +133,7 @@ class MergeFeedSource {
|
|||
queue: AppBskyFeedDefs.FeedViewPost[] = []
|
||||
hasMore = true
|
||||
|
||||
constructor(public agent: BskyAgent, public feedTuners: FeedTunerFn[]) {}
|
||||
constructor(public feedTuners: FeedTunerFn[]) {}
|
||||
|
||||
get numReady() {
|
||||
return this.queue.length
|
||||
|
@ -184,7 +180,7 @@ class MergeFeedSource {
|
|||
}
|
||||
|
||||
class MergeFeedSource_Following extends MergeFeedSource {
|
||||
tuner = new FeedTuner()
|
||||
tuner = new FeedTuner(this.feedTuners)
|
||||
|
||||
reset() {
|
||||
super.reset()
|
||||
|
@ -199,9 +195,9 @@ class MergeFeedSource_Following extends MergeFeedSource {
|
|||
cursor: string | undefined,
|
||||
limit: number,
|
||||
): Promise<AppBskyFeedGetTimeline.Response> {
|
||||
const res = await this.agent.getTimeline({cursor, limit})
|
||||
const res = await getAgent().getTimeline({cursor, limit})
|
||||
// run the tuner pre-emptively to ensure better mixing
|
||||
const slices = this.tuner.tune(res.data.feed, this.feedTuners, {
|
||||
const slices = this.tuner.tune(res.data.feed, {
|
||||
dryRun: false,
|
||||
maintainOrder: true,
|
||||
})
|
||||
|
@ -213,20 +209,16 @@ class MergeFeedSource_Following extends MergeFeedSource {
|
|||
class MergeFeedSource_Custom extends MergeFeedSource {
|
||||
minDate: Date
|
||||
|
||||
constructor(
|
||||
public agent: BskyAgent,
|
||||
public feedUri: string,
|
||||
public feedTuners: FeedTunerFn[],
|
||||
) {
|
||||
super(agent, feedTuners)
|
||||
constructor(public feedUri: string, public feedTuners: FeedTunerFn[]) {
|
||||
super(feedTuners)
|
||||
this.sourceInfo = {
|
||||
$type: 'reasonFeedSource',
|
||||
displayName: feedUri.split('/').pop() || '',
|
||||
uri: feedUriToHref(feedUri),
|
||||
}
|
||||
this.minDate = new Date(Date.now() - POST_AGE_CUTOFF)
|
||||
this.agent.app.bsky.feed
|
||||
.getFeedGenerator({
|
||||
getAgent()
|
||||
.app.bsky.feed.getFeedGenerator({
|
||||
feed: feedUri,
|
||||
})
|
||||
.then(
|
||||
|
@ -244,7 +236,7 @@ class MergeFeedSource_Custom extends MergeFeedSource {
|
|||
limit: number,
|
||||
): Promise<AppBskyFeedGetTimeline.Response> {
|
||||
try {
|
||||
const res = await this.agent.app.bsky.feed.getFeed({
|
||||
const res = await getAgent().app.bsky.feed.getFeed({
|
||||
cursor,
|
||||
limit,
|
||||
feed: this.feedUri,
|
||||
|
|
|
@ -116,8 +116,8 @@ export async function DEFAULT_FEEDS(
|
|||
} else {
|
||||
// production
|
||||
return {
|
||||
pinned: [PROD_DEFAULT_FEED('whats-hot')],
|
||||
saved: [PROD_DEFAULT_FEED('whats-hot')],
|
||||
pinned: [],
|
||||
saved: [],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,22 +7,35 @@ import {useAnalytics} from '#/lib/analytics/analytics'
|
|||
import {useSessionApi, SessionAccount} from '#/state/session'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {useCloseAllActiveElements} from '#/state/util'
|
||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||
|
||||
export function useAccountSwitcher() {
|
||||
const {track} = useAnalytics()
|
||||
const {selectAccount, clearCurrentAccount} = useSessionApi()
|
||||
const closeAllActiveElements = useCloseAllActiveElements()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||
|
||||
const onPressSwitchAccount = useCallback(
|
||||
async (acct: SessionAccount) => {
|
||||
async (account: SessionAccount) => {
|
||||
track('Settings:SwitchAccountButtonClicked')
|
||||
|
||||
try {
|
||||
closeAllActiveElements()
|
||||
navigation.navigate(isWeb ? 'Home' : 'HomeTab')
|
||||
await selectAccount(acct)
|
||||
Toast.show(`Signed in as ${acct.handle}`)
|
||||
if (account.accessJwt) {
|
||||
closeAllActiveElements()
|
||||
navigation.navigate(isWeb ? 'Home' : 'HomeTab')
|
||||
await selectAccount(account)
|
||||
setTimeout(() => {
|
||||
Toast.show(`Signed in as @${account.handle}`)
|
||||
}, 100)
|
||||
} else {
|
||||
closeAllActiveElements()
|
||||
setShowLoggedOut(true)
|
||||
Toast.show(
|
||||
`Please sign in as @${account.handle}`,
|
||||
'circle-exclamation',
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
Toast.show('Sorry! We need you to enter your password.')
|
||||
clearCurrentAccount() // back user out to login
|
||||
|
@ -34,6 +47,7 @@ export function useAccountSwitcher() {
|
|||
selectAccount,
|
||||
closeAllActiveElements,
|
||||
navigation,
|
||||
setShowLoggedOut,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ export function init(queryClient: QueryClient) {
|
|||
)
|
||||
if (event.request.trigger.type === 'push') {
|
||||
// refresh notifications in the background
|
||||
queryClient.invalidateQueries({queryKey: RQKEY_NOTIFS()})
|
||||
queryClient.resetQueries({queryKey: RQKEY_NOTIFS()})
|
||||
// handle payload-based deeplinks
|
||||
let payload
|
||||
if (isIOS) {
|
||||
|
@ -121,7 +121,7 @@ export function init(queryClient: QueryClient) {
|
|||
logger.DebugContext.notifications,
|
||||
)
|
||||
track('Notificatons:OpenApp')
|
||||
queryClient.invalidateQueries({queryKey: RQKEY_NOTIFS()})
|
||||
queryClient.resetQueries({queryKey: RQKEY_NOTIFS()})
|
||||
resetToTab('NotificationsTab') // open notifications tab
|
||||
}
|
||||
},
|
||||
|
|
|
@ -8,6 +8,15 @@ export const queryClient = new QueryClient({
|
|||
// so we NEVER want to enable this
|
||||
// -prf
|
||||
refetchOnWindowFocus: false,
|
||||
// Structural sharing between responses makes it impossible to rely on
|
||||
// "first seen" timestamps on objects to determine if they're fresh.
|
||||
// Disable this optimization so that we can rely on "first seen" timestamps.
|
||||
structuralSharing: false,
|
||||
// We don't want to retry queries by default, because in most cases we
|
||||
// want to fail early and show a response to the user. There are
|
||||
// exceptions, and those can be made on a per-query basis. For others, we
|
||||
// should give users controls to retry.
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -1,8 +1,46 @@
|
|||
/**
|
||||
* Importing these separately from `platform/detection` and `lib/app-info` to
|
||||
* avoid future conflicts and/or circular deps
|
||||
*/
|
||||
|
||||
import {Platform} from 'react-native'
|
||||
import app from 'react-native-version-number'
|
||||
import * as info from 'expo-updates'
|
||||
import {init} from 'sentry-expo'
|
||||
|
||||
/**
|
||||
* Matches the build profile `channel` props in `eas.json`
|
||||
*/
|
||||
const buildChannel = (info.channel || 'development') as
|
||||
| 'development'
|
||||
| 'preview'
|
||||
| 'production'
|
||||
|
||||
/**
|
||||
* Examples:
|
||||
* - `dev`
|
||||
* - `1.57.0`
|
||||
*/
|
||||
const release = app.appVersion ?? 'dev'
|
||||
|
||||
/**
|
||||
* Examples:
|
||||
* - `web.dev`
|
||||
* - `ios.dev`
|
||||
* - `android.dev`
|
||||
* - `web.1.57.0`
|
||||
* - `ios.1.57.0.3`
|
||||
* - `android.1.57.0.46`
|
||||
*/
|
||||
const dist = `${Platform.OS}.${release}${
|
||||
app.buildVersion ? `.${app.buildVersion}` : ''
|
||||
}`
|
||||
|
||||
init({
|
||||
dsn: 'https://05bc3789bf994b81bd7ce20c86ccd3ae@o4505071687041024.ingest.sentry.io/4505071690514432',
|
||||
enableInExpoDevelopment: false, // if true, Sentry will try to send events/errors in development mode.
|
||||
debug: false, // If `true`, Sentry will try to print out useful debugging information if something goes wrong with sending the event. Set it to `false` in production
|
||||
environment: __DEV__ ? 'development' : 'production', // Set the environment
|
||||
enableInExpoDevelopment: true,
|
||||
environment: buildChannel,
|
||||
dist,
|
||||
release,
|
||||
})
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -21,12 +21,6 @@ msgstr ""
|
|||
#~ msgid ". This warning is only available for posts with media attached."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:410
|
||||
#: src/view/shell/desktop/RightNav.tsx:158
|
||||
#: src/view/shell/Drawer.tsx:527
|
||||
msgid "{0, plural, one {# invite code available} other {# invite codes available}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/Repost.tsx:44
|
||||
msgid "{0}"
|
||||
msgstr ""
|
||||
|
@ -35,11 +29,6 @@ msgstr ""
|
|||
msgid "{0} {purposeLabel} List"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:141
|
||||
#: src/view/shell/Drawer.tsx:504
|
||||
msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:86
|
||||
msgid "{message}"
|
||||
msgstr ""
|
||||
|
@ -61,12 +50,12 @@ msgid "A new version of the app is available. Please update to continue using th
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/EditImage.tsx:299
|
||||
#: src/view/screens/Settings.tsx:422
|
||||
#: src/view/screens/Settings.tsx:410
|
||||
msgid "Accessibility"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/LoginForm.tsx:161
|
||||
#: src/view/screens/Settings.tsx:289
|
||||
#: src/view/screens/Settings.tsx:288
|
||||
msgid "Account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -88,8 +77,8 @@ msgstr ""
|
|||
msgid "Add a user to this list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:358
|
||||
#: src/view/screens/Settings.tsx:367
|
||||
#: src/view/screens/Settings.tsx:357
|
||||
#: src/view/screens/Settings.tsx:366
|
||||
msgid "Add account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -140,7 +129,7 @@ msgstr ""
|
|||
msgid "Adult Content"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:574
|
||||
#: src/view/screens/Settings.tsx:562
|
||||
msgid "Advanced"
|
||||
msgstr ""
|
||||
|
||||
|
@ -172,7 +161,7 @@ msgstr ""
|
|||
msgid "App Language"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:594
|
||||
#: src/view/screens/Settings.tsx:582
|
||||
msgid "App passwords"
|
||||
msgstr ""
|
||||
|
||||
|
@ -180,7 +169,7 @@ msgstr ""
|
|||
msgid "App Passwords"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:437
|
||||
#: src/view/screens/Settings.tsx:425
|
||||
msgid "Appearance"
|
||||
msgstr ""
|
||||
|
||||
|
@ -205,7 +194,7 @@ msgid "Artistic or non-erotic nudity."
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/create/CreateAccount.tsx:145
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:122
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:151
|
||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:166
|
||||
#: src/view/com/auth/login/LoginForm.tsx:251
|
||||
#: src/view/com/auth/login/SetNewPasswordForm.tsx:148
|
||||
|
@ -217,7 +206,7 @@ msgstr ""
|
|||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:466
|
||||
#: src/view/screens/Settings.tsx:454
|
||||
msgid "Basics"
|
||||
msgstr ""
|
||||
|
||||
|
@ -226,7 +215,7 @@ msgstr ""
|
|||
msgid "Birthday"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:315
|
||||
#: src/view/screens/Settings.tsx:314
|
||||
msgid "Birthday:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -291,7 +280,7 @@ msgstr ""
|
|||
msgid "Bluesky.Social"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:723
|
||||
#: src/view/screens/Settings.tsx:711
|
||||
msgid "Build version {0} {1}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -359,12 +348,12 @@ msgstr ""
|
|||
msgid "Cancel waitlist signup"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:309
|
||||
#: src/view/screens/Settings.tsx:308
|
||||
msgid "Change"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:606
|
||||
#: src/view/screens/Settings.tsx:615
|
||||
#: src/view/screens/Settings.tsx:594
|
||||
#: src/view/screens/Settings.tsx:603
|
||||
msgid "Change handle"
|
||||
msgstr ""
|
||||
|
||||
|
@ -408,19 +397,19 @@ msgstr ""
|
|||
msgid "Choose your password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:699
|
||||
#: src/view/screens/Settings.tsx:687
|
||||
msgid "Clear all legacy storage data"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:701
|
||||
#: src/view/screens/Settings.tsx:689
|
||||
msgid "Clear all legacy storage data (restart after this)"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:711
|
||||
#: src/view/screens/Settings.tsx:699
|
||||
msgid "Clear all storage data"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:713
|
||||
#: src/view/screens/Settings.tsx:701
|
||||
msgid "Clear all storage data (restart after this)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -561,7 +550,7 @@ msgstr ""
|
|||
msgid "Custom domain"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:620
|
||||
#: src/view/screens/Settings.tsx:608
|
||||
msgid "Danger Zone"
|
||||
msgstr ""
|
||||
|
||||
|
@ -569,7 +558,7 @@ msgstr ""
|
|||
#~ msgid "Dark"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:627
|
||||
#: src/view/screens/Settings.tsx:615
|
||||
msgid "Delete account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -591,7 +580,7 @@ msgstr ""
|
|||
msgid "Delete my account"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:637
|
||||
#: src/view/screens/Settings.tsx:625
|
||||
msgid "Delete my account…"
|
||||
msgstr ""
|
||||
|
||||
|
@ -618,7 +607,7 @@ msgstr ""
|
|||
msgid "Dev Server"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:642
|
||||
#: src/view/screens/Settings.tsx:630
|
||||
msgid "Developer Tools"
|
||||
msgstr ""
|
||||
|
||||
|
@ -677,7 +666,7 @@ msgid "Edit list details"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:367
|
||||
#: src/view/screens/SavedFeeds.tsx:75
|
||||
#: src/view/screens/SavedFeeds.tsx:85
|
||||
msgid "Edit My Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -712,7 +701,7 @@ msgstr ""
|
|||
msgid "Email Updated"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:293
|
||||
#: src/view/screens/Settings.tsx:292
|
||||
msgid "Email:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -757,7 +746,7 @@ msgstr ""
|
|||
msgid "Failed to load recommended feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:558
|
||||
#: src/view/screens/Feeds.tsx:559
|
||||
msgid "Feed offline"
|
||||
msgstr ""
|
||||
|
||||
|
@ -765,12 +754,12 @@ msgstr ""
|
|||
msgid "Feed Preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:64
|
||||
#: src/view/shell/desktop/RightNav.tsx:65
|
||||
#: src/view/shell/Drawer.tsx:411
|
||||
msgid "Feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:474
|
||||
#: src/view/screens/Feeds.tsx:475
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:169
|
||||
#: src/view/shell/desktop/LeftNav.tsx:342
|
||||
#: src/view/shell/Drawer.tsx:328
|
||||
|
@ -782,7 +771,7 @@ msgstr ""
|
|||
msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:132
|
||||
#: src/view/screens/SavedFeeds.tsx:156
|
||||
msgid "Feeds are custom algorithms that users build with a little coding expertise. <0/> for more information."
|
||||
msgstr ""
|
||||
|
||||
|
@ -884,7 +873,7 @@ msgstr ""
|
|||
msgid "Handle"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:93
|
||||
#: src/view/shell/desktop/RightNav.tsx:94
|
||||
#: src/view/shell/Drawer.tsx:421
|
||||
msgid "Help"
|
||||
msgstr ""
|
||||
|
@ -901,6 +890,26 @@ msgstr ""
|
|||
msgid "Hide user list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:101
|
||||
msgid "Hmm, some kind of issue occured when contacting the feed server. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:89
|
||||
msgid "Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:95
|
||||
msgid "Hmm, the feed server appears to be offline. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:92
|
||||
msgid "Hmm, the feed server gave a bad response. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:86
|
||||
msgid "Hmmm, we're having trouble finding this feed. It may have been deleted."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:125
|
||||
#: src/view/shell/desktop/LeftNav.tsx:306
|
||||
#: src/view/shell/Drawer.tsx:275
|
||||
|
@ -908,9 +917,9 @@ msgstr ""
|
|||
msgid "Home"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:71
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:99
|
||||
#: src/view/screens/PreferencesHomeFeed.tsx:95
|
||||
#: src/view/screens/Settings.tsx:486
|
||||
#: src/view/screens/Settings.tsx:474
|
||||
msgid "Home Feed Preferences"
|
||||
msgstr ""
|
||||
|
||||
|
@ -953,12 +962,12 @@ msgstr ""
|
|||
msgid "Invalid username or password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:386
|
||||
#: src/view/screens/Settings.tsx:385
|
||||
msgid "Invite"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/InviteCodes.tsx:91
|
||||
#: src/view/screens/Settings.tsx:374
|
||||
#: src/view/screens/Settings.tsx:373
|
||||
msgid "Invite a Friend"
|
||||
msgstr ""
|
||||
|
||||
|
@ -991,7 +1000,7 @@ msgstr ""
|
|||
msgid "Language Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:546
|
||||
#: src/view/screens/Settings.tsx:534
|
||||
msgid "Languages"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1031,7 +1040,7 @@ msgstr ""
|
|||
#~ msgid "Light"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:625
|
||||
#: src/view/screens/ProfileFeed.tsx:626
|
||||
msgid "Like this feed"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1059,7 +1068,7 @@ msgstr ""
|
|||
msgid "Load more posts"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Notifications.tsx:120
|
||||
#: src/view/screens/Notifications.tsx:130
|
||||
msgid "Load new notifications"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1075,11 +1084,11 @@ msgstr ""
|
|||
msgid "Local dev server"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:104
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:133
|
||||
msgid "Login to account that is not listed"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:477
|
||||
#: src/view/screens/ProfileFeed.tsx:478
|
||||
msgid "Looks like this feed is only available to users with a Bluesky account. Please sign up or sign in to view this feed!"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1092,7 +1101,7 @@ msgid "Menu"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:51
|
||||
#: src/view/screens/Settings.tsx:568
|
||||
#: src/view/screens/Settings.tsx:556
|
||||
#: src/view/shell/desktop/LeftNav.tsx:400
|
||||
#: src/view/shell/Drawer.tsx:346
|
||||
#: src/view/shell/Drawer.tsx:347
|
||||
|
@ -1108,7 +1117,7 @@ msgid "More feeds"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/profile/ProfileHeader.tsx:506
|
||||
#: src/view/screens/ProfileFeed.tsx:367
|
||||
#: src/view/screens/ProfileFeed.tsx:368
|
||||
#: src/view/screens/ProfileList.tsx:506
|
||||
msgid "More options"
|
||||
msgstr ""
|
||||
|
@ -1161,7 +1170,7 @@ msgstr ""
|
|||
msgid "My Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:525
|
||||
#: src/view/screens/Settings.tsx:513
|
||||
msgid "My Saved Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1179,9 +1188,9 @@ msgid "New"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedPage.tsx:187
|
||||
#: src/view/screens/Feeds.tsx:509
|
||||
#: src/view/screens/Profile.tsx:380
|
||||
#: src/view/screens/ProfileFeed.tsx:447
|
||||
#: src/view/screens/Feeds.tsx:510
|
||||
#: src/view/screens/Profile.tsx:381
|
||||
#: src/view/screens/ProfileFeed.tsx:448
|
||||
#: src/view/screens/ProfileList.tsx:199
|
||||
#: src/view/screens/ProfileList.tsx:231
|
||||
#: src/view/shell/desktop/LeftNav.tsx:255
|
||||
|
@ -1212,7 +1221,7 @@ msgstr ""
|
|||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:618
|
||||
#: src/view/screens/ProfileFeed.tsx:619
|
||||
#: src/view/screens/ProfileList.tsx:632
|
||||
msgid "No description"
|
||||
msgstr ""
|
||||
|
@ -1221,7 +1230,7 @@ msgstr ""
|
|||
msgid "No result"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:451
|
||||
#: src/view/screens/Feeds.tsx:452
|
||||
msgid "No results found for \"{query}\""
|
||||
msgstr ""
|
||||
|
||||
|
@ -1246,8 +1255,8 @@ msgstr ""
|
|||
msgid "Not Applicable."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Notifications.tsx:87
|
||||
#: src/view/screens/Notifications.tsx:111
|
||||
#: src/view/screens/Notifications.tsx:97
|
||||
#: src/view/screens/Notifications.tsx:121
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:196
|
||||
#: src/view/shell/desktop/LeftNav.tsx:364
|
||||
#: src/view/shell/Drawer.tsx:299
|
||||
|
@ -1267,52 +1276,47 @@ msgstr ""
|
|||
msgid "One or more images is missing alt text."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:51
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:79
|
||||
msgid "Open navigation"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:538
|
||||
#: src/view/screens/Settings.tsx:526
|
||||
msgid "Opens configurable language settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:146
|
||||
#: src/view/shell/Drawer.tsx:509
|
||||
msgid "Opens list of invite codes"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/ChangeHandle.tsx:279
|
||||
msgid "Opens modal for using custom domain"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:563
|
||||
#: src/view/screens/Settings.tsx:551
|
||||
msgid "Opens moderation settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:519
|
||||
#: src/view/screens/Settings.tsx:507
|
||||
msgid "Opens screen with all saved feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:586
|
||||
#: src/view/screens/Settings.tsx:574
|
||||
msgid "Opens the app password settings page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:478
|
||||
#: src/view/screens/Settings.tsx:466
|
||||
msgid "Opens the home feed preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:669
|
||||
#: src/view/screens/Settings.tsx:657
|
||||
msgid "Opens the storybook page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:649
|
||||
#: src/view/screens/Settings.tsx:637
|
||||
msgid "Opens the system log page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:499
|
||||
#: src/view/screens/Settings.tsx:487
|
||||
msgid "Opens the threads preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:109
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:138
|
||||
msgid "Other account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1349,7 +1353,7 @@ msgstr ""
|
|||
msgid "Pictures meant for adults."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:79
|
||||
#: src/view/screens/SavedFeeds.tsx:89
|
||||
msgid "Pinned Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1415,7 +1419,7 @@ msgstr ""
|
|||
msgid "Prioritize Your Follows"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:75
|
||||
#: src/view/shell/desktop/RightNav.tsx:76
|
||||
msgid "Privacy"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1434,7 +1438,7 @@ msgstr ""
|
|||
msgid "Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:794
|
||||
#: src/view/screens/Settings.tsx:782
|
||||
msgid "Protect your account by verifying your email."
|
||||
msgstr ""
|
||||
|
||||
|
@ -1476,7 +1480,7 @@ msgstr ""
|
|||
msgid "Remove"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:92
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:108
|
||||
msgid "Remove {0} from my feeds?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1484,11 +1488,11 @@ msgstr ""
|
|||
msgid "Remove account"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:106
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:118
|
||||
msgid "Remove feed"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:91
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:107
|
||||
#: src/view/screens/ProfileFeed.tsx:278
|
||||
msgid "Remove from my feeds"
|
||||
msgstr ""
|
||||
|
@ -1501,7 +1505,7 @@ msgstr ""
|
|||
msgid "Remove image preview"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:107
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:119
|
||||
msgid "Remove this feed from your saved feeds?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1565,7 +1569,7 @@ msgstr ""
|
|||
msgid "Reset code"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:691
|
||||
#: src/view/screens/Settings.tsx:679
|
||||
msgid "Reset onboarding state"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1573,15 +1577,15 @@ msgstr ""
|
|||
msgid "Reset password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:681
|
||||
#: src/view/screens/Settings.tsx:669
|
||||
msgid "Reset preferences state"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:689
|
||||
#: src/view/screens/Settings.tsx:677
|
||||
msgid "Resets the onboarding state"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:679
|
||||
#: src/view/screens/Settings.tsx:667
|
||||
msgid "Resets the preferences state"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1628,7 +1632,7 @@ msgstr ""
|
|||
msgid "Save image crop"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:105
|
||||
#: src/view/screens/SavedFeeds.tsx:122
|
||||
msgid "Saved Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1726,7 +1730,7 @@ msgstr ""
|
|||
msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:280
|
||||
#: src/view/screens/Settings.tsx:279
|
||||
#: src/view/shell/desktop/LeftNav.tsx:436
|
||||
#: src/view/shell/Drawer.tsx:380
|
||||
#: src/view/shell/Drawer.tsx:381
|
||||
|
@ -1751,7 +1755,7 @@ msgstr ""
|
|||
#~ msgid "Share link"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:319
|
||||
#: src/view/screens/Settings.tsx:318
|
||||
msgid "Show"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1795,11 +1799,11 @@ msgstr ""
|
|||
msgid "Sign In"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:37
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:44
|
||||
msgid "Sign in as {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:94
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:118
|
||||
#: src/view/com/auth/login/Login.tsx:100
|
||||
msgid "Sign in as..."
|
||||
msgstr ""
|
||||
|
@ -1823,7 +1827,7 @@ msgstr ""
|
|||
msgid "Sign up or sign in to join the conversation"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:330
|
||||
#: src/view/screens/Settings.tsx:329
|
||||
msgid "Signed in as"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1848,11 +1852,11 @@ msgstr ""
|
|||
msgid "Staging"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:735
|
||||
#: src/view/screens/Settings.tsx:723
|
||||
msgid "Status page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:671
|
||||
#: src/view/screens/Settings.tsx:659
|
||||
msgid "Storybook"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1881,7 +1885,7 @@ msgstr ""
|
|||
#~ msgid "System"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:651
|
||||
#: src/view/screens/Settings.tsx:639
|
||||
msgid "System log"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1889,7 +1893,7 @@ msgstr ""
|
|||
msgid "Tall"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:84
|
||||
#: src/view/shell/desktop/RightNav.tsx:85
|
||||
msgid "Terms"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1962,7 +1966,7 @@ msgid "This warning is only available for posts with media attached."
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/PreferencesThreads.tsx:53
|
||||
#: src/view/screens/Settings.tsx:508
|
||||
#: src/view/screens/Settings.tsx:496
|
||||
msgid "Thread Preferences"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2069,15 +2073,15 @@ msgstr ""
|
|||
msgid "Users"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:755
|
||||
#: src/view/screens/Settings.tsx:743
|
||||
msgid "Verify email"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:780
|
||||
#: src/view/screens/Settings.tsx:768
|
||||
msgid "Verify my email"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:789
|
||||
#: src/view/screens/Settings.tsx:777
|
||||
msgid "Verify My Email"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2102,6 +2106,10 @@ msgstr ""
|
|||
msgid "We're so excited to have you join us!"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:98
|
||||
msgid "We're sorry, but this content is not viewable without a Bluesky account."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:236
|
||||
msgid "We're sorry, but your search could not be completed. Please try again in a few minutes."
|
||||
msgstr ""
|
||||
|
@ -2157,7 +2165,7 @@ msgstr ""
|
|||
msgid "You don't have any invite codes yet! We'll send you some when you've been on Bluesky for a little longer."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:92
|
||||
#: src/view/screens/SavedFeeds.tsx:102
|
||||
msgid "You don't have any pinned feeds."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2165,7 +2173,7 @@ msgstr ""
|
|||
msgid "You don't have any saved feeds!"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:118
|
||||
#: src/view/screens/SavedFeeds.tsx:135
|
||||
msgid "You don't have any saved feeds."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2228,12 +2236,6 @@ msgstr ""
|
|||
msgid "Your hosting provider"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:405
|
||||
#: src/view/shell/desktop/RightNav.tsx:127
|
||||
#: src/view/shell/Drawer.tsx:523
|
||||
msgid "Your invite codes are hidden when logged in using an App Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/onboarding/WelcomeMobile.tsx:59
|
||||
msgid "Your posts, likes, and blocks are public. Mutes are private."
|
||||
msgstr ""
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -21,12 +21,6 @@ msgstr ""
|
|||
#~ msgid ". This warning is only available for posts with media attached."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:410
|
||||
#: src/view/shell/desktop/RightNav.tsx:158
|
||||
#: src/view/shell/Drawer.tsx:527
|
||||
msgid "{0, plural, one {# invite code available} other {# invite codes available}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/Repost.tsx:44
|
||||
msgid "{0}"
|
||||
msgstr ""
|
||||
|
@ -35,11 +29,6 @@ msgstr ""
|
|||
msgid "{0} {purposeLabel} List"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:141
|
||||
#: src/view/shell/Drawer.tsx:504
|
||||
msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:86
|
||||
msgid "{message}"
|
||||
msgstr ""
|
||||
|
@ -61,12 +50,12 @@ msgid "A new version of the app is available. Please update to continue using th
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/EditImage.tsx:299
|
||||
#: src/view/screens/Settings.tsx:422
|
||||
#: src/view/screens/Settings.tsx:410
|
||||
msgid "Accessibility"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/LoginForm.tsx:161
|
||||
#: src/view/screens/Settings.tsx:289
|
||||
#: src/view/screens/Settings.tsx:288
|
||||
msgid "Account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -88,8 +77,8 @@ msgstr ""
|
|||
msgid "Add a user to this list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:358
|
||||
#: src/view/screens/Settings.tsx:367
|
||||
#: src/view/screens/Settings.tsx:357
|
||||
#: src/view/screens/Settings.tsx:366
|
||||
msgid "Add account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -140,7 +129,7 @@ msgstr ""
|
|||
msgid "Adult Content"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:574
|
||||
#: src/view/screens/Settings.tsx:562
|
||||
msgid "Advanced"
|
||||
msgstr ""
|
||||
|
||||
|
@ -172,7 +161,7 @@ msgstr ""
|
|||
msgid "App Language"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:594
|
||||
#: src/view/screens/Settings.tsx:582
|
||||
msgid "App passwords"
|
||||
msgstr ""
|
||||
|
||||
|
@ -180,7 +169,7 @@ msgstr ""
|
|||
msgid "App Passwords"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:437
|
||||
#: src/view/screens/Settings.tsx:425
|
||||
msgid "Appearance"
|
||||
msgstr ""
|
||||
|
||||
|
@ -205,7 +194,7 @@ msgid "Artistic or non-erotic nudity."
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/create/CreateAccount.tsx:145
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:122
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:151
|
||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:166
|
||||
#: src/view/com/auth/login/LoginForm.tsx:251
|
||||
#: src/view/com/auth/login/SetNewPasswordForm.tsx:148
|
||||
|
@ -217,7 +206,7 @@ msgstr ""
|
|||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:466
|
||||
#: src/view/screens/Settings.tsx:454
|
||||
msgid "Basics"
|
||||
msgstr ""
|
||||
|
||||
|
@ -226,7 +215,7 @@ msgstr ""
|
|||
msgid "Birthday"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:315
|
||||
#: src/view/screens/Settings.tsx:314
|
||||
msgid "Birthday:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -291,7 +280,7 @@ msgstr ""
|
|||
msgid "Bluesky.Social"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:723
|
||||
#: src/view/screens/Settings.tsx:711
|
||||
msgid "Build version {0} {1}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -359,12 +348,12 @@ msgstr ""
|
|||
msgid "Cancel waitlist signup"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:309
|
||||
#: src/view/screens/Settings.tsx:308
|
||||
msgid "Change"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:606
|
||||
#: src/view/screens/Settings.tsx:615
|
||||
#: src/view/screens/Settings.tsx:594
|
||||
#: src/view/screens/Settings.tsx:603
|
||||
msgid "Change handle"
|
||||
msgstr ""
|
||||
|
||||
|
@ -408,19 +397,19 @@ msgstr ""
|
|||
msgid "Choose your password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:699
|
||||
#: src/view/screens/Settings.tsx:687
|
||||
msgid "Clear all legacy storage data"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:701
|
||||
#: src/view/screens/Settings.tsx:689
|
||||
msgid "Clear all legacy storage data (restart after this)"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:711
|
||||
#: src/view/screens/Settings.tsx:699
|
||||
msgid "Clear all storage data"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:713
|
||||
#: src/view/screens/Settings.tsx:701
|
||||
msgid "Clear all storage data (restart after this)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -561,7 +550,7 @@ msgstr ""
|
|||
msgid "Custom domain"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:620
|
||||
#: src/view/screens/Settings.tsx:608
|
||||
msgid "Danger Zone"
|
||||
msgstr ""
|
||||
|
||||
|
@ -569,7 +558,7 @@ msgstr ""
|
|||
#~ msgid "Dark"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:627
|
||||
#: src/view/screens/Settings.tsx:615
|
||||
msgid "Delete account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -591,7 +580,7 @@ msgstr ""
|
|||
msgid "Delete my account"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:637
|
||||
#: src/view/screens/Settings.tsx:625
|
||||
msgid "Delete my account…"
|
||||
msgstr ""
|
||||
|
||||
|
@ -618,7 +607,7 @@ msgstr ""
|
|||
msgid "Dev Server"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:642
|
||||
#: src/view/screens/Settings.tsx:630
|
||||
msgid "Developer Tools"
|
||||
msgstr ""
|
||||
|
||||
|
@ -677,7 +666,7 @@ msgid "Edit list details"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:367
|
||||
#: src/view/screens/SavedFeeds.tsx:75
|
||||
#: src/view/screens/SavedFeeds.tsx:85
|
||||
msgid "Edit My Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -712,7 +701,7 @@ msgstr ""
|
|||
msgid "Email Updated"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:293
|
||||
#: src/view/screens/Settings.tsx:292
|
||||
msgid "Email:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -757,7 +746,7 @@ msgstr ""
|
|||
msgid "Failed to load recommended feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:558
|
||||
#: src/view/screens/Feeds.tsx:559
|
||||
msgid "Feed offline"
|
||||
msgstr ""
|
||||
|
||||
|
@ -765,12 +754,12 @@ msgstr ""
|
|||
msgid "Feed Preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:64
|
||||
#: src/view/shell/desktop/RightNav.tsx:65
|
||||
#: src/view/shell/Drawer.tsx:411
|
||||
msgid "Feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:474
|
||||
#: src/view/screens/Feeds.tsx:475
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:169
|
||||
#: src/view/shell/desktop/LeftNav.tsx:342
|
||||
#: src/view/shell/Drawer.tsx:328
|
||||
|
@ -782,7 +771,7 @@ msgstr ""
|
|||
msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:132
|
||||
#: src/view/screens/SavedFeeds.tsx:156
|
||||
msgid "Feeds are custom algorithms that users build with a little coding expertise. <0/> for more information."
|
||||
msgstr ""
|
||||
|
||||
|
@ -884,7 +873,7 @@ msgstr ""
|
|||
msgid "Handle"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:93
|
||||
#: src/view/shell/desktop/RightNav.tsx:94
|
||||
#: src/view/shell/Drawer.tsx:421
|
||||
msgid "Help"
|
||||
msgstr ""
|
||||
|
@ -901,6 +890,26 @@ msgstr ""
|
|||
msgid "Hide user list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:101
|
||||
msgid "Hmm, some kind of issue occured when contacting the feed server. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:89
|
||||
msgid "Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:95
|
||||
msgid "Hmm, the feed server appears to be offline. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:92
|
||||
msgid "Hmm, the feed server gave a bad response. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:86
|
||||
msgid "Hmmm, we're having trouble finding this feed. It may have been deleted."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:125
|
||||
#: src/view/shell/desktop/LeftNav.tsx:306
|
||||
#: src/view/shell/Drawer.tsx:275
|
||||
|
@ -908,9 +917,9 @@ msgstr ""
|
|||
msgid "Home"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:71
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:99
|
||||
#: src/view/screens/PreferencesHomeFeed.tsx:95
|
||||
#: src/view/screens/Settings.tsx:486
|
||||
#: src/view/screens/Settings.tsx:474
|
||||
msgid "Home Feed Preferences"
|
||||
msgstr ""
|
||||
|
||||
|
@ -953,12 +962,12 @@ msgstr ""
|
|||
msgid "Invalid username or password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:386
|
||||
#: src/view/screens/Settings.tsx:385
|
||||
msgid "Invite"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/InviteCodes.tsx:91
|
||||
#: src/view/screens/Settings.tsx:374
|
||||
#: src/view/screens/Settings.tsx:373
|
||||
msgid "Invite a Friend"
|
||||
msgstr ""
|
||||
|
||||
|
@ -991,7 +1000,7 @@ msgstr ""
|
|||
msgid "Language Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:546
|
||||
#: src/view/screens/Settings.tsx:534
|
||||
msgid "Languages"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1031,7 +1040,7 @@ msgstr ""
|
|||
#~ msgid "Light"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:625
|
||||
#: src/view/screens/ProfileFeed.tsx:626
|
||||
msgid "Like this feed"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1059,7 +1068,7 @@ msgstr ""
|
|||
msgid "Load more posts"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Notifications.tsx:120
|
||||
#: src/view/screens/Notifications.tsx:130
|
||||
msgid "Load new notifications"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1075,11 +1084,11 @@ msgstr ""
|
|||
msgid "Local dev server"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:104
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:133
|
||||
msgid "Login to account that is not listed"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:477
|
||||
#: src/view/screens/ProfileFeed.tsx:478
|
||||
msgid "Looks like this feed is only available to users with a Bluesky account. Please sign up or sign in to view this feed!"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1092,7 +1101,7 @@ msgid "Menu"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:51
|
||||
#: src/view/screens/Settings.tsx:568
|
||||
#: src/view/screens/Settings.tsx:556
|
||||
#: src/view/shell/desktop/LeftNav.tsx:400
|
||||
#: src/view/shell/Drawer.tsx:346
|
||||
#: src/view/shell/Drawer.tsx:347
|
||||
|
@ -1108,7 +1117,7 @@ msgid "More feeds"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/profile/ProfileHeader.tsx:506
|
||||
#: src/view/screens/ProfileFeed.tsx:367
|
||||
#: src/view/screens/ProfileFeed.tsx:368
|
||||
#: src/view/screens/ProfileList.tsx:506
|
||||
msgid "More options"
|
||||
msgstr ""
|
||||
|
@ -1161,7 +1170,7 @@ msgstr ""
|
|||
msgid "My Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:525
|
||||
#: src/view/screens/Settings.tsx:513
|
||||
msgid "My Saved Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1179,9 +1188,9 @@ msgid "New"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedPage.tsx:187
|
||||
#: src/view/screens/Feeds.tsx:509
|
||||
#: src/view/screens/Profile.tsx:380
|
||||
#: src/view/screens/ProfileFeed.tsx:447
|
||||
#: src/view/screens/Feeds.tsx:510
|
||||
#: src/view/screens/Profile.tsx:381
|
||||
#: src/view/screens/ProfileFeed.tsx:448
|
||||
#: src/view/screens/ProfileList.tsx:199
|
||||
#: src/view/screens/ProfileList.tsx:231
|
||||
#: src/view/shell/desktop/LeftNav.tsx:255
|
||||
|
@ -1212,7 +1221,7 @@ msgstr ""
|
|||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:618
|
||||
#: src/view/screens/ProfileFeed.tsx:619
|
||||
#: src/view/screens/ProfileList.tsx:632
|
||||
msgid "No description"
|
||||
msgstr ""
|
||||
|
@ -1221,7 +1230,7 @@ msgstr ""
|
|||
msgid "No result"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:451
|
||||
#: src/view/screens/Feeds.tsx:452
|
||||
msgid "No results found for \"{query}\""
|
||||
msgstr ""
|
||||
|
||||
|
@ -1246,8 +1255,8 @@ msgstr ""
|
|||
msgid "Not Applicable."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Notifications.tsx:87
|
||||
#: src/view/screens/Notifications.tsx:111
|
||||
#: src/view/screens/Notifications.tsx:97
|
||||
#: src/view/screens/Notifications.tsx:121
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:196
|
||||
#: src/view/shell/desktop/LeftNav.tsx:364
|
||||
#: src/view/shell/Drawer.tsx:299
|
||||
|
@ -1267,52 +1276,47 @@ msgstr ""
|
|||
msgid "One or more images is missing alt text."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:51
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:79
|
||||
msgid "Open navigation"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:538
|
||||
#: src/view/screens/Settings.tsx:526
|
||||
msgid "Opens configurable language settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:146
|
||||
#: src/view/shell/Drawer.tsx:509
|
||||
msgid "Opens list of invite codes"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/ChangeHandle.tsx:279
|
||||
msgid "Opens modal for using custom domain"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:563
|
||||
#: src/view/screens/Settings.tsx:551
|
||||
msgid "Opens moderation settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:519
|
||||
#: src/view/screens/Settings.tsx:507
|
||||
msgid "Opens screen with all saved feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:586
|
||||
#: src/view/screens/Settings.tsx:574
|
||||
msgid "Opens the app password settings page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:478
|
||||
#: src/view/screens/Settings.tsx:466
|
||||
msgid "Opens the home feed preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:669
|
||||
#: src/view/screens/Settings.tsx:657
|
||||
msgid "Opens the storybook page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:649
|
||||
#: src/view/screens/Settings.tsx:637
|
||||
msgid "Opens the system log page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:499
|
||||
#: src/view/screens/Settings.tsx:487
|
||||
msgid "Opens the threads preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:109
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:138
|
||||
msgid "Other account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1349,7 +1353,7 @@ msgstr ""
|
|||
msgid "Pictures meant for adults."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:79
|
||||
#: src/view/screens/SavedFeeds.tsx:89
|
||||
msgid "Pinned Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1415,7 +1419,7 @@ msgstr ""
|
|||
msgid "Prioritize Your Follows"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:75
|
||||
#: src/view/shell/desktop/RightNav.tsx:76
|
||||
msgid "Privacy"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1434,7 +1438,7 @@ msgstr ""
|
|||
msgid "Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:794
|
||||
#: src/view/screens/Settings.tsx:782
|
||||
msgid "Protect your account by verifying your email."
|
||||
msgstr ""
|
||||
|
||||
|
@ -1476,7 +1480,7 @@ msgstr ""
|
|||
msgid "Remove"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:92
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:108
|
||||
msgid "Remove {0} from my feeds?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1484,11 +1488,11 @@ msgstr ""
|
|||
msgid "Remove account"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:106
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:118
|
||||
msgid "Remove feed"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:91
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:107
|
||||
#: src/view/screens/ProfileFeed.tsx:278
|
||||
msgid "Remove from my feeds"
|
||||
msgstr ""
|
||||
|
@ -1501,7 +1505,7 @@ msgstr ""
|
|||
msgid "Remove image preview"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:107
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:119
|
||||
msgid "Remove this feed from your saved feeds?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1565,7 +1569,7 @@ msgstr ""
|
|||
msgid "Reset code"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:691
|
||||
#: src/view/screens/Settings.tsx:679
|
||||
msgid "Reset onboarding state"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1573,15 +1577,15 @@ msgstr ""
|
|||
msgid "Reset password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:681
|
||||
#: src/view/screens/Settings.tsx:669
|
||||
msgid "Reset preferences state"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:689
|
||||
#: src/view/screens/Settings.tsx:677
|
||||
msgid "Resets the onboarding state"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:679
|
||||
#: src/view/screens/Settings.tsx:667
|
||||
msgid "Resets the preferences state"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1628,7 +1632,7 @@ msgstr ""
|
|||
msgid "Save image crop"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:105
|
||||
#: src/view/screens/SavedFeeds.tsx:122
|
||||
msgid "Saved Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1726,7 +1730,7 @@ msgstr ""
|
|||
msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:280
|
||||
#: src/view/screens/Settings.tsx:279
|
||||
#: src/view/shell/desktop/LeftNav.tsx:436
|
||||
#: src/view/shell/Drawer.tsx:380
|
||||
#: src/view/shell/Drawer.tsx:381
|
||||
|
@ -1751,7 +1755,7 @@ msgstr ""
|
|||
#~ msgid "Share link"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:319
|
||||
#: src/view/screens/Settings.tsx:318
|
||||
msgid "Show"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1795,11 +1799,11 @@ msgstr ""
|
|||
msgid "Sign In"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:37
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:44
|
||||
msgid "Sign in as {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:94
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:118
|
||||
#: src/view/com/auth/login/Login.tsx:100
|
||||
msgid "Sign in as..."
|
||||
msgstr ""
|
||||
|
@ -1823,7 +1827,7 @@ msgstr ""
|
|||
msgid "Sign up or sign in to join the conversation"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:330
|
||||
#: src/view/screens/Settings.tsx:329
|
||||
msgid "Signed in as"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1848,11 +1852,11 @@ msgstr ""
|
|||
msgid "Staging"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:735
|
||||
#: src/view/screens/Settings.tsx:723
|
||||
msgid "Status page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:671
|
||||
#: src/view/screens/Settings.tsx:659
|
||||
msgid "Storybook"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1881,7 +1885,7 @@ msgstr ""
|
|||
#~ msgid "System"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:651
|
||||
#: src/view/screens/Settings.tsx:639
|
||||
msgid "System log"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1889,7 +1893,7 @@ msgstr ""
|
|||
msgid "Tall"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:84
|
||||
#: src/view/shell/desktop/RightNav.tsx:85
|
||||
msgid "Terms"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1962,7 +1966,7 @@ msgid "This warning is only available for posts with media attached."
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/PreferencesThreads.tsx:53
|
||||
#: src/view/screens/Settings.tsx:508
|
||||
#: src/view/screens/Settings.tsx:496
|
||||
msgid "Thread Preferences"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2069,15 +2073,15 @@ msgstr ""
|
|||
msgid "Users"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:755
|
||||
#: src/view/screens/Settings.tsx:743
|
||||
msgid "Verify email"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:780
|
||||
#: src/view/screens/Settings.tsx:768
|
||||
msgid "Verify my email"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:789
|
||||
#: src/view/screens/Settings.tsx:777
|
||||
msgid "Verify My Email"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2102,6 +2106,10 @@ msgstr ""
|
|||
msgid "We're so excited to have you join us!"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:98
|
||||
msgid "We're sorry, but this content is not viewable without a Bluesky account."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:236
|
||||
msgid "We're sorry, but your search could not be completed. Please try again in a few minutes."
|
||||
msgstr ""
|
||||
|
@ -2157,7 +2165,7 @@ msgstr ""
|
|||
msgid "You don't have any invite codes yet! We'll send you some when you've been on Bluesky for a little longer."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:92
|
||||
#: src/view/screens/SavedFeeds.tsx:102
|
||||
msgid "You don't have any pinned feeds."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2165,7 +2173,7 @@ msgstr ""
|
|||
msgid "You don't have any saved feeds!"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:118
|
||||
#: src/view/screens/SavedFeeds.tsx:135
|
||||
msgid "You don't have any saved feeds."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2228,12 +2236,6 @@ msgstr ""
|
|||
msgid "Your hosting provider"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:405
|
||||
#: src/view/shell/desktop/RightNav.tsx:127
|
||||
#: src/view/shell/Drawer.tsx:523
|
||||
msgid "Your invite codes are hidden when logged in using an App Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/onboarding/WelcomeMobile.tsx:59
|
||||
msgid "Your posts, likes, and blocks are public. Mutes are private."
|
||||
msgstr ""
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -21,12 +21,6 @@ msgstr ""
|
|||
#~ msgid ". This warning is only available for posts with media attached."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:410
|
||||
#: src/view/shell/desktop/RightNav.tsx:158
|
||||
#: src/view/shell/Drawer.tsx:527
|
||||
msgid "{0, plural, one {# invite code available} other {# invite codes available}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/Repost.tsx:44
|
||||
msgid "{0}"
|
||||
msgstr ""
|
||||
|
@ -35,11 +29,6 @@ msgstr ""
|
|||
msgid "{0} {purposeLabel} List"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:141
|
||||
#: src/view/shell/Drawer.tsx:504
|
||||
msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:86
|
||||
msgid "{message}"
|
||||
msgstr ""
|
||||
|
@ -61,12 +50,12 @@ msgid "A new version of the app is available. Please update to continue using th
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/EditImage.tsx:299
|
||||
#: src/view/screens/Settings.tsx:422
|
||||
#: src/view/screens/Settings.tsx:410
|
||||
msgid "Accessibility"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/LoginForm.tsx:161
|
||||
#: src/view/screens/Settings.tsx:289
|
||||
#: src/view/screens/Settings.tsx:288
|
||||
msgid "Account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -88,8 +77,8 @@ msgstr ""
|
|||
msgid "Add a user to this list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:358
|
||||
#: src/view/screens/Settings.tsx:367
|
||||
#: src/view/screens/Settings.tsx:357
|
||||
#: src/view/screens/Settings.tsx:366
|
||||
msgid "Add account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -140,7 +129,7 @@ msgstr ""
|
|||
msgid "Adult Content"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:574
|
||||
#: src/view/screens/Settings.tsx:562
|
||||
msgid "Advanced"
|
||||
msgstr ""
|
||||
|
||||
|
@ -172,7 +161,7 @@ msgstr ""
|
|||
msgid "App Language"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:594
|
||||
#: src/view/screens/Settings.tsx:582
|
||||
msgid "App passwords"
|
||||
msgstr ""
|
||||
|
||||
|
@ -180,7 +169,7 @@ msgstr ""
|
|||
msgid "App Passwords"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:437
|
||||
#: src/view/screens/Settings.tsx:425
|
||||
msgid "Appearance"
|
||||
msgstr ""
|
||||
|
||||
|
@ -205,7 +194,7 @@ msgid "Artistic or non-erotic nudity."
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/create/CreateAccount.tsx:145
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:122
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:151
|
||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:166
|
||||
#: src/view/com/auth/login/LoginForm.tsx:251
|
||||
#: src/view/com/auth/login/SetNewPasswordForm.tsx:148
|
||||
|
@ -217,7 +206,7 @@ msgstr ""
|
|||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:466
|
||||
#: src/view/screens/Settings.tsx:454
|
||||
msgid "Basics"
|
||||
msgstr ""
|
||||
|
||||
|
@ -226,7 +215,7 @@ msgstr ""
|
|||
msgid "Birthday"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:315
|
||||
#: src/view/screens/Settings.tsx:314
|
||||
msgid "Birthday:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -291,7 +280,7 @@ msgstr ""
|
|||
msgid "Bluesky.Social"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:723
|
||||
#: src/view/screens/Settings.tsx:711
|
||||
msgid "Build version {0} {1}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -359,12 +348,12 @@ msgstr ""
|
|||
msgid "Cancel waitlist signup"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:309
|
||||
#: src/view/screens/Settings.tsx:308
|
||||
msgid "Change"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:606
|
||||
#: src/view/screens/Settings.tsx:615
|
||||
#: src/view/screens/Settings.tsx:594
|
||||
#: src/view/screens/Settings.tsx:603
|
||||
msgid "Change handle"
|
||||
msgstr ""
|
||||
|
||||
|
@ -408,19 +397,19 @@ msgstr ""
|
|||
msgid "Choose your password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:699
|
||||
#: src/view/screens/Settings.tsx:687
|
||||
msgid "Clear all legacy storage data"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:701
|
||||
#: src/view/screens/Settings.tsx:689
|
||||
msgid "Clear all legacy storage data (restart after this)"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:711
|
||||
#: src/view/screens/Settings.tsx:699
|
||||
msgid "Clear all storage data"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:713
|
||||
#: src/view/screens/Settings.tsx:701
|
||||
msgid "Clear all storage data (restart after this)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -561,7 +550,7 @@ msgstr ""
|
|||
msgid "Custom domain"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:620
|
||||
#: src/view/screens/Settings.tsx:608
|
||||
msgid "Danger Zone"
|
||||
msgstr ""
|
||||
|
||||
|
@ -569,7 +558,7 @@ msgstr ""
|
|||
#~ msgid "Dark"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:627
|
||||
#: src/view/screens/Settings.tsx:615
|
||||
msgid "Delete account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -591,7 +580,7 @@ msgstr ""
|
|||
msgid "Delete my account"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:637
|
||||
#: src/view/screens/Settings.tsx:625
|
||||
msgid "Delete my account…"
|
||||
msgstr ""
|
||||
|
||||
|
@ -618,7 +607,7 @@ msgstr ""
|
|||
msgid "Dev Server"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:642
|
||||
#: src/view/screens/Settings.tsx:630
|
||||
msgid "Developer Tools"
|
||||
msgstr ""
|
||||
|
||||
|
@ -677,7 +666,7 @@ msgid "Edit list details"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:367
|
||||
#: src/view/screens/SavedFeeds.tsx:75
|
||||
#: src/view/screens/SavedFeeds.tsx:85
|
||||
msgid "Edit My Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -712,7 +701,7 @@ msgstr ""
|
|||
msgid "Email Updated"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:293
|
||||
#: src/view/screens/Settings.tsx:292
|
||||
msgid "Email:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -757,7 +746,7 @@ msgstr ""
|
|||
msgid "Failed to load recommended feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:558
|
||||
#: src/view/screens/Feeds.tsx:559
|
||||
msgid "Feed offline"
|
||||
msgstr ""
|
||||
|
||||
|
@ -765,12 +754,12 @@ msgstr ""
|
|||
msgid "Feed Preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:64
|
||||
#: src/view/shell/desktop/RightNav.tsx:65
|
||||
#: src/view/shell/Drawer.tsx:411
|
||||
msgid "Feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:474
|
||||
#: src/view/screens/Feeds.tsx:475
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:169
|
||||
#: src/view/shell/desktop/LeftNav.tsx:342
|
||||
#: src/view/shell/Drawer.tsx:328
|
||||
|
@ -782,7 +771,7 @@ msgstr ""
|
|||
msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:132
|
||||
#: src/view/screens/SavedFeeds.tsx:156
|
||||
msgid "Feeds are custom algorithms that users build with a little coding expertise. <0/> for more information."
|
||||
msgstr ""
|
||||
|
||||
|
@ -884,7 +873,7 @@ msgstr ""
|
|||
msgid "Handle"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:93
|
||||
#: src/view/shell/desktop/RightNav.tsx:94
|
||||
#: src/view/shell/Drawer.tsx:421
|
||||
msgid "Help"
|
||||
msgstr ""
|
||||
|
@ -901,6 +890,26 @@ msgstr ""
|
|||
msgid "Hide user list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:101
|
||||
msgid "Hmm, some kind of issue occured when contacting the feed server. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:89
|
||||
msgid "Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:95
|
||||
msgid "Hmm, the feed server appears to be offline. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:92
|
||||
msgid "Hmm, the feed server gave a bad response. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:86
|
||||
msgid "Hmmm, we're having trouble finding this feed. It may have been deleted."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:125
|
||||
#: src/view/shell/desktop/LeftNav.tsx:306
|
||||
#: src/view/shell/Drawer.tsx:275
|
||||
|
@ -908,9 +917,9 @@ msgstr ""
|
|||
msgid "Home"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:71
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:99
|
||||
#: src/view/screens/PreferencesHomeFeed.tsx:95
|
||||
#: src/view/screens/Settings.tsx:486
|
||||
#: src/view/screens/Settings.tsx:474
|
||||
msgid "Home Feed Preferences"
|
||||
msgstr ""
|
||||
|
||||
|
@ -953,12 +962,12 @@ msgstr ""
|
|||
msgid "Invalid username or password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:386
|
||||
#: src/view/screens/Settings.tsx:385
|
||||
msgid "Invite"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/InviteCodes.tsx:91
|
||||
#: src/view/screens/Settings.tsx:374
|
||||
#: src/view/screens/Settings.tsx:373
|
||||
msgid "Invite a Friend"
|
||||
msgstr ""
|
||||
|
||||
|
@ -991,7 +1000,7 @@ msgstr ""
|
|||
msgid "Language Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:546
|
||||
#: src/view/screens/Settings.tsx:534
|
||||
msgid "Languages"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1031,7 +1040,7 @@ msgstr ""
|
|||
#~ msgid "Light"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:625
|
||||
#: src/view/screens/ProfileFeed.tsx:626
|
||||
msgid "Like this feed"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1059,7 +1068,7 @@ msgstr ""
|
|||
msgid "Load more posts"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Notifications.tsx:120
|
||||
#: src/view/screens/Notifications.tsx:130
|
||||
msgid "Load new notifications"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1075,11 +1084,11 @@ msgstr ""
|
|||
msgid "Local dev server"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:104
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:133
|
||||
msgid "Login to account that is not listed"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:477
|
||||
#: src/view/screens/ProfileFeed.tsx:478
|
||||
msgid "Looks like this feed is only available to users with a Bluesky account. Please sign up or sign in to view this feed!"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1092,7 +1101,7 @@ msgid "Menu"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:51
|
||||
#: src/view/screens/Settings.tsx:568
|
||||
#: src/view/screens/Settings.tsx:556
|
||||
#: src/view/shell/desktop/LeftNav.tsx:400
|
||||
#: src/view/shell/Drawer.tsx:346
|
||||
#: src/view/shell/Drawer.tsx:347
|
||||
|
@ -1108,7 +1117,7 @@ msgid "More feeds"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/profile/ProfileHeader.tsx:506
|
||||
#: src/view/screens/ProfileFeed.tsx:367
|
||||
#: src/view/screens/ProfileFeed.tsx:368
|
||||
#: src/view/screens/ProfileList.tsx:506
|
||||
msgid "More options"
|
||||
msgstr ""
|
||||
|
@ -1161,7 +1170,7 @@ msgstr ""
|
|||
msgid "My Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:525
|
||||
#: src/view/screens/Settings.tsx:513
|
||||
msgid "My Saved Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1179,9 +1188,9 @@ msgid "New"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedPage.tsx:187
|
||||
#: src/view/screens/Feeds.tsx:509
|
||||
#: src/view/screens/Profile.tsx:380
|
||||
#: src/view/screens/ProfileFeed.tsx:447
|
||||
#: src/view/screens/Feeds.tsx:510
|
||||
#: src/view/screens/Profile.tsx:381
|
||||
#: src/view/screens/ProfileFeed.tsx:448
|
||||
#: src/view/screens/ProfileList.tsx:199
|
||||
#: src/view/screens/ProfileList.tsx:231
|
||||
#: src/view/shell/desktop/LeftNav.tsx:255
|
||||
|
@ -1212,7 +1221,7 @@ msgstr ""
|
|||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:618
|
||||
#: src/view/screens/ProfileFeed.tsx:619
|
||||
#: src/view/screens/ProfileList.tsx:632
|
||||
msgid "No description"
|
||||
msgstr ""
|
||||
|
@ -1221,7 +1230,7 @@ msgstr ""
|
|||
msgid "No result"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:451
|
||||
#: src/view/screens/Feeds.tsx:452
|
||||
msgid "No results found for \"{query}\""
|
||||
msgstr ""
|
||||
|
||||
|
@ -1246,8 +1255,8 @@ msgstr ""
|
|||
msgid "Not Applicable."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Notifications.tsx:87
|
||||
#: src/view/screens/Notifications.tsx:111
|
||||
#: src/view/screens/Notifications.tsx:97
|
||||
#: src/view/screens/Notifications.tsx:121
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:196
|
||||
#: src/view/shell/desktop/LeftNav.tsx:364
|
||||
#: src/view/shell/Drawer.tsx:299
|
||||
|
@ -1267,52 +1276,47 @@ msgstr ""
|
|||
msgid "One or more images is missing alt text."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:51
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:79
|
||||
msgid "Open navigation"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:538
|
||||
#: src/view/screens/Settings.tsx:526
|
||||
msgid "Opens configurable language settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:146
|
||||
#: src/view/shell/Drawer.tsx:509
|
||||
msgid "Opens list of invite codes"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/ChangeHandle.tsx:279
|
||||
msgid "Opens modal for using custom domain"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:563
|
||||
#: src/view/screens/Settings.tsx:551
|
||||
msgid "Opens moderation settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:519
|
||||
#: src/view/screens/Settings.tsx:507
|
||||
msgid "Opens screen with all saved feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:586
|
||||
#: src/view/screens/Settings.tsx:574
|
||||
msgid "Opens the app password settings page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:478
|
||||
#: src/view/screens/Settings.tsx:466
|
||||
msgid "Opens the home feed preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:669
|
||||
#: src/view/screens/Settings.tsx:657
|
||||
msgid "Opens the storybook page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:649
|
||||
#: src/view/screens/Settings.tsx:637
|
||||
msgid "Opens the system log page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:499
|
||||
#: src/view/screens/Settings.tsx:487
|
||||
msgid "Opens the threads preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:109
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:138
|
||||
msgid "Other account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1349,7 +1353,7 @@ msgstr ""
|
|||
msgid "Pictures meant for adults."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:79
|
||||
#: src/view/screens/SavedFeeds.tsx:89
|
||||
msgid "Pinned Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1415,7 +1419,7 @@ msgstr ""
|
|||
msgid "Prioritize Your Follows"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:75
|
||||
#: src/view/shell/desktop/RightNav.tsx:76
|
||||
msgid "Privacy"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1434,7 +1438,7 @@ msgstr ""
|
|||
msgid "Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:794
|
||||
#: src/view/screens/Settings.tsx:782
|
||||
msgid "Protect your account by verifying your email."
|
||||
msgstr ""
|
||||
|
||||
|
@ -1476,7 +1480,7 @@ msgstr ""
|
|||
msgid "Remove"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:92
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:108
|
||||
msgid "Remove {0} from my feeds?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1484,11 +1488,11 @@ msgstr ""
|
|||
msgid "Remove account"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:106
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:118
|
||||
msgid "Remove feed"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:91
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:107
|
||||
#: src/view/screens/ProfileFeed.tsx:278
|
||||
msgid "Remove from my feeds"
|
||||
msgstr ""
|
||||
|
@ -1501,7 +1505,7 @@ msgstr ""
|
|||
msgid "Remove image preview"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:107
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:119
|
||||
msgid "Remove this feed from your saved feeds?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1565,7 +1569,7 @@ msgstr ""
|
|||
msgid "Reset code"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:691
|
||||
#: src/view/screens/Settings.tsx:679
|
||||
msgid "Reset onboarding state"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1573,15 +1577,15 @@ msgstr ""
|
|||
msgid "Reset password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:681
|
||||
#: src/view/screens/Settings.tsx:669
|
||||
msgid "Reset preferences state"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:689
|
||||
#: src/view/screens/Settings.tsx:677
|
||||
msgid "Resets the onboarding state"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:679
|
||||
#: src/view/screens/Settings.tsx:667
|
||||
msgid "Resets the preferences state"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1628,7 +1632,7 @@ msgstr ""
|
|||
msgid "Save image crop"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:105
|
||||
#: src/view/screens/SavedFeeds.tsx:122
|
||||
msgid "Saved Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1726,7 +1730,7 @@ msgstr ""
|
|||
msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:280
|
||||
#: src/view/screens/Settings.tsx:279
|
||||
#: src/view/shell/desktop/LeftNav.tsx:436
|
||||
#: src/view/shell/Drawer.tsx:380
|
||||
#: src/view/shell/Drawer.tsx:381
|
||||
|
@ -1751,7 +1755,7 @@ msgstr ""
|
|||
#~ msgid "Share link"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:319
|
||||
#: src/view/screens/Settings.tsx:318
|
||||
msgid "Show"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1795,11 +1799,11 @@ msgstr ""
|
|||
msgid "Sign In"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:37
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:44
|
||||
msgid "Sign in as {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:94
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:118
|
||||
#: src/view/com/auth/login/Login.tsx:100
|
||||
msgid "Sign in as..."
|
||||
msgstr ""
|
||||
|
@ -1823,7 +1827,7 @@ msgstr ""
|
|||
msgid "Sign up or sign in to join the conversation"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:330
|
||||
#: src/view/screens/Settings.tsx:329
|
||||
msgid "Signed in as"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1848,11 +1852,11 @@ msgstr ""
|
|||
msgid "Staging"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:735
|
||||
#: src/view/screens/Settings.tsx:723
|
||||
msgid "Status page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:671
|
||||
#: src/view/screens/Settings.tsx:659
|
||||
msgid "Storybook"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1881,7 +1885,7 @@ msgstr ""
|
|||
#~ msgid "System"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:651
|
||||
#: src/view/screens/Settings.tsx:639
|
||||
msgid "System log"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1889,7 +1893,7 @@ msgstr ""
|
|||
msgid "Tall"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:84
|
||||
#: src/view/shell/desktop/RightNav.tsx:85
|
||||
msgid "Terms"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1962,7 +1966,7 @@ msgid "This warning is only available for posts with media attached."
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/PreferencesThreads.tsx:53
|
||||
#: src/view/screens/Settings.tsx:508
|
||||
#: src/view/screens/Settings.tsx:496
|
||||
msgid "Thread Preferences"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2069,15 +2073,15 @@ msgstr ""
|
|||
msgid "Users"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:755
|
||||
#: src/view/screens/Settings.tsx:743
|
||||
msgid "Verify email"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:780
|
||||
#: src/view/screens/Settings.tsx:768
|
||||
msgid "Verify my email"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:789
|
||||
#: src/view/screens/Settings.tsx:777
|
||||
msgid "Verify My Email"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2102,6 +2106,10 @@ msgstr ""
|
|||
msgid "We're so excited to have you join us!"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:98
|
||||
msgid "We're sorry, but this content is not viewable without a Bluesky account."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:236
|
||||
msgid "We're sorry, but your search could not be completed. Please try again in a few minutes."
|
||||
msgstr ""
|
||||
|
@ -2157,7 +2165,7 @@ msgstr ""
|
|||
msgid "You don't have any invite codes yet! We'll send you some when you've been on Bluesky for a little longer."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:92
|
||||
#: src/view/screens/SavedFeeds.tsx:102
|
||||
msgid "You don't have any pinned feeds."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2165,7 +2173,7 @@ msgstr ""
|
|||
msgid "You don't have any saved feeds!"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:118
|
||||
#: src/view/screens/SavedFeeds.tsx:135
|
||||
msgid "You don't have any saved feeds."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2228,12 +2236,6 @@ msgstr ""
|
|||
msgid "Your hosting provider"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:405
|
||||
#: src/view/shell/desktop/RightNav.tsx:127
|
||||
#: src/view/shell/Drawer.tsx:523
|
||||
msgid "Your invite codes are hidden when logged in using an App Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/onboarding/WelcomeMobile.tsx:59
|
||||
msgid "Your posts, likes, and blocks are public. Mutes are private."
|
||||
msgstr ""
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -21,12 +21,6 @@ msgstr ""
|
|||
#~ msgid ". This warning is only available for posts with media attached."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:410
|
||||
#: src/view/shell/desktop/RightNav.tsx:158
|
||||
#: src/view/shell/Drawer.tsx:527
|
||||
msgid "{0, plural, one {# invite code available} other {# invite codes available}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/Repost.tsx:44
|
||||
msgid "{0}"
|
||||
msgstr ""
|
||||
|
@ -35,11 +29,6 @@ msgstr ""
|
|||
msgid "{0} {purposeLabel} List"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:141
|
||||
#: src/view/shell/Drawer.tsx:504
|
||||
msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:86
|
||||
msgid "{message}"
|
||||
msgstr ""
|
||||
|
@ -61,12 +50,12 @@ msgid "A new version of the app is available. Please update to continue using th
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/EditImage.tsx:299
|
||||
#: src/view/screens/Settings.tsx:422
|
||||
#: src/view/screens/Settings.tsx:410
|
||||
msgid "Accessibility"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/LoginForm.tsx:161
|
||||
#: src/view/screens/Settings.tsx:289
|
||||
#: src/view/screens/Settings.tsx:288
|
||||
msgid "Account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -88,8 +77,8 @@ msgstr ""
|
|||
msgid "Add a user to this list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:358
|
||||
#: src/view/screens/Settings.tsx:367
|
||||
#: src/view/screens/Settings.tsx:357
|
||||
#: src/view/screens/Settings.tsx:366
|
||||
msgid "Add account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -140,7 +129,7 @@ msgstr ""
|
|||
msgid "Adult Content"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:574
|
||||
#: src/view/screens/Settings.tsx:562
|
||||
msgid "Advanced"
|
||||
msgstr ""
|
||||
|
||||
|
@ -172,7 +161,7 @@ msgstr ""
|
|||
msgid "App Language"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:594
|
||||
#: src/view/screens/Settings.tsx:582
|
||||
msgid "App passwords"
|
||||
msgstr ""
|
||||
|
||||
|
@ -180,7 +169,7 @@ msgstr ""
|
|||
msgid "App Passwords"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:437
|
||||
#: src/view/screens/Settings.tsx:425
|
||||
msgid "Appearance"
|
||||
msgstr ""
|
||||
|
||||
|
@ -205,7 +194,7 @@ msgid "Artistic or non-erotic nudity."
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/create/CreateAccount.tsx:145
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:122
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:151
|
||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:166
|
||||
#: src/view/com/auth/login/LoginForm.tsx:251
|
||||
#: src/view/com/auth/login/SetNewPasswordForm.tsx:148
|
||||
|
@ -217,7 +206,7 @@ msgstr ""
|
|||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:466
|
||||
#: src/view/screens/Settings.tsx:454
|
||||
msgid "Basics"
|
||||
msgstr ""
|
||||
|
||||
|
@ -226,7 +215,7 @@ msgstr ""
|
|||
msgid "Birthday"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:315
|
||||
#: src/view/screens/Settings.tsx:314
|
||||
msgid "Birthday:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -291,7 +280,7 @@ msgstr ""
|
|||
msgid "Bluesky.Social"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:723
|
||||
#: src/view/screens/Settings.tsx:711
|
||||
msgid "Build version {0} {1}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -359,12 +348,12 @@ msgstr ""
|
|||
msgid "Cancel waitlist signup"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:309
|
||||
#: src/view/screens/Settings.tsx:308
|
||||
msgid "Change"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:606
|
||||
#: src/view/screens/Settings.tsx:615
|
||||
#: src/view/screens/Settings.tsx:594
|
||||
#: src/view/screens/Settings.tsx:603
|
||||
msgid "Change handle"
|
||||
msgstr ""
|
||||
|
||||
|
@ -408,19 +397,19 @@ msgstr ""
|
|||
msgid "Choose your password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:699
|
||||
#: src/view/screens/Settings.tsx:687
|
||||
msgid "Clear all legacy storage data"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:701
|
||||
#: src/view/screens/Settings.tsx:689
|
||||
msgid "Clear all legacy storage data (restart after this)"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:711
|
||||
#: src/view/screens/Settings.tsx:699
|
||||
msgid "Clear all storage data"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:713
|
||||
#: src/view/screens/Settings.tsx:701
|
||||
msgid "Clear all storage data (restart after this)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -561,7 +550,7 @@ msgstr ""
|
|||
msgid "Custom domain"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:620
|
||||
#: src/view/screens/Settings.tsx:608
|
||||
msgid "Danger Zone"
|
||||
msgstr ""
|
||||
|
||||
|
@ -569,7 +558,7 @@ msgstr ""
|
|||
#~ msgid "Dark"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:627
|
||||
#: src/view/screens/Settings.tsx:615
|
||||
msgid "Delete account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -591,7 +580,7 @@ msgstr ""
|
|||
msgid "Delete my account"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:637
|
||||
#: src/view/screens/Settings.tsx:625
|
||||
msgid "Delete my account…"
|
||||
msgstr ""
|
||||
|
||||
|
@ -618,7 +607,7 @@ msgstr ""
|
|||
msgid "Dev Server"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:642
|
||||
#: src/view/screens/Settings.tsx:630
|
||||
msgid "Developer Tools"
|
||||
msgstr ""
|
||||
|
||||
|
@ -677,7 +666,7 @@ msgid "Edit list details"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:367
|
||||
#: src/view/screens/SavedFeeds.tsx:75
|
||||
#: src/view/screens/SavedFeeds.tsx:85
|
||||
msgid "Edit My Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -712,7 +701,7 @@ msgstr ""
|
|||
msgid "Email Updated"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:293
|
||||
#: src/view/screens/Settings.tsx:292
|
||||
msgid "Email:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -757,7 +746,7 @@ msgstr ""
|
|||
msgid "Failed to load recommended feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:558
|
||||
#: src/view/screens/Feeds.tsx:559
|
||||
msgid "Feed offline"
|
||||
msgstr ""
|
||||
|
||||
|
@ -765,12 +754,12 @@ msgstr ""
|
|||
msgid "Feed Preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:64
|
||||
#: src/view/shell/desktop/RightNav.tsx:65
|
||||
#: src/view/shell/Drawer.tsx:411
|
||||
msgid "Feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:474
|
||||
#: src/view/screens/Feeds.tsx:475
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:169
|
||||
#: src/view/shell/desktop/LeftNav.tsx:342
|
||||
#: src/view/shell/Drawer.tsx:328
|
||||
|
@ -782,7 +771,7 @@ msgstr ""
|
|||
msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:132
|
||||
#: src/view/screens/SavedFeeds.tsx:156
|
||||
msgid "Feeds are custom algorithms that users build with a little coding expertise. <0/> for more information."
|
||||
msgstr ""
|
||||
|
||||
|
@ -884,7 +873,7 @@ msgstr ""
|
|||
msgid "Handle"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:93
|
||||
#: src/view/shell/desktop/RightNav.tsx:94
|
||||
#: src/view/shell/Drawer.tsx:421
|
||||
msgid "Help"
|
||||
msgstr ""
|
||||
|
@ -901,6 +890,26 @@ msgstr ""
|
|||
msgid "Hide user list"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:101
|
||||
msgid "Hmm, some kind of issue occured when contacting the feed server. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:89
|
||||
msgid "Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:95
|
||||
msgid "Hmm, the feed server appears to be offline. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:92
|
||||
msgid "Hmm, the feed server gave a bad response. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:86
|
||||
msgid "Hmmm, we're having trouble finding this feed. It may have been deleted."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:125
|
||||
#: src/view/shell/desktop/LeftNav.tsx:306
|
||||
#: src/view/shell/Drawer.tsx:275
|
||||
|
@ -908,9 +917,9 @@ msgstr ""
|
|||
msgid "Home"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:71
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:99
|
||||
#: src/view/screens/PreferencesHomeFeed.tsx:95
|
||||
#: src/view/screens/Settings.tsx:486
|
||||
#: src/view/screens/Settings.tsx:474
|
||||
msgid "Home Feed Preferences"
|
||||
msgstr ""
|
||||
|
||||
|
@ -953,12 +962,12 @@ msgstr ""
|
|||
msgid "Invalid username or password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:386
|
||||
#: src/view/screens/Settings.tsx:385
|
||||
msgid "Invite"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/InviteCodes.tsx:91
|
||||
#: src/view/screens/Settings.tsx:374
|
||||
#: src/view/screens/Settings.tsx:373
|
||||
msgid "Invite a Friend"
|
||||
msgstr ""
|
||||
|
||||
|
@ -991,7 +1000,7 @@ msgstr ""
|
|||
msgid "Language Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:546
|
||||
#: src/view/screens/Settings.tsx:534
|
||||
msgid "Languages"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1031,7 +1040,7 @@ msgstr ""
|
|||
#~ msgid "Light"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:625
|
||||
#: src/view/screens/ProfileFeed.tsx:626
|
||||
msgid "Like this feed"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1059,7 +1068,7 @@ msgstr ""
|
|||
msgid "Load more posts"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Notifications.tsx:120
|
||||
#: src/view/screens/Notifications.tsx:130
|
||||
msgid "Load new notifications"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1075,11 +1084,11 @@ msgstr ""
|
|||
msgid "Local dev server"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:104
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:133
|
||||
msgid "Login to account that is not listed"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:477
|
||||
#: src/view/screens/ProfileFeed.tsx:478
|
||||
msgid "Looks like this feed is only available to users with a Bluesky account. Please sign up or sign in to view this feed!"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1092,7 +1101,7 @@ msgid "Menu"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/Moderation.tsx:51
|
||||
#: src/view/screens/Settings.tsx:568
|
||||
#: src/view/screens/Settings.tsx:556
|
||||
#: src/view/shell/desktop/LeftNav.tsx:400
|
||||
#: src/view/shell/Drawer.tsx:346
|
||||
#: src/view/shell/Drawer.tsx:347
|
||||
|
@ -1108,7 +1117,7 @@ msgid "More feeds"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/profile/ProfileHeader.tsx:506
|
||||
#: src/view/screens/ProfileFeed.tsx:367
|
||||
#: src/view/screens/ProfileFeed.tsx:368
|
||||
#: src/view/screens/ProfileList.tsx:506
|
||||
msgid "More options"
|
||||
msgstr ""
|
||||
|
@ -1161,7 +1170,7 @@ msgstr ""
|
|||
msgid "My Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:525
|
||||
#: src/view/screens/Settings.tsx:513
|
||||
msgid "My Saved Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1179,9 +1188,9 @@ msgid "New"
|
|||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedPage.tsx:187
|
||||
#: src/view/screens/Feeds.tsx:509
|
||||
#: src/view/screens/Profile.tsx:380
|
||||
#: src/view/screens/ProfileFeed.tsx:447
|
||||
#: src/view/screens/Feeds.tsx:510
|
||||
#: src/view/screens/Profile.tsx:381
|
||||
#: src/view/screens/ProfileFeed.tsx:448
|
||||
#: src/view/screens/ProfileList.tsx:199
|
||||
#: src/view/screens/ProfileList.tsx:231
|
||||
#: src/view/shell/desktop/LeftNav.tsx:255
|
||||
|
@ -1212,7 +1221,7 @@ msgstr ""
|
|||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:618
|
||||
#: src/view/screens/ProfileFeed.tsx:619
|
||||
#: src/view/screens/ProfileList.tsx:632
|
||||
msgid "No description"
|
||||
msgstr ""
|
||||
|
@ -1221,7 +1230,7 @@ msgstr ""
|
|||
msgid "No result"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:451
|
||||
#: src/view/screens/Feeds.tsx:452
|
||||
msgid "No results found for \"{query}\""
|
||||
msgstr ""
|
||||
|
||||
|
@ -1246,8 +1255,8 @@ msgstr ""
|
|||
msgid "Not Applicable."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Notifications.tsx:87
|
||||
#: src/view/screens/Notifications.tsx:111
|
||||
#: src/view/screens/Notifications.tsx:97
|
||||
#: src/view/screens/Notifications.tsx:121
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:196
|
||||
#: src/view/shell/desktop/LeftNav.tsx:364
|
||||
#: src/view/shell/Drawer.tsx:299
|
||||
|
@ -1267,52 +1276,47 @@ msgstr ""
|
|||
msgid "One or more images is missing alt text."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:51
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:79
|
||||
msgid "Open navigation"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:538
|
||||
#: src/view/screens/Settings.tsx:526
|
||||
msgid "Opens configurable language settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:146
|
||||
#: src/view/shell/Drawer.tsx:509
|
||||
msgid "Opens list of invite codes"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/ChangeHandle.tsx:279
|
||||
msgid "Opens modal for using custom domain"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:563
|
||||
#: src/view/screens/Settings.tsx:551
|
||||
msgid "Opens moderation settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:519
|
||||
#: src/view/screens/Settings.tsx:507
|
||||
msgid "Opens screen with all saved feeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:586
|
||||
#: src/view/screens/Settings.tsx:574
|
||||
msgid "Opens the app password settings page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:478
|
||||
#: src/view/screens/Settings.tsx:466
|
||||
msgid "Opens the home feed preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:669
|
||||
#: src/view/screens/Settings.tsx:657
|
||||
msgid "Opens the storybook page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:649
|
||||
#: src/view/screens/Settings.tsx:637
|
||||
msgid "Opens the system log page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:499
|
||||
#: src/view/screens/Settings.tsx:487
|
||||
msgid "Opens the threads preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:109
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:138
|
||||
msgid "Other account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1349,7 +1353,7 @@ msgstr ""
|
|||
msgid "Pictures meant for adults."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:79
|
||||
#: src/view/screens/SavedFeeds.tsx:89
|
||||
msgid "Pinned Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1415,7 +1419,7 @@ msgstr ""
|
|||
msgid "Prioritize Your Follows"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:75
|
||||
#: src/view/shell/desktop/RightNav.tsx:76
|
||||
msgid "Privacy"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1434,7 +1438,7 @@ msgstr ""
|
|||
msgid "Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:794
|
||||
#: src/view/screens/Settings.tsx:782
|
||||
msgid "Protect your account by verifying your email."
|
||||
msgstr ""
|
||||
|
||||
|
@ -1476,7 +1480,7 @@ msgstr ""
|
|||
msgid "Remove"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:92
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:108
|
||||
msgid "Remove {0} from my feeds?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1484,11 +1488,11 @@ msgstr ""
|
|||
msgid "Remove account"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:106
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:118
|
||||
msgid "Remove feed"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:91
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:107
|
||||
#: src/view/screens/ProfileFeed.tsx:278
|
||||
msgid "Remove from my feeds"
|
||||
msgstr ""
|
||||
|
@ -1501,7 +1505,7 @@ msgstr ""
|
|||
msgid "Remove image preview"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:107
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:119
|
||||
msgid "Remove this feed from your saved feeds?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1565,7 +1569,7 @@ msgstr ""
|
|||
msgid "Reset code"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:691
|
||||
#: src/view/screens/Settings.tsx:679
|
||||
msgid "Reset onboarding state"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1573,15 +1577,15 @@ msgstr ""
|
|||
msgid "Reset password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:681
|
||||
#: src/view/screens/Settings.tsx:669
|
||||
msgid "Reset preferences state"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:689
|
||||
#: src/view/screens/Settings.tsx:677
|
||||
msgid "Resets the onboarding state"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:679
|
||||
#: src/view/screens/Settings.tsx:667
|
||||
msgid "Resets the preferences state"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1628,7 +1632,7 @@ msgstr ""
|
|||
msgid "Save image crop"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:105
|
||||
#: src/view/screens/SavedFeeds.tsx:122
|
||||
msgid "Saved Feeds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1726,7 +1730,7 @@ msgstr ""
|
|||
msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:280
|
||||
#: src/view/screens/Settings.tsx:279
|
||||
#: src/view/shell/desktop/LeftNav.tsx:436
|
||||
#: src/view/shell/Drawer.tsx:380
|
||||
#: src/view/shell/Drawer.tsx:381
|
||||
|
@ -1751,7 +1755,7 @@ msgstr ""
|
|||
#~ msgid "Share link"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:319
|
||||
#: src/view/screens/Settings.tsx:318
|
||||
msgid "Show"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1795,11 +1799,11 @@ msgstr ""
|
|||
msgid "Sign In"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:37
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:44
|
||||
msgid "Sign in as {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:94
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:118
|
||||
#: src/view/com/auth/login/Login.tsx:100
|
||||
msgid "Sign in as..."
|
||||
msgstr ""
|
||||
|
@ -1823,7 +1827,7 @@ msgstr ""
|
|||
msgid "Sign up or sign in to join the conversation"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:330
|
||||
#: src/view/screens/Settings.tsx:329
|
||||
msgid "Signed in as"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1848,11 +1852,11 @@ msgstr ""
|
|||
msgid "Staging"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:735
|
||||
#: src/view/screens/Settings.tsx:723
|
||||
msgid "Status page"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:671
|
||||
#: src/view/screens/Settings.tsx:659
|
||||
msgid "Storybook"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1881,7 +1885,7 @@ msgstr ""
|
|||
#~ msgid "System"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:651
|
||||
#: src/view/screens/Settings.tsx:639
|
||||
msgid "System log"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1889,7 +1893,7 @@ msgstr ""
|
|||
msgid "Tall"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:84
|
||||
#: src/view/shell/desktop/RightNav.tsx:85
|
||||
msgid "Terms"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1962,7 +1966,7 @@ msgid "This warning is only available for posts with media attached."
|
|||
msgstr ""
|
||||
|
||||
#: src/view/screens/PreferencesThreads.tsx:53
|
||||
#: src/view/screens/Settings.tsx:508
|
||||
#: src/view/screens/Settings.tsx:496
|
||||
msgid "Thread Preferences"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2069,15 +2073,15 @@ msgstr ""
|
|||
msgid "Users"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:755
|
||||
#: src/view/screens/Settings.tsx:743
|
||||
msgid "Verify email"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:780
|
||||
#: src/view/screens/Settings.tsx:768
|
||||
msgid "Verify my email"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:789
|
||||
#: src/view/screens/Settings.tsx:777
|
||||
msgid "Verify My Email"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2102,6 +2106,10 @@ msgstr ""
|
|||
msgid "We're so excited to have you join us!"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:98
|
||||
msgid "We're sorry, but this content is not viewable without a Bluesky account."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:236
|
||||
msgid "We're sorry, but your search could not be completed. Please try again in a few minutes."
|
||||
msgstr ""
|
||||
|
@ -2157,7 +2165,7 @@ msgstr ""
|
|||
msgid "You don't have any invite codes yet! We'll send you some when you've been on Bluesky for a little longer."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:92
|
||||
#: src/view/screens/SavedFeeds.tsx:102
|
||||
msgid "You don't have any pinned feeds."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2165,7 +2173,7 @@ msgstr ""
|
|||
msgid "You don't have any saved feeds!"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:118
|
||||
#: src/view/screens/SavedFeeds.tsx:135
|
||||
msgid "You don't have any saved feeds."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2228,12 +2236,6 @@ msgstr ""
|
|||
msgid "Your hosting provider"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:405
|
||||
#: src/view/shell/desktop/RightNav.tsx:127
|
||||
#: src/view/shell/Drawer.tsx:523
|
||||
msgid "Your invite codes are hidden when logged in using an App Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/onboarding/WelcomeMobile.tsx:59
|
||||
msgid "Your posts, likes, and blocks are public. Mutes are private."
|
||||
msgstr ""
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -21,12 +21,6 @@ msgstr ""
|
|||
#~ msgid ". This warning is only available for posts with media attached."
|
||||
#~ msgstr "यह चेतावनी केवल मीडिया वाले पोस्ट के लिए उपलब्ध है।"
|
||||
|
||||
#: src/view/screens/Settings.tsx:410
|
||||
#: src/view/shell/desktop/RightNav.tsx:158
|
||||
#: src/view/shell/Drawer.tsx:527
|
||||
msgid "{0, plural, one {# invite code available} other {# invite codes available}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/Repost.tsx:44
|
||||
msgid "{0}"
|
||||
msgstr "{0}"
|
||||
|
@ -35,11 +29,6 @@ msgstr "{0}"
|
|||
msgid "{0} {purposeLabel} List"
|
||||
msgstr "{0} {purposeLabel} सूची"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:141
|
||||
#: src/view/shell/Drawer.tsx:504
|
||||
msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:86
|
||||
msgid "{message}"
|
||||
msgstr ""
|
||||
|
@ -61,12 +50,12 @@ msgid "A new version of the app is available. Please update to continue using th
|
|||
msgstr "ऐप का एक नया संस्करण उपलब्ध है. कृपया ऐप का उपयोग जारी रखने के लिए अपडेट करें।"
|
||||
|
||||
#: src/view/com/modals/EditImage.tsx:299
|
||||
#: src/view/screens/Settings.tsx:422
|
||||
#: src/view/screens/Settings.tsx:410
|
||||
msgid "Accessibility"
|
||||
msgstr "प्रवेर्शयोग्यता"
|
||||
|
||||
#: src/view/com/auth/login/LoginForm.tsx:161
|
||||
#: src/view/screens/Settings.tsx:289
|
||||
#: src/view/screens/Settings.tsx:288
|
||||
msgid "Account"
|
||||
msgstr "अकाउंट"
|
||||
|
||||
|
@ -88,8 +77,8 @@ msgstr "सामग्री चेतावनी जोड़ें"
|
|||
msgid "Add a user to this list"
|
||||
msgstr "इस सूची में किसी को जोड़ें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:358
|
||||
#: src/view/screens/Settings.tsx:367
|
||||
#: src/view/screens/Settings.tsx:357
|
||||
#: src/view/screens/Settings.tsx:366
|
||||
msgid "Add account"
|
||||
msgstr "अकाउंट जोड़ें"
|
||||
|
||||
|
@ -140,7 +129,7 @@ msgstr "पसंद की संख्या को समायोजित
|
|||
msgid "Adult Content"
|
||||
msgstr "वयस्क सामग्री"
|
||||
|
||||
#: src/view/screens/Settings.tsx:574
|
||||
#: src/view/screens/Settings.tsx:562
|
||||
msgid "Advanced"
|
||||
msgstr "विकसित"
|
||||
|
||||
|
@ -172,7 +161,7 @@ msgstr "और"
|
|||
msgid "App Language"
|
||||
msgstr "ऐप भाषा"
|
||||
|
||||
#: src/view/screens/Settings.tsx:594
|
||||
#: src/view/screens/Settings.tsx:582
|
||||
msgid "App passwords"
|
||||
msgstr "ऐप पासवर्ड"
|
||||
|
||||
|
@ -180,7 +169,7 @@ msgstr "ऐप पासवर्ड"
|
|||
msgid "App Passwords"
|
||||
msgstr "ऐप पासवर्ड"
|
||||
|
||||
#: src/view/screens/Settings.tsx:437
|
||||
#: src/view/screens/Settings.tsx:425
|
||||
msgid "Appearance"
|
||||
msgstr "दिखावट"
|
||||
|
||||
|
@ -205,7 +194,7 @@ msgid "Artistic or non-erotic nudity."
|
|||
msgstr "कलात्मक या गैर-कामुक नग्नता।।"
|
||||
|
||||
#: src/view/com/auth/create/CreateAccount.tsx:145
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:122
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:151
|
||||
#: src/view/com/auth/login/ForgotPasswordForm.tsx:166
|
||||
#: src/view/com/auth/login/LoginForm.tsx:251
|
||||
#: src/view/com/auth/login/SetNewPasswordForm.tsx:148
|
||||
|
@ -217,7 +206,7 @@ msgstr "कलात्मक या गैर-कामुक नग्नत
|
|||
msgid "Back"
|
||||
msgstr "वापस"
|
||||
|
||||
#: src/view/screens/Settings.tsx:466
|
||||
#: src/view/screens/Settings.tsx:454
|
||||
msgid "Basics"
|
||||
msgstr "मूल बातें"
|
||||
|
||||
|
@ -226,7 +215,7 @@ msgstr "मूल बातें"
|
|||
msgid "Birthday"
|
||||
msgstr "जन्मदिन"
|
||||
|
||||
#: src/view/screens/Settings.tsx:315
|
||||
#: src/view/screens/Settings.tsx:314
|
||||
msgid "Birthday:"
|
||||
msgstr "जन्मदिन:"
|
||||
|
||||
|
@ -291,7 +280,7 @@ msgstr "ब्लूस्की एक स्वस्थ समुदाय
|
|||
msgid "Bluesky.Social"
|
||||
msgstr "Bluesky.Social"
|
||||
|
||||
#: src/view/screens/Settings.tsx:723
|
||||
#: src/view/screens/Settings.tsx:711
|
||||
msgid "Build version {0} {1}"
|
||||
msgstr "Build version {0} {1}"
|
||||
|
||||
|
@ -359,12 +348,12 @@ msgstr "खोज मत करो"
|
|||
msgid "Cancel waitlist signup"
|
||||
msgstr "प्रतीक्षा सूची पंजीकरण मत करो"
|
||||
|
||||
#: src/view/screens/Settings.tsx:309
|
||||
#: src/view/screens/Settings.tsx:308
|
||||
msgid "Change"
|
||||
msgstr "परिवर्तन"
|
||||
|
||||
#: src/view/screens/Settings.tsx:606
|
||||
#: src/view/screens/Settings.tsx:615
|
||||
#: src/view/screens/Settings.tsx:594
|
||||
#: src/view/screens/Settings.tsx:603
|
||||
msgid "Change handle"
|
||||
msgstr "हैंडल बदलें"
|
||||
|
||||
|
@ -404,19 +393,19 @@ msgstr "उन एल्गोरिदम का चयन करें जो
|
|||
msgid "Choose your password"
|
||||
msgstr "अपना पासवर्ड चुनें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:699
|
||||
#: src/view/screens/Settings.tsx:687
|
||||
msgid "Clear all legacy storage data"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:701
|
||||
#: src/view/screens/Settings.tsx:689
|
||||
msgid "Clear all legacy storage data (restart after this)"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:711
|
||||
#: src/view/screens/Settings.tsx:699
|
||||
msgid "Clear all storage data"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:713
|
||||
#: src/view/screens/Settings.tsx:701
|
||||
msgid "Clear all storage data (restart after this)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -557,7 +546,7 @@ msgstr "बनाया गया {0}"
|
|||
msgid "Custom domain"
|
||||
msgstr "कस्टम डोमेन"
|
||||
|
||||
#: src/view/screens/Settings.tsx:620
|
||||
#: src/view/screens/Settings.tsx:608
|
||||
msgid "Danger Zone"
|
||||
msgstr "खतरा क्षेत्र"
|
||||
|
||||
|
@ -565,7 +554,7 @@ msgstr "खतरा क्षेत्र"
|
|||
#~ msgid "Dark"
|
||||
#~ msgstr "डार्क मोड"
|
||||
|
||||
#: src/view/screens/Settings.tsx:627
|
||||
#: src/view/screens/Settings.tsx:615
|
||||
msgid "Delete account"
|
||||
msgstr "खाता हटाएं"
|
||||
|
||||
|
@ -587,7 +576,7 @@ msgstr "सूची हटाएँ"
|
|||
msgid "Delete my account"
|
||||
msgstr "मेरा खाता हटाएं"
|
||||
|
||||
#: src/view/screens/Settings.tsx:637
|
||||
#: src/view/screens/Settings.tsx:625
|
||||
msgid "Delete my account…"
|
||||
msgstr "मेरा खाता हटाएं…"
|
||||
|
||||
|
@ -614,7 +603,7 @@ msgstr "विवरण"
|
|||
msgid "Dev Server"
|
||||
msgstr "देव सर्वर"
|
||||
|
||||
#: src/view/screens/Settings.tsx:642
|
||||
#: src/view/screens/Settings.tsx:630
|
||||
msgid "Developer Tools"
|
||||
msgstr "डेवलपर उपकरण"
|
||||
|
||||
|
@ -673,7 +662,7 @@ msgid "Edit list details"
|
|||
msgstr "सूची विवरण संपादित करें"
|
||||
|
||||
#: src/view/screens/Feeds.tsx:367
|
||||
#: src/view/screens/SavedFeeds.tsx:75
|
||||
#: src/view/screens/SavedFeeds.tsx:85
|
||||
msgid "Edit My Feeds"
|
||||
msgstr "मेरी फ़ीड संपादित करें"
|
||||
|
||||
|
@ -708,7 +697,7 @@ msgstr "ईमेल"
|
|||
msgid "Email Updated"
|
||||
msgstr "ईमेल अपडेट किया गया"
|
||||
|
||||
#: src/view/screens/Settings.tsx:293
|
||||
#: src/view/screens/Settings.tsx:292
|
||||
msgid "Email:"
|
||||
msgstr "ईमेल:"
|
||||
|
||||
|
@ -753,7 +742,7 @@ msgstr "ऑल्ट टेक्स्ट"
|
|||
msgid "Failed to load recommended feeds"
|
||||
msgstr "अनुशंसित फ़ीड लोड करने में विफल"
|
||||
|
||||
#: src/view/screens/Feeds.tsx:558
|
||||
#: src/view/screens/Feeds.tsx:559
|
||||
msgid "Feed offline"
|
||||
msgstr "फ़ीड ऑफ़लाइन है"
|
||||
|
||||
|
@ -761,12 +750,12 @@ msgstr "फ़ीड ऑफ़लाइन है"
|
|||
msgid "Feed Preferences"
|
||||
msgstr "फ़ीड प्राथमिकता"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:64
|
||||
#: src/view/shell/desktop/RightNav.tsx:65
|
||||
#: src/view/shell/Drawer.tsx:411
|
||||
msgid "Feedback"
|
||||
msgstr "प्रतिक्रिया"
|
||||
|
||||
#: src/view/screens/Feeds.tsx:474
|
||||
#: src/view/screens/Feeds.tsx:475
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:169
|
||||
#: src/view/shell/desktop/LeftNav.tsx:342
|
||||
#: src/view/shell/Drawer.tsx:328
|
||||
|
@ -778,7 +767,7 @@ msgstr "सभी फ़ीड"
|
|||
msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting."
|
||||
msgstr "सामग्री को व्यवस्थित करने के लिए उपयोगकर्ताओं द्वारा फ़ीड बनाए जाते हैं। कुछ फ़ीड चुनें जो आपको दिलचस्प लगें।"
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:132
|
||||
#: src/view/screens/SavedFeeds.tsx:156
|
||||
msgid "Feeds are custom algorithms that users build with a little coding expertise. <0/> for more information."
|
||||
msgstr "फ़ीड कस्टम एल्गोरिदम हैं जिन्हें उपयोगकर्ता थोड़ी कोडिंग विशेषज्ञता के साथ बनाते हैं। <0/> अधिक जानकारी के लिए."
|
||||
|
||||
|
@ -876,7 +865,7 @@ msgstr "अगला"
|
|||
msgid "Handle"
|
||||
msgstr "हैंडल"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:93
|
||||
#: src/view/shell/desktop/RightNav.tsx:94
|
||||
#: src/view/shell/Drawer.tsx:421
|
||||
msgid "Help"
|
||||
msgstr "सहायता"
|
||||
|
@ -893,6 +882,26 @@ msgstr "इसे छिपाएं"
|
|||
msgid "Hide user list"
|
||||
msgstr "उपयोगकर्ता सूची छुपाएँ"
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:101
|
||||
msgid "Hmm, some kind of issue occured when contacting the feed server. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:89
|
||||
msgid "Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:95
|
||||
msgid "Hmm, the feed server appears to be offline. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:92
|
||||
msgid "Hmm, the feed server gave a bad response. Please let the feed owner know about this issue."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:86
|
||||
msgid "Hmmm, we're having trouble finding this feed. It may have been deleted."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:125
|
||||
#: src/view/shell/desktop/LeftNav.tsx:306
|
||||
#: src/view/shell/Drawer.tsx:275
|
||||
|
@ -900,9 +909,9 @@ msgstr "उपयोगकर्ता सूची छुपाएँ"
|
|||
msgid "Home"
|
||||
msgstr "होम फीड"
|
||||
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:71
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:99
|
||||
#: src/view/screens/PreferencesHomeFeed.tsx:95
|
||||
#: src/view/screens/Settings.tsx:486
|
||||
#: src/view/screens/Settings.tsx:474
|
||||
msgid "Home Feed Preferences"
|
||||
msgstr "होम फ़ीड प्राथमिकताएं"
|
||||
|
||||
|
@ -945,12 +954,12 @@ msgstr "छवि विकल्प"
|
|||
msgid "Invalid username or password"
|
||||
msgstr "अवैध उपयोगकर्ता नाम या पासवर्ड"
|
||||
|
||||
#: src/view/screens/Settings.tsx:386
|
||||
#: src/view/screens/Settings.tsx:385
|
||||
msgid "Invite"
|
||||
msgstr "आमंत्रण भेजो"
|
||||
|
||||
#: src/view/com/modals/InviteCodes.tsx:91
|
||||
#: src/view/screens/Settings.tsx:374
|
||||
#: src/view/screens/Settings.tsx:373
|
||||
msgid "Invite a Friend"
|
||||
msgstr "एक दोस्त को आमंत्रित करें"
|
||||
|
||||
|
@ -983,7 +992,7 @@ msgstr "अपनी भाषा चुने"
|
|||
msgid "Language Settings"
|
||||
msgstr "भाषा सेटिंग्स"
|
||||
|
||||
#: src/view/screens/Settings.tsx:546
|
||||
#: src/view/screens/Settings.tsx:534
|
||||
msgid "Languages"
|
||||
msgstr "भाषा"
|
||||
|
||||
|
@ -1023,7 +1032,7 @@ msgstr "चित्र पुस्तकालय"
|
|||
#~ msgid "Light"
|
||||
#~ msgstr "लाइट मोड"
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:625
|
||||
#: src/view/screens/ProfileFeed.tsx:626
|
||||
msgid "Like this feed"
|
||||
msgstr "इस फ़ीड को लाइक करो"
|
||||
|
||||
|
@ -1051,7 +1060,7 @@ msgstr "सूची"
|
|||
msgid "Load more posts"
|
||||
msgstr "अधिक पोस्ट लोड करें"
|
||||
|
||||
#: src/view/screens/Notifications.tsx:120
|
||||
#: src/view/screens/Notifications.tsx:130
|
||||
msgid "Load new notifications"
|
||||
msgstr "नई सूचनाएं लोड करें"
|
||||
|
||||
|
@ -1067,11 +1076,11 @@ msgstr ""
|
|||
msgid "Local dev server"
|
||||
msgstr "स्थानीय देव सर्वर"
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:104
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:133
|
||||
msgid "Login to account that is not listed"
|
||||
msgstr "उस खाते में लॉग इन करें जो सूचीबद्ध नहीं है"
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:477
|
||||
#: src/view/screens/ProfileFeed.tsx:478
|
||||
msgid "Looks like this feed is only available to users with a Bluesky account. Please sign up or sign in to view this feed!"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1084,7 +1093,7 @@ msgid "Menu"
|
|||
msgstr "मेनू"
|
||||
|
||||
#: src/view/screens/Moderation.tsx:51
|
||||
#: src/view/screens/Settings.tsx:568
|
||||
#: src/view/screens/Settings.tsx:556
|
||||
#: src/view/shell/desktop/LeftNav.tsx:400
|
||||
#: src/view/shell/Drawer.tsx:346
|
||||
#: src/view/shell/Drawer.tsx:347
|
||||
|
@ -1100,7 +1109,7 @@ msgid "More feeds"
|
|||
msgstr "अधिक फ़ीड"
|
||||
|
||||
#: src/view/com/profile/ProfileHeader.tsx:506
|
||||
#: src/view/screens/ProfileFeed.tsx:367
|
||||
#: src/view/screens/ProfileFeed.tsx:368
|
||||
#: src/view/screens/ProfileList.tsx:506
|
||||
msgid "More options"
|
||||
msgstr "अधिक विकल्प"
|
||||
|
@ -1153,7 +1162,7 @@ msgstr "मेरी फ़ीड"
|
|||
msgid "My Profile"
|
||||
msgstr "मेरी प्रोफाइल"
|
||||
|
||||
#: src/view/screens/Settings.tsx:525
|
||||
#: src/view/screens/Settings.tsx:513
|
||||
msgid "My Saved Feeds"
|
||||
msgstr "मेरी फ़ीड"
|
||||
|
||||
|
@ -1171,9 +1180,9 @@ msgid "New"
|
|||
msgstr "नया"
|
||||
|
||||
#: src/view/com/feeds/FeedPage.tsx:187
|
||||
#: src/view/screens/Feeds.tsx:509
|
||||
#: src/view/screens/Profile.tsx:380
|
||||
#: src/view/screens/ProfileFeed.tsx:447
|
||||
#: src/view/screens/Feeds.tsx:510
|
||||
#: src/view/screens/Profile.tsx:381
|
||||
#: src/view/screens/ProfileFeed.tsx:448
|
||||
#: src/view/screens/ProfileList.tsx:199
|
||||
#: src/view/screens/ProfileList.tsx:231
|
||||
#: src/view/shell/desktop/LeftNav.tsx:255
|
||||
|
@ -1204,7 +1213,7 @@ msgstr "अगली फोटो"
|
|||
msgid "No"
|
||||
msgstr "नहीं"
|
||||
|
||||
#: src/view/screens/ProfileFeed.tsx:618
|
||||
#: src/view/screens/ProfileFeed.tsx:619
|
||||
#: src/view/screens/ProfileList.tsx:632
|
||||
msgid "No description"
|
||||
msgstr "कोई विवरण नहीं"
|
||||
|
@ -1213,7 +1222,7 @@ msgstr "कोई विवरण नहीं"
|
|||
msgid "No result"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Feeds.tsx:451
|
||||
#: src/view/screens/Feeds.tsx:452
|
||||
msgid "No results found for \"{query}\""
|
||||
msgstr "\"{query}\" के लिए कोई परिणाम नहीं मिला"
|
||||
|
||||
|
@ -1238,8 +1247,8 @@ msgstr ""
|
|||
msgid "Not Applicable."
|
||||
msgstr "लागू नहीं।"
|
||||
|
||||
#: src/view/screens/Notifications.tsx:87
|
||||
#: src/view/screens/Notifications.tsx:111
|
||||
#: src/view/screens/Notifications.tsx:97
|
||||
#: src/view/screens/Notifications.tsx:121
|
||||
#: src/view/shell/bottom-bar/BottomBar.tsx:196
|
||||
#: src/view/shell/desktop/LeftNav.tsx:364
|
||||
#: src/view/shell/Drawer.tsx:299
|
||||
|
@ -1259,52 +1268,47 @@ msgstr "ठीक है"
|
|||
msgid "One or more images is missing alt text."
|
||||
msgstr "एक या अधिक छवियाँ alt पाठ याद आती हैं।।"
|
||||
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:51
|
||||
#: src/view/com/pager/FeedsTabBarMobile.tsx:79
|
||||
msgid "Open navigation"
|
||||
msgstr "ओपन नेविगेशन"
|
||||
|
||||
#: src/view/screens/Settings.tsx:538
|
||||
#: src/view/screens/Settings.tsx:526
|
||||
msgid "Opens configurable language settings"
|
||||
msgstr "भाषा सेटिंग्स खोलें"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:146
|
||||
#: src/view/shell/Drawer.tsx:509
|
||||
msgid "Opens list of invite codes"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/modals/ChangeHandle.tsx:279
|
||||
msgid "Opens modal for using custom domain"
|
||||
msgstr "कस्टम डोमेन का उपयोग करने के लिए मोडल खोलें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:563
|
||||
#: src/view/screens/Settings.tsx:551
|
||||
msgid "Opens moderation settings"
|
||||
msgstr "मॉडरेशन सेटिंग्स खोलें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:519
|
||||
#: src/view/screens/Settings.tsx:507
|
||||
msgid "Opens screen with all saved feeds"
|
||||
msgstr "सभी बचाया फ़ीड के साथ स्क्रीन खोलें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:586
|
||||
#: src/view/screens/Settings.tsx:574
|
||||
msgid "Opens the app password settings page"
|
||||
msgstr "ऐप पासवर्ड सेटिंग पेज खोलें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:478
|
||||
#: src/view/screens/Settings.tsx:466
|
||||
msgid "Opens the home feed preferences"
|
||||
msgstr "होम फीड वरीयताओं को खोलता है"
|
||||
|
||||
#: src/view/screens/Settings.tsx:669
|
||||
#: src/view/screens/Settings.tsx:657
|
||||
msgid "Opens the storybook page"
|
||||
msgstr "स्टोरीबुक पेज खोलें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:649
|
||||
#: src/view/screens/Settings.tsx:637
|
||||
msgid "Opens the system log page"
|
||||
msgstr "सिस्टम लॉग पेज खोलें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:499
|
||||
#: src/view/screens/Settings.tsx:487
|
||||
msgid "Opens the threads preferences"
|
||||
msgstr "धागे वरीयताओं को खोलता है"
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:109
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:138
|
||||
msgid "Other account"
|
||||
msgstr "अन्य खाता"
|
||||
|
||||
|
@ -1341,7 +1345,7 @@ msgstr "पासवर्ड अद्यतन!"
|
|||
msgid "Pictures meant for adults."
|
||||
msgstr "चित्र वयस्कों के लिए थे।।"
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:79
|
||||
#: src/view/screens/SavedFeeds.tsx:89
|
||||
msgid "Pinned Feeds"
|
||||
msgstr "पिन किया गया फ़ीड"
|
||||
|
||||
|
@ -1407,7 +1411,7 @@ msgstr "प्राथमिक भाषा"
|
|||
msgid "Prioritize Your Follows"
|
||||
msgstr "अपने फ़ॉलोअर्स को प्राथमिकता दें"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:75
|
||||
#: src/view/shell/desktop/RightNav.tsx:76
|
||||
msgid "Privacy"
|
||||
msgstr "गोपनीयता"
|
||||
|
||||
|
@ -1426,7 +1430,7 @@ msgstr "प्रसंस्करण..."
|
|||
msgid "Profile"
|
||||
msgstr "प्रोफ़ाइल"
|
||||
|
||||
#: src/view/screens/Settings.tsx:794
|
||||
#: src/view/screens/Settings.tsx:782
|
||||
msgid "Protect your account by verifying your email."
|
||||
msgstr "अपने ईमेल को सत्यापित करके अपने खाते को सुरक्षित रखें।।"
|
||||
|
||||
|
@ -1468,7 +1472,7 @@ msgstr "अनुशंसित लोग"
|
|||
msgid "Remove"
|
||||
msgstr "निकालें"
|
||||
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:92
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:108
|
||||
msgid "Remove {0} from my feeds?"
|
||||
msgstr "मेरे फ़ीड से {0} हटाएं?"
|
||||
|
||||
|
@ -1476,11 +1480,11 @@ msgstr "मेरे फ़ीड से {0} हटाएं?"
|
|||
msgid "Remove account"
|
||||
msgstr "खाता हटाएं"
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:106
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:118
|
||||
msgid "Remove feed"
|
||||
msgstr "फ़ीड हटाएँ"
|
||||
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:91
|
||||
#: src/view/com/feeds/FeedSourceCard.tsx:107
|
||||
#: src/view/screens/ProfileFeed.tsx:278
|
||||
msgid "Remove from my feeds"
|
||||
msgstr "मेरे फ़ीड से हटाएँ"
|
||||
|
@ -1493,7 +1497,7 @@ msgstr "छवि निकालें"
|
|||
msgid "Remove image preview"
|
||||
msgstr "छवि पूर्वावलोकन निकालें"
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:107
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:119
|
||||
msgid "Remove this feed from your saved feeds?"
|
||||
msgstr "इस फ़ीड को सहेजे गए फ़ीड से हटा दें?"
|
||||
|
||||
|
@ -1557,7 +1561,7 @@ msgstr "इस प्रदाता के लिए आवश्यक"
|
|||
msgid "Reset code"
|
||||
msgstr "कोड रीसेट करें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:691
|
||||
#: src/view/screens/Settings.tsx:679
|
||||
msgid "Reset onboarding state"
|
||||
msgstr "ऑनबोर्डिंग स्टेट को रीसेट करें"
|
||||
|
||||
|
@ -1565,15 +1569,15 @@ msgstr "ऑनबोर्डिंग स्टेट को रीसेट
|
|||
msgid "Reset password"
|
||||
msgstr "पासवर्ड रीसेट"
|
||||
|
||||
#: src/view/screens/Settings.tsx:681
|
||||
#: src/view/screens/Settings.tsx:669
|
||||
msgid "Reset preferences state"
|
||||
msgstr "प्राथमिकताओं को रीसेट करें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:689
|
||||
#: src/view/screens/Settings.tsx:677
|
||||
msgid "Resets the onboarding state"
|
||||
msgstr "ऑनबोर्डिंग स्टेट को रीसेट करें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:679
|
||||
#: src/view/screens/Settings.tsx:667
|
||||
msgid "Resets the preferences state"
|
||||
msgstr "प्राथमिकताओं की स्थिति को रीसेट करें"
|
||||
|
||||
|
@ -1620,7 +1624,7 @@ msgstr "बदलाव सेव करो"
|
|||
msgid "Save image crop"
|
||||
msgstr "फोटो बदलाव सेव करो"
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:105
|
||||
#: src/view/screens/SavedFeeds.tsx:122
|
||||
msgid "Saved Feeds"
|
||||
msgstr "सहेजे गए फ़ीड"
|
||||
|
||||
|
@ -1718,7 +1722,7 @@ msgstr "इस सेटिंग को \"हाँ\" में सेट क
|
|||
msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature."
|
||||
msgstr "इस सेटिंग को अपने निम्नलिखित फ़ीड में अपने सहेजे गए फ़ीड के नमूने दिखाने के लिए \"हाँ\" पर सेट करें। यह एक प्रयोगात्मक विशेषता है।।"
|
||||
|
||||
#: src/view/screens/Settings.tsx:280
|
||||
#: src/view/screens/Settings.tsx:279
|
||||
#: src/view/shell/desktop/LeftNav.tsx:436
|
||||
#: src/view/shell/Drawer.tsx:380
|
||||
#: src/view/shell/Drawer.tsx:381
|
||||
|
@ -1743,7 +1747,7 @@ msgstr ""
|
|||
#~ msgid "Share link"
|
||||
#~ msgstr "लिंक शेयर करें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:319
|
||||
#: src/view/screens/Settings.tsx:318
|
||||
msgid "Show"
|
||||
msgstr "दिखाओ"
|
||||
|
||||
|
@ -1787,11 +1791,11 @@ msgstr "साइन इन करें"
|
|||
msgid "Sign In"
|
||||
msgstr "साइन इन करें"
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:37
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:44
|
||||
msgid "Sign in as {0}"
|
||||
msgstr "{0} के रूप में साइन इन करें"
|
||||
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:94
|
||||
#: src/view/com/auth/login/ChooseAccountForm.tsx:118
|
||||
#: src/view/com/auth/login/Login.tsx:100
|
||||
msgid "Sign in as..."
|
||||
msgstr "... के रूप में साइन इन करें"
|
||||
|
@ -1815,7 +1819,7 @@ msgstr ""
|
|||
msgid "Sign up or sign in to join the conversation"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Settings.tsx:330
|
||||
#: src/view/screens/Settings.tsx:329
|
||||
msgid "Signed in as"
|
||||
msgstr "आपने इस रूप में साइन इन करा है:"
|
||||
|
||||
|
@ -1840,11 +1844,11 @@ msgstr "स्क्वायर"
|
|||
msgid "Staging"
|
||||
msgstr "स्टेजिंग"
|
||||
|
||||
#: src/view/screens/Settings.tsx:735
|
||||
#: src/view/screens/Settings.tsx:723
|
||||
msgid "Status page"
|
||||
msgstr "स्थिति पृष्ठ"
|
||||
|
||||
#: src/view/screens/Settings.tsx:671
|
||||
#: src/view/screens/Settings.tsx:659
|
||||
msgid "Storybook"
|
||||
msgstr "Storybook"
|
||||
|
||||
|
@ -1873,7 +1877,7 @@ msgstr "खाते बदलें"
|
|||
#~ msgid "System"
|
||||
#~ msgstr "प्रणाली"
|
||||
|
||||
#: src/view/screens/Settings.tsx:651
|
||||
#: src/view/screens/Settings.tsx:639
|
||||
msgid "System log"
|
||||
msgstr "सिस्टम लॉग"
|
||||
|
||||
|
@ -1881,7 +1885,7 @@ msgstr "सिस्टम लॉग"
|
|||
msgid "Tall"
|
||||
msgstr "लंबा"
|
||||
|
||||
#: src/view/shell/desktop/RightNav.tsx:84
|
||||
#: src/view/shell/desktop/RightNav.tsx:85
|
||||
msgid "Terms"
|
||||
msgstr "शर्तें"
|
||||
|
||||
|
@ -1954,7 +1958,7 @@ msgid "This warning is only available for posts with media attached."
|
|||
msgstr "यह चेतावनी केवल मीडिया संलग्न पोस्ट के लिए उपलब्ध है।"
|
||||
|
||||
#: src/view/screens/PreferencesThreads.tsx:53
|
||||
#: src/view/screens/Settings.tsx:508
|
||||
#: src/view/screens/Settings.tsx:496
|
||||
msgid "Thread Preferences"
|
||||
msgstr "थ्रेड प्राथमिकता"
|
||||
|
||||
|
@ -2061,15 +2065,15 @@ msgstr "यूजर नाम या ईमेल पता"
|
|||
msgid "Users"
|
||||
msgstr "यूजर लोग"
|
||||
|
||||
#: src/view/screens/Settings.tsx:755
|
||||
#: src/view/screens/Settings.tsx:743
|
||||
msgid "Verify email"
|
||||
msgstr "ईमेल सत्यापित करें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:780
|
||||
#: src/view/screens/Settings.tsx:768
|
||||
msgid "Verify my email"
|
||||
msgstr "मेरी ईमेल सत्यापित करें"
|
||||
|
||||
#: src/view/screens/Settings.tsx:789
|
||||
#: src/view/screens/Settings.tsx:777
|
||||
msgid "Verify My Email"
|
||||
msgstr "मेरी ईमेल सत्यापित करें"
|
||||
|
||||
|
@ -2094,6 +2098,10 @@ msgstr "साइट पर जाएं"
|
|||
msgid "We're so excited to have you join us!"
|
||||
msgstr "हम आपके हमारी सेवा में शामिल होने को लेकर बहुत उत्साहित हैं!"
|
||||
|
||||
#: src/view/com/posts/FeedErrorMessage.tsx:98
|
||||
msgid "We're sorry, but this content is not viewable without a Bluesky account."
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/Search/Search.tsx:236
|
||||
msgid "We're sorry, but your search could not be completed. Please try again in a few minutes."
|
||||
msgstr ""
|
||||
|
@ -2149,7 +2157,7 @@ msgstr "अब आप अपने नए पासवर्ड के साथ
|
|||
msgid "You don't have any invite codes yet! We'll send you some when you've been on Bluesky for a little longer."
|
||||
msgstr "आपके पास अभी तक कोई आमंत्रण कोड नहीं है! जब आप कुछ अधिक समय के लिए Bluesky पर रहेंगे तो हम आपको कुछ भेजेंगे।"
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:92
|
||||
#: src/view/screens/SavedFeeds.tsx:102
|
||||
msgid "You don't have any pinned feeds."
|
||||
msgstr "आपके पास कोई पिन किया हुआ फ़ीड नहीं है."
|
||||
|
||||
|
@ -2157,7 +2165,7 @@ msgstr "आपके पास कोई पिन किया हुआ फ़
|
|||
msgid "You don't have any saved feeds!"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/screens/SavedFeeds.tsx:118
|
||||
#: src/view/screens/SavedFeeds.tsx:135
|
||||
msgid "You don't have any saved feeds."
|
||||
msgstr "आपके पास कोई सहेजी गई फ़ीड नहीं है."
|
||||
|
||||
|
@ -2220,12 +2228,6 @@ msgstr "आपका पूरा हैंडल होगा"
|
|||
msgid "Your hosting provider"
|
||||
msgstr "आपका होस्टिंग प्रदाता"
|
||||
|
||||
#: src/view/screens/Settings.tsx:405
|
||||
#: src/view/shell/desktop/RightNav.tsx:127
|
||||
#: src/view/shell/Drawer.tsx:523
|
||||
msgid "Your invite codes are hidden when logged in using an App Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/view/com/auth/onboarding/WelcomeMobile.tsx:59
|
||||
msgid "Your posts, likes, and blocks are public. Mutes are private."
|
||||
msgstr "आपकी पोस्ट, पसंद और ब्लॉक सार्वजनिक हैं। म्यूट निजी हैं।।"
|
||||
|
|
|
@ -288,16 +288,13 @@ export class Logger {
|
|||
*/
|
||||
export const logger = new Logger()
|
||||
|
||||
/**
|
||||
* Report to console in dev, Sentry in prod, nothing in test.
|
||||
*/
|
||||
if (env.IS_DEV && !env.IS_TEST) {
|
||||
logger.addTransport(consoleTransport)
|
||||
|
||||
/**
|
||||
* Uncomment this to test Sentry in dev
|
||||
* Comment this out to disable Sentry transport in dev
|
||||
*/
|
||||
// logger.addTransport(sentryTransport);
|
||||
logger.addTransport(sentryTransport)
|
||||
} else if (env.IS_PROD) {
|
||||
// logger.addTransport(sentryTransport)
|
||||
logger.addTransport(sentryTransport)
|
||||
}
|
||||
|
|
|
@ -2,9 +2,13 @@ import {useMemo} from 'react'
|
|||
import {FeedTuner} from '#/lib/api/feed-manip'
|
||||
import {FeedDescriptor} from '../queries/post-feed'
|
||||
import {useLanguagePrefs} from './languages'
|
||||
import {usePreferencesQuery} from '../queries/preferences'
|
||||
import {useSession} from '../session'
|
||||
|
||||
export function useFeedTuners(feedDesc: FeedDescriptor) {
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
return useMemo(() => {
|
||||
if (feedDesc.startsWith('feedgen')) {
|
||||
|
@ -19,30 +23,30 @@ export function useFeedTuners(feedDesc: FeedDescriptor) {
|
|||
if (feedDesc === 'home' || feedDesc === 'following') {
|
||||
const feedTuners = []
|
||||
|
||||
if (false /*TODOthis.homeFeed.hideReposts*/) {
|
||||
if (preferences?.feedViewPrefs.hideReposts) {
|
||||
feedTuners.push(FeedTuner.removeReposts)
|
||||
} else {
|
||||
feedTuners.push(FeedTuner.dedupReposts)
|
||||
}
|
||||
|
||||
if (true /*TODOthis.homeFeed.hideReplies*/) {
|
||||
if (preferences?.feedViewPrefs.hideReplies) {
|
||||
feedTuners.push(FeedTuner.removeReplies)
|
||||
} /* TODO else {
|
||||
} else {
|
||||
feedTuners.push(
|
||||
FeedTuner.thresholdRepliesOnly({
|
||||
userDid: this.rootStore.session.data?.did || '',
|
||||
minLikes: this.homeFeed.hideRepliesByLikeCount,
|
||||
followedOnly: !!this.homeFeed.hideRepliesByUnfollowed,
|
||||
userDid: currentAccount?.did || '',
|
||||
minLikes: preferences?.feedViewPrefs.hideRepliesByLikeCount || 0,
|
||||
followedOnly: !!preferences?.feedViewPrefs.hideRepliesByUnfollowed,
|
||||
}),
|
||||
)
|
||||
}*/
|
||||
}
|
||||
|
||||
if (false /*TODOthis.homeFeed.hideQuotePosts*/) {
|
||||
if (preferences?.feedViewPrefs.hideQuotePosts) {
|
||||
feedTuners.push(FeedTuner.removeQuotePosts)
|
||||
}
|
||||
|
||||
return feedTuners
|
||||
}
|
||||
return []
|
||||
}, [feedDesc, langPrefs])
|
||||
}, [feedDesc, currentAccount, preferences, langPrefs])
|
||||
}
|
||||
|
|
|
@ -181,6 +181,9 @@ export function useIsFeedPublicQuery({uri}: {uri: string}) {
|
|||
|
||||
if (msg.includes('missing jwt')) {
|
||||
return false
|
||||
} else if (msg.includes('This feed requires being logged-in')) {
|
||||
// e.g. https://github.com/bluesky-social/atproto/blob/99ab1ae55c463e8d5321a1eaad07a175bdd56fea/packages/bsky/src/feed-gen/best-of-follows.ts#L13
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -243,13 +246,19 @@ const FOLLOWING_FEED_STUB: FeedSourceInfo = {
|
|||
likeUri: '',
|
||||
}
|
||||
|
||||
export function usePinnedFeedsInfos(): FeedSourceInfo[] {
|
||||
export function usePinnedFeedsInfos(): {
|
||||
feeds: FeedSourceInfo[]
|
||||
hasPinnedCustom: boolean
|
||||
} {
|
||||
const queryClient = useQueryClient()
|
||||
const [tabs, setTabs] = React.useState<FeedSourceInfo[]>([
|
||||
FOLLOWING_FEED_STUB,
|
||||
])
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
const pinnedFeedsKey = JSON.stringify(preferences?.feeds?.pinned)
|
||||
|
||||
const hasPinnedCustom = React.useMemo<boolean>(() => {
|
||||
return tabs.some(tab => tab !== FOLLOWING_FEED_STUB)
|
||||
}, [tabs])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!preferences?.feeds?.pinned) return
|
||||
|
@ -296,13 +305,7 @@ export function usePinnedFeedsInfos(): FeedSourceInfo[] {
|
|||
}
|
||||
|
||||
fetchFeedInfo()
|
||||
}, [
|
||||
queryClient,
|
||||
setTabs,
|
||||
preferences?.feeds?.pinned,
|
||||
// ensure we react to re-ordering
|
||||
pinnedFeedsKey,
|
||||
])
|
||||
}, [queryClient, setTabs, preferences?.feeds?.pinned])
|
||||
|
||||
return tabs
|
||||
return {feeds: tabs, hasPinnedCustom}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import {
|
|||
AppBskyGraphGetList,
|
||||
AppBskyGraphList,
|
||||
AppBskyGraphDefs,
|
||||
BskyAgent,
|
||||
} from '@atproto/api'
|
||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
|
@ -75,13 +74,9 @@ export function useListCreateMutation() {
|
|||
)
|
||||
|
||||
// wait for the appview to update
|
||||
await whenAppViewReady(
|
||||
getAgent(),
|
||||
res.uri,
|
||||
(v: AppBskyGraphGetList.Response) => {
|
||||
return typeof v?.data?.list.uri === 'string'
|
||||
},
|
||||
)
|
||||
await whenAppViewReady(res.uri, (v: AppBskyGraphGetList.Response) => {
|
||||
return typeof v?.data?.list.uri === 'string'
|
||||
})
|
||||
return res
|
||||
},
|
||||
onSuccess() {
|
||||
|
@ -142,16 +137,12 @@ export function useListMetadataMutation() {
|
|||
).data
|
||||
|
||||
// wait for the appview to update
|
||||
await whenAppViewReady(
|
||||
getAgent(),
|
||||
res.uri,
|
||||
(v: AppBskyGraphGetList.Response) => {
|
||||
const list = v.data.list
|
||||
return (
|
||||
list.name === record.name && list.description === record.description
|
||||
)
|
||||
},
|
||||
)
|
||||
await whenAppViewReady(res.uri, (v: AppBskyGraphGetList.Response) => {
|
||||
const list = v.data.list
|
||||
return (
|
||||
list.name === record.name && list.description === record.description
|
||||
)
|
||||
})
|
||||
return res
|
||||
},
|
||||
onSuccess(data, variables) {
|
||||
|
@ -216,13 +207,9 @@ export function useListDeleteMutation() {
|
|||
}
|
||||
|
||||
// wait for the appview to update
|
||||
await whenAppViewReady(
|
||||
getAgent(),
|
||||
uri,
|
||||
(v: AppBskyGraphGetList.Response) => {
|
||||
return !v?.success
|
||||
},
|
||||
)
|
||||
await whenAppViewReady(uri, (v: AppBskyGraphGetList.Response) => {
|
||||
return !v?.success
|
||||
})
|
||||
},
|
||||
onSuccess() {
|
||||
invalidateMyLists(queryClient)
|
||||
|
@ -271,7 +258,6 @@ export function useListBlockMutation() {
|
|||
}
|
||||
|
||||
async function whenAppViewReady(
|
||||
agent: BskyAgent,
|
||||
uri: string,
|
||||
fn: (res: AppBskyGraphGetList.Response) => boolean,
|
||||
) {
|
||||
|
@ -280,7 +266,7 @@ async function whenAppViewReady(
|
|||
1e3, // 1s delay between tries
|
||||
fn,
|
||||
() =>
|
||||
agent.app.bsky.graph.getList({
|
||||
getAgent().app.bsky.graph.getList({
|
||||
list: uri,
|
||||
limit: 1,
|
||||
}),
|
||||
|
|
|
@ -2,7 +2,6 @@ import {AppBskyGraphGetBlocks} from '@atproto/api'
|
|||
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
|
||||
|
||||
import {getAgent} from '#/state/session'
|
||||
import {STALE} from '#/state/queries'
|
||||
|
||||
export const RQKEY = () => ['my-blocked-accounts']
|
||||
type RQPageParam = string | undefined
|
||||
|
@ -15,7 +14,6 @@ export function useMyBlockedAccountsQuery() {
|
|||
QueryKey,
|
||||
RQPageParam
|
||||
>({
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
const res = await getAgent().app.bsky.graph.getBlocks({
|
||||
|
|
|
@ -2,7 +2,6 @@ import {AppBskyGraphGetMutes} from '@atproto/api'
|
|||
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
|
||||
|
||||
import {getAgent} from '#/state/session'
|
||||
import {STALE} from '#/state/queries'
|
||||
|
||||
export const RQKEY = () => ['my-muted-accounts']
|
||||
type RQPageParam = string | undefined
|
||||
|
@ -15,7 +14,6 @@ export function useMyMutedAccountsQuery() {
|
|||
QueryKey,
|
||||
RQPageParam
|
||||
>({
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
const res = await getAgent().app.bsky.graph.getMutes({
|
||||
|
|
|
@ -1,12 +1,22 @@
|
|||
import {
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedPost,
|
||||
AppBskyFeedRepost,
|
||||
AppBskyFeedLike,
|
||||
AppBskyNotificationListNotifications,
|
||||
BskyAgent,
|
||||
} from '@atproto/api'
|
||||
import chunk from 'lodash.chunk'
|
||||
/**
|
||||
* NOTE
|
||||
* The ./unread.ts API:
|
||||
*
|
||||
* - Provides a `checkUnread()` function to sync with the server,
|
||||
* - Periodically calls `checkUnread()`, and
|
||||
* - Caches the first page of notifications.
|
||||
*
|
||||
* IMPORTANT: This query uses ./unread.ts's cache as its first page,
|
||||
* IMPORTANT: which means the cache-freshness of this query is driven by the unread API.
|
||||
*
|
||||
* Follow these rules:
|
||||
*
|
||||
* 1. Call `checkUnread()` if you want to fetch latest in the background.
|
||||
* 2. Call `checkUnread({invalidate: true})` if you want latest to sync into this query's results immediately.
|
||||
* 3. Don't call this query's `refetch()` if you're trying to sync latest; call `checkUnread()` instead.
|
||||
*/
|
||||
|
||||
import {AppBskyFeedDefs} from '@atproto/api'
|
||||
import {
|
||||
useInfiniteQuery,
|
||||
InfiniteData,
|
||||
|
@ -14,50 +24,27 @@ import {
|
|||
useQueryClient,
|
||||
QueryClient,
|
||||
} from '@tanstack/react-query'
|
||||
import {getAgent} from '../../session'
|
||||
import {useModerationOpts} from '../preferences'
|
||||
import {shouldFilterNotif} from './util'
|
||||
import {useUnreadNotificationsApi} from './unread'
|
||||
import {fetchPage} from './util'
|
||||
import {FeedPage} from './types'
|
||||
import {useMutedThreads} from '#/state/muted-threads'
|
||||
import {precacheProfile as precacheResolvedUri} from '../resolve-uri'
|
||||
|
||||
const GROUPABLE_REASONS = ['like', 'repost', 'follow']
|
||||
export type {NotificationType, FeedNotification, FeedPage} from './types'
|
||||
|
||||
const PAGE_SIZE = 30
|
||||
const MS_1HR = 1e3 * 60 * 60
|
||||
const MS_2DAY = MS_1HR * 48
|
||||
|
||||
type RQPageParam = string | undefined
|
||||
type NotificationType =
|
||||
| 'post-like'
|
||||
| 'feedgen-like'
|
||||
| 'repost'
|
||||
| 'mention'
|
||||
| 'reply'
|
||||
| 'quote'
|
||||
| 'follow'
|
||||
| 'unknown'
|
||||
|
||||
export function RQKEY() {
|
||||
return ['notification-feed']
|
||||
}
|
||||
|
||||
export interface FeedNotification {
|
||||
_reactKey: string
|
||||
type: NotificationType
|
||||
notification: AppBskyNotificationListNotifications.Notification
|
||||
additional?: AppBskyNotificationListNotifications.Notification[]
|
||||
subjectUri?: string
|
||||
subject?: AppBskyFeedDefs.PostView
|
||||
}
|
||||
|
||||
export interface FeedPage {
|
||||
cursor: string | undefined
|
||||
items: FeedNotification[]
|
||||
}
|
||||
|
||||
export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
|
||||
const queryClient = useQueryClient()
|
||||
const moderationOpts = useModerationOpts()
|
||||
const threadMutes = useMutedThreads()
|
||||
const unreads = useUnreadNotificationsApi()
|
||||
const enabled = opts?.enabled !== false
|
||||
|
||||
return useInfiniteQuery<
|
||||
|
@ -69,40 +56,21 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
|
|||
>({
|
||||
queryKey: RQKEY(),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
const res = await getAgent().listNotifications({
|
||||
limit: PAGE_SIZE,
|
||||
cursor: pageParam,
|
||||
})
|
||||
|
||||
// filter out notifs by mod rules
|
||||
const notifs = res.data.notifications.filter(
|
||||
notif => !shouldFilterNotif(notif, moderationOpts),
|
||||
)
|
||||
|
||||
// group notifications which are essentially similar (follows, likes on a post)
|
||||
let notifsGrouped = groupNotifications(notifs)
|
||||
|
||||
// we fetch subjects of notifications (usually posts) now instead of lazily
|
||||
// in the UI to avoid relayouts
|
||||
const subjects = await fetchSubjects(getAgent(), notifsGrouped)
|
||||
for (const notif of notifsGrouped) {
|
||||
if (notif.subjectUri) {
|
||||
notif.subject = subjects.get(notif.subjectUri)
|
||||
if (notif.subject) {
|
||||
precacheResolvedUri(queryClient, notif.subject.author) // precache the handle->did resolution
|
||||
}
|
||||
// for the first page, we check the cached page held by the unread-checker first
|
||||
if (!pageParam) {
|
||||
const cachedPage = unreads.getCachedUnreadPage()
|
||||
if (cachedPage) {
|
||||
return cachedPage
|
||||
}
|
||||
}
|
||||
|
||||
// apply thread muting
|
||||
notifsGrouped = notifsGrouped.filter(
|
||||
notif => !isThreadMuted(notif, threadMutes),
|
||||
)
|
||||
|
||||
return {
|
||||
cursor: res.data.cursor,
|
||||
items: notifsGrouped,
|
||||
}
|
||||
// do a normal fetch
|
||||
return fetchPage({
|
||||
limit: PAGE_SIZE,
|
||||
cursor: pageParam,
|
||||
queryClient,
|
||||
moderationOpts,
|
||||
threadMutes,
|
||||
})
|
||||
},
|
||||
initialPageParam: undefined,
|
||||
getNextPageParam: lastPage => lastPage.cursor,
|
||||
|
@ -135,114 +103,3 @@ export function findPostInQueryData(
|
|||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function groupNotifications(
|
||||
notifs: AppBskyNotificationListNotifications.Notification[],
|
||||
): FeedNotification[] {
|
||||
const groupedNotifs: FeedNotification[] = []
|
||||
for (const notif of notifs) {
|
||||
const ts = +new Date(notif.indexedAt)
|
||||
let grouped = false
|
||||
if (GROUPABLE_REASONS.includes(notif.reason)) {
|
||||
for (const groupedNotif of groupedNotifs) {
|
||||
const ts2 = +new Date(groupedNotif.notification.indexedAt)
|
||||
if (
|
||||
Math.abs(ts2 - ts) < MS_2DAY &&
|
||||
notif.reason === groupedNotif.notification.reason &&
|
||||
notif.reasonSubject === groupedNotif.notification.reasonSubject &&
|
||||
notif.author.did !== groupedNotif.notification.author.did
|
||||
) {
|
||||
groupedNotif.additional = groupedNotif.additional || []
|
||||
groupedNotif.additional.push(notif)
|
||||
grouped = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!grouped) {
|
||||
const type = toKnownType(notif)
|
||||
groupedNotifs.push({
|
||||
_reactKey: `notif-${notif.uri}`,
|
||||
type,
|
||||
notification: notif,
|
||||
subjectUri: getSubjectUri(type, notif),
|
||||
})
|
||||
}
|
||||
}
|
||||
return groupedNotifs
|
||||
}
|
||||
|
||||
async function fetchSubjects(
|
||||
agent: BskyAgent,
|
||||
groupedNotifs: FeedNotification[],
|
||||
): Promise<Map<string, AppBskyFeedDefs.PostView>> {
|
||||
const uris = new Set<string>()
|
||||
for (const notif of groupedNotifs) {
|
||||
if (notif.subjectUri) {
|
||||
uris.add(notif.subjectUri)
|
||||
}
|
||||
}
|
||||
const uriChunks = chunk(Array.from(uris), 25)
|
||||
const postsChunks = await Promise.all(
|
||||
uriChunks.map(uris =>
|
||||
agent.app.bsky.feed.getPosts({uris}).then(res => res.data.posts),
|
||||
),
|
||||
)
|
||||
const map = new Map<string, AppBskyFeedDefs.PostView>()
|
||||
for (const post of postsChunks.flat()) {
|
||||
if (
|
||||
AppBskyFeedPost.isRecord(post.record) &&
|
||||
AppBskyFeedPost.validateRecord(post.record).success
|
||||
) {
|
||||
map.set(post.uri, post)
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
function toKnownType(
|
||||
notif: AppBskyNotificationListNotifications.Notification,
|
||||
): NotificationType {
|
||||
if (notif.reason === 'like') {
|
||||
if (notif.reasonSubject?.includes('feed.generator')) {
|
||||
return 'feedgen-like'
|
||||
}
|
||||
return 'post-like'
|
||||
}
|
||||
if (
|
||||
notif.reason === 'repost' ||
|
||||
notif.reason === 'mention' ||
|
||||
notif.reason === 'reply' ||
|
||||
notif.reason === 'quote' ||
|
||||
notif.reason === 'follow'
|
||||
) {
|
||||
return notif.reason as NotificationType
|
||||
}
|
||||
return 'unknown'
|
||||
}
|
||||
|
||||
function getSubjectUri(
|
||||
type: NotificationType,
|
||||
notif: AppBskyNotificationListNotifications.Notification,
|
||||
): string | undefined {
|
||||
if (type === 'reply' || type === 'quote' || type === 'mention') {
|
||||
return notif.uri
|
||||
} else if (type === 'post-like' || type === 'repost') {
|
||||
if (
|
||||
AppBskyFeedRepost.isRecord(notif.record) ||
|
||||
AppBskyFeedLike.isRecord(notif.record)
|
||||
) {
|
||||
return typeof notif.record.subject?.uri === 'string'
|
||||
? notif.record.subject?.uri
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isThreadMuted(notif: FeedNotification, mutes: string[]): boolean {
|
||||
if (!notif.subject) {
|
||||
return false
|
||||
}
|
||||
const record = notif.subject.record as AppBskyFeedPost.Record // assured in fetchSubjects()
|
||||
return mutes.includes(record.reply?.root.uri || notif.subject.uri)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import {
|
||||
AppBskyNotificationListNotifications,
|
||||
AppBskyFeedDefs,
|
||||
} from '@atproto/api'
|
||||
|
||||
export type NotificationType =
|
||||
| 'post-like'
|
||||
| 'feedgen-like'
|
||||
| 'repost'
|
||||
| 'mention'
|
||||
| 'reply'
|
||||
| 'quote'
|
||||
| 'follow'
|
||||
| 'unknown'
|
||||
|
||||
export interface FeedNotification {
|
||||
_reactKey: string
|
||||
type: NotificationType
|
||||
notification: AppBskyNotificationListNotifications.Notification
|
||||
additional?: AppBskyNotificationListNotifications.Notification[]
|
||||
subjectUri?: string
|
||||
subject?: AppBskyFeedDefs.PostView
|
||||
}
|
||||
|
||||
export interface FeedPage {
|
||||
cursor: string | undefined
|
||||
items: FeedNotification[]
|
||||
}
|
||||
|
||||
export interface CachedFeedPage {
|
||||
sessDid: string // used to invalidate on session changes
|
||||
syncedAt: Date
|
||||
data: FeedPage | undefined
|
||||
}
|
|
@ -1,10 +1,19 @@
|
|||
/**
|
||||
* A kind of companion API to ./feed.ts. See that file for more info.
|
||||
*/
|
||||
|
||||
import React from 'react'
|
||||
import * as Notifications from 'expo-notifications'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import BroadcastChannel from '#/lib/broadcast'
|
||||
import {useSession, getAgent} from '#/state/session'
|
||||
import {useModerationOpts} from '../preferences'
|
||||
import {shouldFilterNotif} from './util'
|
||||
import {fetchPage} from './util'
|
||||
import {CachedFeedPage, FeedPage} from './types'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {useMutedThreads} from '#/state/muted-threads'
|
||||
import {RQKEY as RQKEY_NOTIFS} from './feed'
|
||||
import {logger} from '#/logger'
|
||||
|
||||
const UPDATE_INTERVAL = 30 * 1e3 // 30sec
|
||||
|
||||
|
@ -14,7 +23,8 @@ type StateContext = string
|
|||
|
||||
interface ApiContext {
|
||||
markAllRead: () => Promise<void>
|
||||
checkUnread: () => Promise<void>
|
||||
checkUnread: (opts?: {invalidate?: boolean}) => Promise<void>
|
||||
getCachedUnreadPage: () => FeedPage | undefined
|
||||
}
|
||||
|
||||
const stateContext = React.createContext<StateContext>('')
|
||||
|
@ -22,16 +32,23 @@ const stateContext = React.createContext<StateContext>('')
|
|||
const apiContext = React.createContext<ApiContext>({
|
||||
async markAllRead() {},
|
||||
async checkUnread() {},
|
||||
getCachedUnreadPage: () => undefined,
|
||||
})
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const {hasSession} = useSession()
|
||||
const {hasSession, currentAccount} = useSession()
|
||||
const queryClient = useQueryClient()
|
||||
const moderationOpts = useModerationOpts()
|
||||
const threadMutes = useMutedThreads()
|
||||
|
||||
const [numUnread, setNumUnread] = React.useState('')
|
||||
|
||||
const checkUnreadRef = React.useRef<(() => Promise<void>) | null>(null)
|
||||
const lastSyncRef = React.useRef<Date>(new Date())
|
||||
const checkUnreadRef = React.useRef<ApiContext['checkUnread'] | null>(null)
|
||||
const cacheRef = React.useRef<CachedFeedPage>({
|
||||
sessDid: currentAccount?.did || '',
|
||||
syncedAt: new Date(),
|
||||
data: undefined,
|
||||
})
|
||||
|
||||
// periodic sync
|
||||
React.useEffect(() => {
|
||||
|
@ -46,14 +63,18 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||
// listen for broadcasts
|
||||
React.useEffect(() => {
|
||||
const listener = ({data}: MessageEvent) => {
|
||||
lastSyncRef.current = new Date()
|
||||
cacheRef.current = {
|
||||
sessDid: currentAccount?.did || '',
|
||||
syncedAt: new Date(),
|
||||
data: undefined,
|
||||
}
|
||||
setNumUnread(data.event)
|
||||
}
|
||||
broadcast.addEventListener('message', listener)
|
||||
return () => {
|
||||
broadcast.removeEventListener('message', listener)
|
||||
}
|
||||
}, [setNumUnread])
|
||||
}, [setNumUnread, currentAccount])
|
||||
|
||||
// create API
|
||||
const api = React.useMemo<ApiContext>(() => {
|
||||
|
@ -61,7 +82,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||
async markAllRead() {
|
||||
// update server
|
||||
await getAgent().updateSeenNotifications(
|
||||
lastSyncRef.current.toISOString(),
|
||||
cacheRef.current.syncedAt.toISOString(),
|
||||
)
|
||||
|
||||
// update & broadcast
|
||||
|
@ -69,38 +90,59 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||
broadcast.postMessage({event: ''})
|
||||
},
|
||||
|
||||
async checkUnread() {
|
||||
const agent = getAgent()
|
||||
async checkUnread({invalidate}: {invalidate?: boolean} = {}) {
|
||||
try {
|
||||
if (!getAgent().session) return
|
||||
|
||||
if (!agent.session) return
|
||||
// count
|
||||
const page = await fetchPage({
|
||||
cursor: undefined,
|
||||
limit: 40,
|
||||
queryClient,
|
||||
moderationOpts,
|
||||
threadMutes,
|
||||
})
|
||||
const unreadCount = countUnread(page)
|
||||
const unreadCountStr =
|
||||
unreadCount >= 30
|
||||
? '30+'
|
||||
: unreadCount === 0
|
||||
? ''
|
||||
: String(unreadCount)
|
||||
if (isNative) {
|
||||
Notifications.setBadgeCountAsync(Math.min(unreadCount, 30))
|
||||
}
|
||||
|
||||
// count
|
||||
const res = await agent.listNotifications({limit: 40})
|
||||
const filtered = res.data.notifications.filter(
|
||||
notif => !notif.isRead && !shouldFilterNotif(notif, moderationOpts),
|
||||
)
|
||||
const num =
|
||||
filtered.length >= 30
|
||||
? '30+'
|
||||
: filtered.length === 0
|
||||
? ''
|
||||
: String(filtered.length)
|
||||
if (isNative) {
|
||||
Notifications.setBadgeCountAsync(Math.min(filtered.length, 30))
|
||||
// track last sync
|
||||
const now = new Date()
|
||||
const lastIndexed =
|
||||
page.items[0] && new Date(page.items[0].notification.indexedAt)
|
||||
cacheRef.current = {
|
||||
sessDid: currentAccount?.did || '',
|
||||
data: page,
|
||||
syncedAt: !lastIndexed || now > lastIndexed ? now : lastIndexed,
|
||||
}
|
||||
|
||||
// update & broadcast
|
||||
setNumUnread(unreadCountStr)
|
||||
if (invalidate) {
|
||||
queryClient.resetQueries({queryKey: RQKEY_NOTIFS()})
|
||||
}
|
||||
broadcast.postMessage({event: unreadCountStr})
|
||||
} catch (e) {
|
||||
logger.error('Failed to check unread notifications', {error: e})
|
||||
}
|
||||
},
|
||||
|
||||
// track last sync
|
||||
const now = new Date()
|
||||
const lastIndexed = filtered[0] && new Date(filtered[0].indexedAt)
|
||||
lastSyncRef.current =
|
||||
!lastIndexed || now > lastIndexed ? now : lastIndexed
|
||||
|
||||
// update & broadcast
|
||||
setNumUnread(num)
|
||||
broadcast.postMessage({event: num})
|
||||
getCachedUnreadPage() {
|
||||
// return cached page if was for the current user
|
||||
// (protects against session changes serving data from the past session)
|
||||
if (cacheRef.current.sessDid === currentAccount?.did) {
|
||||
return cacheRef.current.data
|
||||
}
|
||||
},
|
||||
}
|
||||
}, [setNumUnread, moderationOpts])
|
||||
}, [setNumUnread, queryClient, moderationOpts, threadMutes, currentAccount])
|
||||
checkUnreadRef.current = api.checkUnread
|
||||
|
||||
return (
|
||||
|
@ -117,3 +159,20 @@ export function useUnreadNotifications() {
|
|||
export function useUnreadNotificationsApi() {
|
||||
return React.useContext(apiContext)
|
||||
}
|
||||
|
||||
function countUnread(page: FeedPage) {
|
||||
let num = 0
|
||||
for (const item of page.items) {
|
||||
if (!item.notification.isRead) {
|
||||
num++
|
||||
}
|
||||
if (item.additional) {
|
||||
for (const item2 of item.additional) {
|
||||
if (!item2.isRead) {
|
||||
num++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
|
|
@ -3,10 +3,78 @@ import {
|
|||
ModerationOpts,
|
||||
moderateProfile,
|
||||
moderatePost,
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedPost,
|
||||
AppBskyFeedRepost,
|
||||
AppBskyFeedLike,
|
||||
} from '@atproto/api'
|
||||
import chunk from 'lodash.chunk'
|
||||
import {QueryClient} from '@tanstack/react-query'
|
||||
import {getAgent} from '../../session'
|
||||
import {precacheProfile as precacheResolvedUri} from '../resolve-uri'
|
||||
import {NotificationType, FeedNotification, FeedPage} from './types'
|
||||
|
||||
const GROUPABLE_REASONS = ['like', 'repost', 'follow']
|
||||
const MS_1HR = 1e3 * 60 * 60
|
||||
const MS_2DAY = MS_1HR * 48
|
||||
|
||||
// exported api
|
||||
// =
|
||||
|
||||
export async function fetchPage({
|
||||
cursor,
|
||||
limit,
|
||||
queryClient,
|
||||
moderationOpts,
|
||||
threadMutes,
|
||||
}: {
|
||||
cursor: string | undefined
|
||||
limit: number
|
||||
queryClient: QueryClient
|
||||
moderationOpts: ModerationOpts | undefined
|
||||
threadMutes: string[]
|
||||
}): Promise<FeedPage> {
|
||||
const res = await getAgent().listNotifications({
|
||||
limit,
|
||||
cursor,
|
||||
})
|
||||
|
||||
// filter out notifs by mod rules
|
||||
const notifs = res.data.notifications.filter(
|
||||
notif => !shouldFilterNotif(notif, moderationOpts),
|
||||
)
|
||||
|
||||
// group notifications which are essentially similar (follows, likes on a post)
|
||||
let notifsGrouped = groupNotifications(notifs)
|
||||
|
||||
// we fetch subjects of notifications (usually posts) now instead of lazily
|
||||
// in the UI to avoid relayouts
|
||||
const subjects = await fetchSubjects(notifsGrouped)
|
||||
for (const notif of notifsGrouped) {
|
||||
if (notif.subjectUri) {
|
||||
notif.subject = subjects.get(notif.subjectUri)
|
||||
if (notif.subject) {
|
||||
precacheResolvedUri(queryClient, notif.subject.author) // precache the handle->did resolution
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply thread muting
|
||||
notifsGrouped = notifsGrouped.filter(
|
||||
notif => !isThreadMuted(notif, threadMutes),
|
||||
)
|
||||
|
||||
return {
|
||||
cursor: res.data.cursor,
|
||||
items: notifsGrouped,
|
||||
}
|
||||
}
|
||||
|
||||
// internal methods
|
||||
// =
|
||||
|
||||
// TODO this should be in the sdk as moderateNotification -prf
|
||||
export function shouldFilterNotif(
|
||||
function shouldFilterNotif(
|
||||
notif: AppBskyNotificationListNotifications.Notification,
|
||||
moderationOpts: ModerationOpts | undefined,
|
||||
): boolean {
|
||||
|
@ -36,3 +104,116 @@ export function shouldFilterNotif(
|
|||
// (this requires fetching the post)
|
||||
return false
|
||||
}
|
||||
|
||||
function groupNotifications(
|
||||
notifs: AppBskyNotificationListNotifications.Notification[],
|
||||
): FeedNotification[] {
|
||||
const groupedNotifs: FeedNotification[] = []
|
||||
for (const notif of notifs) {
|
||||
const ts = +new Date(notif.indexedAt)
|
||||
let grouped = false
|
||||
if (GROUPABLE_REASONS.includes(notif.reason)) {
|
||||
for (const groupedNotif of groupedNotifs) {
|
||||
const ts2 = +new Date(groupedNotif.notification.indexedAt)
|
||||
if (
|
||||
Math.abs(ts2 - ts) < MS_2DAY &&
|
||||
notif.reason === groupedNotif.notification.reason &&
|
||||
notif.reasonSubject === groupedNotif.notification.reasonSubject &&
|
||||
notif.author.did !== groupedNotif.notification.author.did &&
|
||||
notif.isRead === groupedNotif.notification.isRead
|
||||
) {
|
||||
groupedNotif.additional = groupedNotif.additional || []
|
||||
groupedNotif.additional.push(notif)
|
||||
grouped = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!grouped) {
|
||||
const type = toKnownType(notif)
|
||||
groupedNotifs.push({
|
||||
_reactKey: `notif-${notif.uri}`,
|
||||
type,
|
||||
notification: notif,
|
||||
subjectUri: getSubjectUri(type, notif),
|
||||
})
|
||||
}
|
||||
}
|
||||
return groupedNotifs
|
||||
}
|
||||
|
||||
async function fetchSubjects(
|
||||
groupedNotifs: FeedNotification[],
|
||||
): Promise<Map<string, AppBskyFeedDefs.PostView>> {
|
||||
const uris = new Set<string>()
|
||||
for (const notif of groupedNotifs) {
|
||||
if (notif.subjectUri) {
|
||||
uris.add(notif.subjectUri)
|
||||
}
|
||||
}
|
||||
const uriChunks = chunk(Array.from(uris), 25)
|
||||
const postsChunks = await Promise.all(
|
||||
uriChunks.map(uris =>
|
||||
getAgent()
|
||||
.app.bsky.feed.getPosts({uris})
|
||||
.then(res => res.data.posts),
|
||||
),
|
||||
)
|
||||
const map = new Map<string, AppBskyFeedDefs.PostView>()
|
||||
for (const post of postsChunks.flat()) {
|
||||
if (
|
||||
AppBskyFeedPost.isRecord(post.record) &&
|
||||
AppBskyFeedPost.validateRecord(post.record).success
|
||||
) {
|
||||
map.set(post.uri, post)
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
function toKnownType(
|
||||
notif: AppBskyNotificationListNotifications.Notification,
|
||||
): NotificationType {
|
||||
if (notif.reason === 'like') {
|
||||
if (notif.reasonSubject?.includes('feed.generator')) {
|
||||
return 'feedgen-like'
|
||||
}
|
||||
return 'post-like'
|
||||
}
|
||||
if (
|
||||
notif.reason === 'repost' ||
|
||||
notif.reason === 'mention' ||
|
||||
notif.reason === 'reply' ||
|
||||
notif.reason === 'quote' ||
|
||||
notif.reason === 'follow'
|
||||
) {
|
||||
return notif.reason as NotificationType
|
||||
}
|
||||
return 'unknown'
|
||||
}
|
||||
|
||||
function getSubjectUri(
|
||||
type: NotificationType,
|
||||
notif: AppBskyNotificationListNotifications.Notification,
|
||||
): string | undefined {
|
||||
if (type === 'reply' || type === 'quote' || type === 'mention') {
|
||||
return notif.uri
|
||||
} else if (type === 'post-like' || type === 'repost') {
|
||||
if (
|
||||
AppBskyFeedRepost.isRecord(notif.record) ||
|
||||
AppBskyFeedLike.isRecord(notif.record)
|
||||
) {
|
||||
return typeof notif.record.subject?.uri === 'string'
|
||||
? notif.record.subject?.uri
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isThreadMuted(notif: FeedNotification, mutes: string[]): boolean {
|
||||
if (!notif.subject) {
|
||||
return false
|
||||
}
|
||||
const record = notif.subject.record as AppBskyFeedPost.Record // assured in fetchSubjects()
|
||||
return mutes.includes(record.reply?.root.uri || notif.subject.uri)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import {useCallback, useMemo} from 'react'
|
||||
import {AppBskyFeedDefs, AppBskyFeedPost, moderatePost} from '@atproto/api'
|
||||
import {
|
||||
useInfiniteQuery,
|
||||
|
@ -7,9 +6,8 @@ import {
|
|||
QueryClient,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query'
|
||||
import {getAgent} from '../session'
|
||||
import {useFeedTuners} from '../preferences/feed-tuners'
|
||||
import {FeedTuner, NoopFeedTuner} from 'lib/api/feed-manip'
|
||||
import {FeedTuner, FeedTunerFn, NoopFeedTuner} from 'lib/api/feed-manip'
|
||||
import {FeedAPI, ReasonFeedSource} from 'lib/api/feed/types'
|
||||
import {FollowingFeedAPI} from 'lib/api/feed/following'
|
||||
import {AuthorFeedAPI} from 'lib/api/feed/author'
|
||||
|
@ -17,10 +15,13 @@ import {LikesFeedAPI} from 'lib/api/feed/likes'
|
|||
import {CustomFeedAPI} from 'lib/api/feed/custom'
|
||||
import {ListFeedAPI} from 'lib/api/feed/list'
|
||||
import {MergeFeedAPI} from 'lib/api/feed/merge'
|
||||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
import {logger} from '#/logger'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {precacheFeedPosts as precacheResolvedUris} from './resolve-uri'
|
||||
import {getAgent} from '#/state/session'
|
||||
import {DEFAULT_LOGGED_OUT_PREFERENCES} from '#/state/queries/preferences/const'
|
||||
import {getModerationOpts} from '#/state/queries/preferences/moderation'
|
||||
import {KnownError} from '#/view/com/posts/FeedErrorMessage'
|
||||
|
||||
type ActorDid = string
|
||||
type AuthorFilter =
|
||||
|
@ -42,7 +43,7 @@ export interface FeedParams {
|
|||
mergeFeedSources?: string[]
|
||||
}
|
||||
|
||||
type RQPageParam = string | undefined
|
||||
type RQPageParam = {cursor: string | undefined; api: FeedAPI} | undefined
|
||||
|
||||
export function RQKEY(feedDesc: FeedDescriptor, params?: FeedParams) {
|
||||
return ['post-feed', feedDesc, params || {}]
|
||||
|
@ -63,7 +64,15 @@ export interface FeedPostSlice {
|
|||
items: FeedPostSliceItem[]
|
||||
}
|
||||
|
||||
export interface FeedPageUnselected {
|
||||
api: FeedAPI
|
||||
cursor: string | undefined
|
||||
feed: AppBskyFeedDefs.FeedViewPost[]
|
||||
}
|
||||
|
||||
export interface FeedPage {
|
||||
api: FeedAPI
|
||||
tuner: FeedTuner | NoopFeedTuner
|
||||
cursor: string | undefined
|
||||
slices: FeedPostSlice[]
|
||||
}
|
||||
|
@ -76,117 +85,139 @@ export function usePostFeedQuery(
|
|||
const queryClient = useQueryClient()
|
||||
const feedTuners = useFeedTuners(feedDesc)
|
||||
const enabled = opts?.enabled !== false
|
||||
const moderationOpts = useModerationOpts()
|
||||
const agent = getAgent()
|
||||
|
||||
const api: FeedAPI = useMemo(() => {
|
||||
if (feedDesc === 'home') {
|
||||
return new MergeFeedAPI(agent, params || {}, feedTuners)
|
||||
} else if (feedDesc === 'following') {
|
||||
return new FollowingFeedAPI(agent)
|
||||
} else if (feedDesc.startsWith('author')) {
|
||||
const [_, actor, filter] = feedDesc.split('|')
|
||||
return new AuthorFeedAPI(agent, {actor, filter})
|
||||
} else if (feedDesc.startsWith('likes')) {
|
||||
const [_, actor] = feedDesc.split('|')
|
||||
return new LikesFeedAPI(agent, {actor})
|
||||
} else if (feedDesc.startsWith('feedgen')) {
|
||||
const [_, feed] = feedDesc.split('|')
|
||||
return new CustomFeedAPI(agent, {feed})
|
||||
} else if (feedDesc.startsWith('list')) {
|
||||
const [_, list] = feedDesc.split('|')
|
||||
return new ListFeedAPI(agent, {list})
|
||||
} else {
|
||||
// shouldnt happen
|
||||
return new FollowingFeedAPI(agent)
|
||||
}
|
||||
}, [feedDesc, params, feedTuners, agent])
|
||||
|
||||
const disableTuner = !!params?.disableTuner
|
||||
const tuner = useMemo(
|
||||
() => (disableTuner ? new NoopFeedTuner() : new FeedTuner()),
|
||||
[disableTuner],
|
||||
)
|
||||
|
||||
const pollLatest = useCallback(async () => {
|
||||
if (!enabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
logger.debug('usePostFeedQuery: pollLatest')
|
||||
|
||||
const post = await api.peekLatest()
|
||||
|
||||
if (post && moderationOpts) {
|
||||
const slices = tuner.tune([post], feedTuners, {
|
||||
dryRun: true,
|
||||
maintainOrder: true,
|
||||
})
|
||||
if (slices[0]) {
|
||||
if (
|
||||
!moderatePost(slices[0].items[0].post, moderationOpts).content.filter
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}, [api, tuner, feedTuners, moderationOpts, enabled])
|
||||
|
||||
const out = useInfiniteQuery<
|
||||
FeedPage,
|
||||
return useInfiniteQuery<
|
||||
FeedPageUnselected,
|
||||
Error,
|
||||
InfiniteData<FeedPage>,
|
||||
QueryKey,
|
||||
RQPageParam
|
||||
>({
|
||||
enabled,
|
||||
staleTime: STALE.INFINITY,
|
||||
queryKey: RQKEY(feedDesc, params),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
logger.debug('usePostFeedQuery', {feedDesc, pageParam})
|
||||
if (!pageParam) {
|
||||
tuner.reset()
|
||||
}
|
||||
const res = await api.fetch({cursor: pageParam, limit: 30})
|
||||
|
||||
const {api, cursor} = pageParam
|
||||
? pageParam
|
||||
: {
|
||||
api: createApi(feedDesc, params || {}, feedTuners),
|
||||
cursor: undefined,
|
||||
}
|
||||
|
||||
const res = await api.fetch({cursor, limit: 30})
|
||||
precacheResolvedUris(queryClient, res.feed) // precache the handle->did resolution
|
||||
const slices = tuner.tune(res.feed, feedTuners)
|
||||
|
||||
/*
|
||||
* If this is a public view, we need to check if posts fail moderation.
|
||||
* If all fail, we throw an error. If only some fail, we continue and let
|
||||
* moderations happen later, which results in some posts being shown and
|
||||
* some not.
|
||||
*/
|
||||
if (!getAgent().session) {
|
||||
assertSomePostsPassModeration(res.feed)
|
||||
}
|
||||
|
||||
return {
|
||||
api,
|
||||
cursor: res.cursor,
|
||||
slices: slices.map(slice => ({
|
||||
_reactKey: slice._reactKey,
|
||||
rootUri: slice.rootItem.post.uri,
|
||||
isThread:
|
||||
slice.items.length > 1 &&
|
||||
slice.items.every(
|
||||
item => item.post.author.did === slice.items[0].post.author.did,
|
||||
),
|
||||
items: slice.items
|
||||
.map((item, i) => {
|
||||
if (
|
||||
AppBskyFeedPost.isRecord(item.post.record) &&
|
||||
AppBskyFeedPost.validateRecord(item.post.record).success
|
||||
) {
|
||||
return {
|
||||
_reactKey: `${slice._reactKey}-${i}`,
|
||||
uri: item.post.uri,
|
||||
post: item.post,
|
||||
record: item.post.record,
|
||||
reason: i === 0 && slice.source ? slice.source : item.reason,
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
.filter(Boolean) as FeedPostSliceItem[],
|
||||
})),
|
||||
feed: res.feed,
|
||||
}
|
||||
},
|
||||
initialPageParam: undefined,
|
||||
getNextPageParam: lastPage => lastPage.cursor,
|
||||
enabled,
|
||||
getNextPageParam: lastPage => ({
|
||||
api: lastPage.api,
|
||||
cursor: lastPage.cursor,
|
||||
}),
|
||||
select(data) {
|
||||
const tuner = params?.disableTuner
|
||||
? new NoopFeedTuner()
|
||||
: new FeedTuner(feedTuners)
|
||||
return {
|
||||
pageParams: data.pageParams,
|
||||
pages: data.pages.map(page => ({
|
||||
api: page.api,
|
||||
tuner,
|
||||
cursor: page.cursor,
|
||||
slices: tuner.tune(page.feed).map(slice => ({
|
||||
_reactKey: slice._reactKey,
|
||||
rootUri: slice.rootItem.post.uri,
|
||||
isThread:
|
||||
slice.items.length > 1 &&
|
||||
slice.items.every(
|
||||
item => item.post.author.did === slice.items[0].post.author.did,
|
||||
),
|
||||
items: slice.items
|
||||
.map((item, i) => {
|
||||
if (
|
||||
AppBskyFeedPost.isRecord(item.post.record) &&
|
||||
AppBskyFeedPost.validateRecord(item.post.record).success
|
||||
) {
|
||||
return {
|
||||
_reactKey: `${slice._reactKey}-${i}`,
|
||||
uri: item.post.uri,
|
||||
post: item.post,
|
||||
record: item.post.record,
|
||||
reason:
|
||||
i === 0 && slice.source ? slice.source : item.reason,
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
.filter(Boolean) as FeedPostSliceItem[],
|
||||
})),
|
||||
})),
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return {...out, pollLatest}
|
||||
export async function pollLatest(page: FeedPage | undefined) {
|
||||
if (!page) {
|
||||
return false
|
||||
}
|
||||
|
||||
logger.debug('usePostFeedQuery: pollLatest')
|
||||
const post = await page.api.peekLatest()
|
||||
if (post) {
|
||||
const slices = page.tuner.tune([post], {
|
||||
dryRun: true,
|
||||
maintainOrder: true,
|
||||
})
|
||||
if (slices[0]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function createApi(
|
||||
feedDesc: FeedDescriptor,
|
||||
params: FeedParams,
|
||||
feedTuners: FeedTunerFn[],
|
||||
) {
|
||||
if (feedDesc === 'home') {
|
||||
return new MergeFeedAPI(params, feedTuners)
|
||||
} else if (feedDesc === 'following') {
|
||||
return new FollowingFeedAPI()
|
||||
} else if (feedDesc.startsWith('author')) {
|
||||
const [_, actor, filter] = feedDesc.split('|')
|
||||
return new AuthorFeedAPI({actor, filter})
|
||||
} else if (feedDesc.startsWith('likes')) {
|
||||
const [_, actor] = feedDesc.split('|')
|
||||
return new LikesFeedAPI({actor})
|
||||
} else if (feedDesc.startsWith('feedgen')) {
|
||||
const [_, feed] = feedDesc.split('|')
|
||||
return new CustomFeedAPI({feed})
|
||||
} else if (feedDesc.startsWith('list')) {
|
||||
const [_, list] = feedDesc.split('|')
|
||||
return new ListFeedAPI({list})
|
||||
} else {
|
||||
// shouldnt happen
|
||||
return new FollowingFeedAPI()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,8 +227,10 @@ export function usePostFeedQuery(
|
|||
export function findPostInQueryData(
|
||||
queryClient: QueryClient,
|
||||
uri: string,
|
||||
): FeedPostSliceItem | undefined {
|
||||
const queryDatas = queryClient.getQueriesData<InfiniteData<FeedPage>>({
|
||||
): AppBskyFeedDefs.FeedViewPost | undefined {
|
||||
const queryDatas = queryClient.getQueriesData<
|
||||
InfiniteData<FeedPageUnselected>
|
||||
>({
|
||||
queryKey: ['post-feed'],
|
||||
})
|
||||
for (const [_queryKey, queryData] of queryDatas) {
|
||||
|
@ -205,14 +238,34 @@ export function findPostInQueryData(
|
|||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const slice of page.slices) {
|
||||
for (const item of slice.items) {
|
||||
if (item.uri === uri) {
|
||||
return item
|
||||
}
|
||||
for (const item of page.feed) {
|
||||
if (item.post.uri === uri) {
|
||||
return item
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function assertSomePostsPassModeration(feed: AppBskyFeedDefs.FeedViewPost[]) {
|
||||
// assume false
|
||||
let somePostsPassModeration = false
|
||||
|
||||
for (const item of feed) {
|
||||
const moderationOpts = getModerationOpts({
|
||||
userDid: '',
|
||||
preferences: DEFAULT_LOGGED_OUT_PREFERENCES,
|
||||
})
|
||||
const moderation = moderatePost(item.post, moderationOpts)
|
||||
|
||||
if (!moderation.content.filter) {
|
||||
// we have a sfw post
|
||||
somePostsPassModeration = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!somePostsPassModeration) {
|
||||
throw new Error(KnownError.FeedNSFPublic)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import {AppBskyFeedGetLikes} from '@atproto/api'
|
|||
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
|
||||
|
||||
import {getAgent} from '#/state/session'
|
||||
import {STALE} from '#/state/queries'
|
||||
|
||||
const PAGE_SIZE = 30
|
||||
type RQPageParam = string | undefined
|
||||
|
@ -18,7 +17,6 @@ export function usePostLikedByQuery(resolvedUri: string | undefined) {
|
|||
QueryKey,
|
||||
RQPageParam
|
||||
>({
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(resolvedUri || ''),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
const res = await getAgent().getLikes({
|
||||
|
|
|
@ -2,7 +2,6 @@ import {AppBskyFeedGetRepostedBy} from '@atproto/api'
|
|||
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
|
||||
|
||||
import {getAgent} from '#/state/session'
|
||||
import {STALE} from '#/state/queries'
|
||||
|
||||
const PAGE_SIZE = 30
|
||||
type RQPageParam = string | undefined
|
||||
|
@ -18,7 +17,6 @@ export function usePostRepostedByQuery(resolvedUri: string | undefined) {
|
|||
QueryKey,
|
||||
RQPageParam
|
||||
>({
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(resolvedUri || ''),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
const res = await getAgent().getRepostedBy({
|
||||
|
|
|
@ -7,11 +7,7 @@ import {useQuery, useQueryClient, QueryClient} from '@tanstack/react-query'
|
|||
|
||||
import {getAgent} from '#/state/session'
|
||||
import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {
|
||||
findPostInQueryData as findPostInFeedQueryData,
|
||||
FeedPostSliceItem,
|
||||
} from './post-feed'
|
||||
import {findPostInQueryData as findPostInFeedQueryData} from './post-feed'
|
||||
import {findPostInQueryData as findPostInNotifsQueryData} from './notifications/feed'
|
||||
import {precacheThreadPosts as precacheResolvedUris} from './resolve-uri'
|
||||
|
||||
|
@ -68,7 +64,6 @@ export type ThreadNode =
|
|||
export function usePostThreadQuery(uri: string | undefined) {
|
||||
const queryClient = useQueryClient()
|
||||
return useQuery<ThreadNode, Error>({
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(uri || ''),
|
||||
async queryFn() {
|
||||
const res = await getAgent().getPostThread({uri: uri!})
|
||||
|
@ -93,7 +88,7 @@ export function usePostThreadQuery(uri: string | undefined) {
|
|||
{
|
||||
const item = findPostInFeedQueryData(queryClient, uri)
|
||||
if (item) {
|
||||
return feedItemToPlaceholderThread(item)
|
||||
return feedViewPostToPlaceholderThread(item)
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -275,13 +270,15 @@ function threadNodeToPlaceholderThread(
|
|||
}
|
||||
}
|
||||
|
||||
function feedItemToPlaceholderThread(item: FeedPostSliceItem): ThreadNode {
|
||||
function feedViewPostToPlaceholderThread(
|
||||
item: AppBskyFeedDefs.FeedViewPost,
|
||||
): ThreadNode {
|
||||
return {
|
||||
type: 'post',
|
||||
_reactKey: item.post.uri,
|
||||
uri: item.post.uri,
|
||||
post: item.post,
|
||||
record: item.record,
|
||||
record: item.post.record as AppBskyFeedPost.Record, // validated in post-feed
|
||||
parent: undefined,
|
||||
replies: undefined,
|
||||
viewer: item.post.viewer,
|
||||
|
@ -291,7 +288,7 @@ function feedItemToPlaceholderThread(item: FeedPostSliceItem): ThreadNode {
|
|||
hasMore: false,
|
||||
showChildReplyLine: false,
|
||||
showParentReplyLine: false,
|
||||
isParentLoading: !!item.record.reply,
|
||||
isParentLoading: !!(item.post.record as AppBskyFeedPost.Record).reply,
|
||||
isChildLoading: !!item.post.replyCount,
|
||||
},
|
||||
}
|
||||
|
@ -305,7 +302,7 @@ function postViewToPlaceholderThread(
|
|||
_reactKey: post.uri,
|
||||
uri: post.uri,
|
||||
post: post,
|
||||
record: post.record as AppBskyFeedPost.Record, // validate in notifs
|
||||
record: post.record as AppBskyFeedPost.Record, // validated in notifs
|
||||
parent: undefined,
|
||||
replies: undefined,
|
||||
viewer: post.viewer,
|
||||
|
|
|
@ -4,13 +4,11 @@ import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query'
|
|||
|
||||
import {getAgent} from '#/state/session'
|
||||
import {updatePostShadow} from '#/state/cache/post-shadow'
|
||||
import {STALE} from '#/state/queries'
|
||||
|
||||
export const RQKEY = (postUri: string) => ['post', postUri]
|
||||
|
||||
export function usePostQuery(uri: string | undefined) {
|
||||
return useQuery<AppBskyFeedDefs.PostView>({
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(uri || ''),
|
||||
async queryFn() {
|
||||
const res = await getAgent().getPosts({uris: [uri!]})
|
||||
|
@ -29,7 +27,6 @@ export function useGetPost() {
|
|||
return React.useCallback(
|
||||
async ({uri}: {uri: string}) => {
|
||||
return queryClient.fetchQuery({
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(uri || ''),
|
||||
async queryFn() {
|
||||
const urip = new AtUri(uri)
|
||||
|
|
|
@ -2,7 +2,6 @@ import {AppBskyFeedGetActorFeeds} from '@atproto/api'
|
|||
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
|
||||
|
||||
import {getAgent} from '#/state/session'
|
||||
import {STALE} from '#/state/queries'
|
||||
|
||||
const PAGE_SIZE = 30
|
||||
type RQPageParam = string | undefined
|
||||
|
@ -22,7 +21,6 @@ export function useProfileFeedgensQuery(
|
|||
QueryKey,
|
||||
RQPageParam
|
||||
>({
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(did),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
const res = await getAgent().app.bsky.feed.getActorFeeds({
|
||||
|
|
|
@ -2,7 +2,6 @@ import {AppBskyGraphGetFollowers} from '@atproto/api'
|
|||
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
|
||||
|
||||
import {getAgent} from '#/state/session'
|
||||
import {STALE} from '#/state/queries'
|
||||
|
||||
const PAGE_SIZE = 30
|
||||
type RQPageParam = string | undefined
|
||||
|
@ -17,7 +16,6 @@ export function useProfileFollowersQuery(did: string | undefined) {
|
|||
QueryKey,
|
||||
RQPageParam
|
||||
>({
|
||||
staleTime: STALE.MINUTES.FIVE,
|
||||
queryKey: RQKEY(did || ''),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
const res = await getAgent().app.bsky.graph.getFollowers({
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import {AppBskyGraphGetLists} from '@atproto/api'
|
||||
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
|
||||
|
||||
import {getAgent} from '#/state/session'
|
||||
import {STALE} from '#/state/queries'
|
||||
|
||||
const PAGE_SIZE = 30
|
||||
type RQPageParam = string | undefined
|
||||
|
@ -18,7 +16,6 @@ export function useProfileListsQuery(did: string, opts?: {enabled?: boolean}) {
|
|||
QueryKey,
|
||||
RQPageParam
|
||||
>({
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(did),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
const res = await getAgent().app.bsky.graph.getLists({
|
||||
|
|
|
@ -4,7 +4,6 @@ import {
|
|||
AppBskyActorDefs,
|
||||
AppBskyActorProfile,
|
||||
AppBskyActorGetProfile,
|
||||
BskyAgent,
|
||||
} from '@atproto/api'
|
||||
import {useQuery, useQueryClient, useMutation} from '@tanstack/react-query'
|
||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
|
@ -22,6 +21,10 @@ export const RQKEY = (did: string) => ['profile', did]
|
|||
|
||||
export function useProfileQuery({did}: {did: string | undefined}) {
|
||||
return useQuery({
|
||||
// WARNING
|
||||
// this staleTime is load-bearing
|
||||
// if you remove it, the UI infinite-loops
|
||||
// -prf
|
||||
staleTime: STALE.MINUTES.FIVE,
|
||||
queryKey: RQKEY(did || ''),
|
||||
queryFn: async () => {
|
||||
|
@ -68,7 +71,7 @@ export function useProfileUpdateMutation() {
|
|||
}
|
||||
return existing
|
||||
})
|
||||
await whenAppViewReady(getAgent(), profile.did, res => {
|
||||
await whenAppViewReady(profile.did, res => {
|
||||
if (typeof newUserAvatar !== 'undefined') {
|
||||
if (newUserAvatar === null && res.data.avatar) {
|
||||
// url hasnt cleared yet
|
||||
|
@ -464,7 +467,6 @@ function useProfileUnblockMutation() {
|
|||
}
|
||||
|
||||
async function whenAppViewReady(
|
||||
agent: BskyAgent,
|
||||
actor: string,
|
||||
fn: (res: AppBskyActorGetProfile.Response) => boolean,
|
||||
) {
|
||||
|
@ -472,6 +474,6 @@ async function whenAppViewReady(
|
|||
5, // 5 tries
|
||||
1e3, // 1s delay between tries
|
||||
fn,
|
||||
() => agent.app.bsky.actor.getProfile({actor}),
|
||||
() => getAgent().app.bsky.actor.getProfile({actor}),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ export function useResolveUriQuery(uri: string | undefined): UriUseQueryResult {
|
|||
|
||||
export function useResolveDidQuery(didOrHandle: string | undefined) {
|
||||
return useQuery<string, Error>({
|
||||
staleTime: STALE.INFINITY,
|
||||
staleTime: STALE.HOURS.ONE,
|
||||
queryKey: RQKEY(didOrHandle || ''),
|
||||
async queryFn() {
|
||||
if (!didOrHandle) {
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import {BskyAgent} from '@atproto/api'
|
||||
import {useQuery} from '@tanstack/react-query'
|
||||
|
||||
import {STALE} from '#/state/queries'
|
||||
|
||||
export const RQKEY = (serviceUrl: string) => ['service', serviceUrl]
|
||||
|
||||
export function useServiceQuery(serviceUrl: string) {
|
||||
return useQuery({
|
||||
staleTime: STALE.HOURS.ONE,
|
||||
queryKey: RQKEY(serviceUrl),
|
||||
queryFn: async () => {
|
||||
const agent = new BskyAgent({service: serviceUrl})
|
||||
|
|
|
@ -90,7 +90,7 @@ export function useGetSuggestedFollowersByActor() {
|
|||
return React.useCallback(
|
||||
async (actor: string) => {
|
||||
const res = await queryClient.fetchQuery({
|
||||
staleTime: 60 * 1000,
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: suggestedFollowsByActorQueryKey(actor),
|
||||
queryFn: async () => {
|
||||
const res =
|
||||
|
|
|
@ -13,6 +13,12 @@ import {useCloseAllActiveElements} from '#/state/util'
|
|||
|
||||
let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT
|
||||
|
||||
/**
|
||||
* NOTE
|
||||
* Never hold on to the object returned by this function.
|
||||
* Call `getAgent()` at the time of invocation to ensure
|
||||
* that you never have a stale agent.
|
||||
*/
|
||||
export function getAgent() {
|
||||
return __globalAgent
|
||||
}
|
||||
|
|
|
@ -1,23 +1,30 @@
|
|||
import React from 'react'
|
||||
import {ScrollView, TouchableOpacity, View} from 'react-native'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {
|
||||
FontAwesomeIcon,
|
||||
FontAwesomeIconStyle,
|
||||
} from '@fortawesome/react-native-fontawesome'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {Text} from '../../util/text/Text'
|
||||
import {UserAvatar} from '../../util/UserAvatar'
|
||||
import {s} from 'lib/styles'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {styles} from './styles'
|
||||
import {useSession, useSessionApi, SessionAccount} from '#/state/session'
|
||||
import {useProfileQuery} from '#/state/queries/profile'
|
||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
|
||||
function AccountItem({
|
||||
account,
|
||||
onSelect,
|
||||
isCurrentAccount,
|
||||
}: {
|
||||
account: SessionAccount
|
||||
onSelect: (account: SessionAccount) => void
|
||||
isCurrentAccount: boolean
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
|
@ -48,11 +55,19 @@ function AccountItem({
|
|||
{account.handle}
|
||||
</Text>
|
||||
</Text>
|
||||
<FontAwesomeIcon
|
||||
icon="angle-right"
|
||||
size={16}
|
||||
style={[pal.text, s.mr10]}
|
||||
/>
|
||||
{isCurrentAccount ? (
|
||||
<FontAwesomeIcon
|
||||
icon="check"
|
||||
size={16}
|
||||
style={[{color: colors.green3} as FontAwesomeIconStyle, s.mr10]}
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon="angle-right"
|
||||
size={16}
|
||||
style={[pal.text, s.mr10]}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
|
@ -67,8 +82,9 @@ export const ChooseAccountForm = ({
|
|||
const {track, screen} = useAnalytics()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {accounts} = useSession()
|
||||
const {accounts, currentAccount} = useSession()
|
||||
const {initSession} = useSessionApi()
|
||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||
|
||||
React.useEffect(() => {
|
||||
screen('Choose Account')
|
||||
|
@ -77,13 +93,21 @@ export const ChooseAccountForm = ({
|
|||
const onSelect = React.useCallback(
|
||||
async (account: SessionAccount) => {
|
||||
if (account.accessJwt) {
|
||||
await initSession(account)
|
||||
track('Sign In', {resumedSession: true})
|
||||
if (account.did === currentAccount?.did) {
|
||||
setShowLoggedOut(false)
|
||||
Toast.show(`Already signed in as @${account.handle}`)
|
||||
} else {
|
||||
await initSession(account)
|
||||
track('Sign In', {resumedSession: true})
|
||||
setTimeout(() => {
|
||||
Toast.show(`Signed in as @${account.handle}`)
|
||||
}, 100)
|
||||
}
|
||||
} else {
|
||||
onSelectAccount(account)
|
||||
}
|
||||
},
|
||||
[track, initSession, onSelectAccount],
|
||||
[currentAccount, track, initSession, onSelectAccount, setShowLoggedOut],
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -94,7 +118,12 @@ export const ChooseAccountForm = ({
|
|||
<Trans>Sign in as...</Trans>
|
||||
</Text>
|
||||
{accounts.map(account => (
|
||||
<AccountItem key={account.did} account={account} onSelect={onSelect} />
|
||||
<AccountItem
|
||||
key={account.did}
|
||||
account={account}
|
||||
onSelect={onSelect}
|
||||
isCurrentAccount={account.did === currentAccount?.did}
|
||||
/>
|
||||
))}
|
||||
<TouchableOpacity
|
||||
testID="chooseNewAccountBtn"
|
||||
|
|
|
@ -62,7 +62,7 @@ export function FeedPage({
|
|||
const onSoftReset = React.useCallback(() => {
|
||||
if (isPageFocused) {
|
||||
scrollToTop()
|
||||
queryClient.invalidateQueries({queryKey: FEED_RQKEY(feed)})
|
||||
queryClient.resetQueries({queryKey: FEED_RQKEY(feed)})
|
||||
setHasNew(false)
|
||||
}
|
||||
}, [isPageFocused, scrollToTop, queryClient, feed, setHasNew])
|
||||
|
@ -83,7 +83,7 @@ export function FeedPage({
|
|||
|
||||
const onPressLoadLatest = React.useCallback(() => {
|
||||
scrollToTop()
|
||||
queryClient.invalidateQueries({queryKey: FEED_RQKEY(feed)})
|
||||
queryClient.resetQueries({queryKey: FEED_RQKEY(feed)})
|
||||
setHasNew(false)
|
||||
}, [scrollToTop, feed, queryClient, setHasNew])
|
||||
|
||||
|
|
|
@ -17,12 +17,14 @@ import {useModalControls} from '#/state/modals'
|
|||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {
|
||||
usePinFeedMutation,
|
||||
UsePreferencesQueryResponse,
|
||||
usePreferencesQuery,
|
||||
useSaveFeedMutation,
|
||||
useRemoveFeedMutation,
|
||||
} from '#/state/queries/preferences'
|
||||
import {useFeedSourceInfoQuery, FeedSourceInfo} from '#/state/queries/feed'
|
||||
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||
|
||||
export function FeedSourceCard({
|
||||
feedUri,
|
||||
|
@ -30,17 +32,27 @@ export function FeedSourceCard({
|
|||
showSaveBtn = false,
|
||||
showDescription = false,
|
||||
showLikes = false,
|
||||
LoadingComponent,
|
||||
pinOnSave = false,
|
||||
}: {
|
||||
feedUri: string
|
||||
style?: StyleProp<ViewStyle>
|
||||
showSaveBtn?: boolean
|
||||
showDescription?: boolean
|
||||
showLikes?: boolean
|
||||
LoadingComponent?: JSX.Element
|
||||
pinOnSave?: boolean
|
||||
}) {
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
const {data: feed} = useFeedSourceInfoQuery({uri: feedUri})
|
||||
|
||||
if (!feed || !preferences) return null
|
||||
if (!feed || !preferences) {
|
||||
return LoadingComponent ? (
|
||||
LoadingComponent
|
||||
) : (
|
||||
<FeedLoadingPlaceholder style={{flex: 1}} />
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<FeedSourceCardLoaded
|
||||
|
@ -50,6 +62,7 @@ export function FeedSourceCard({
|
|||
showSaveBtn={showSaveBtn}
|
||||
showDescription={showDescription}
|
||||
showLikes={showLikes}
|
||||
pinOnSave={pinOnSave}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -61,6 +74,7 @@ export function FeedSourceCardLoaded({
|
|||
showSaveBtn = false,
|
||||
showDescription = false,
|
||||
showLikes = false,
|
||||
pinOnSave = false,
|
||||
}: {
|
||||
feed: FeedSourceInfo
|
||||
preferences: UsePreferencesQueryResponse
|
||||
|
@ -68,6 +82,7 @@ export function FeedSourceCardLoaded({
|
|||
showSaveBtn?: boolean
|
||||
showDescription?: boolean
|
||||
showLikes?: boolean
|
||||
pinOnSave?: boolean
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
|
@ -78,6 +93,7 @@ export function FeedSourceCardLoaded({
|
|||
useSaveFeedMutation()
|
||||
const {isPending: isRemovePending, mutateAsync: removeFeed} =
|
||||
useRemoveFeedMutation()
|
||||
const {isPending: isPinPending, mutateAsync: pinFeed} = usePinFeedMutation()
|
||||
|
||||
const isSaved = Boolean(preferences?.feeds?.saved?.includes(feed.uri))
|
||||
|
||||
|
@ -103,14 +119,18 @@ export function FeedSourceCardLoaded({
|
|||
})
|
||||
} else {
|
||||
try {
|
||||
await saveFeed({uri: feed.uri})
|
||||
if (pinOnSave) {
|
||||
await pinFeed({uri: feed.uri})
|
||||
} else {
|
||||
await saveFeed({uri: feed.uri})
|
||||
}
|
||||
Toast.show('Added to my feeds')
|
||||
} catch (e) {
|
||||
Toast.show('There was an issue contacting your server')
|
||||
logger.error('Failed to save feed', {error: e})
|
||||
}
|
||||
}
|
||||
}, [isSaved, openModal, feed, removeFeed, saveFeed, _])
|
||||
}, [isSaved, openModal, feed, removeFeed, saveFeed, _, pinOnSave, pinFeed])
|
||||
|
||||
if (!feed || !preferences) return null
|
||||
|
||||
|
@ -150,7 +170,7 @@ export function FeedSourceCardLoaded({
|
|||
{showSaveBtn && feed.type === 'feed' && (
|
||||
<View>
|
||||
<Pressable
|
||||
disabled={isSavePending || isRemovePending}
|
||||
disabled={isSavePending || isPinPending || isRemovePending}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={
|
||||
isSaved ? 'Remove from my feeds' : 'Add to my feeds'
|
||||
|
|
|
@ -35,15 +35,13 @@ export function Feed({
|
|||
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||
|
||||
const moderationOpts = useModerationOpts()
|
||||
const {markAllRead} = useUnreadNotificationsApi()
|
||||
const {markAllRead, checkUnread} = useUnreadNotificationsApi()
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
isFetching,
|
||||
isFetched,
|
||||
isError,
|
||||
error,
|
||||
refetch,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
|
@ -52,13 +50,11 @@ export function Feed({
|
|||
const firstItem = data?.pages[0]?.items[0]
|
||||
|
||||
// mark all read on fresh data
|
||||
// (this will fire each time firstItem changes)
|
||||
React.useEffect(() => {
|
||||
let cleanup
|
||||
if (firstItem) {
|
||||
const to = setTimeout(() => markAllRead(), 250)
|
||||
cleanup = () => clearTimeout(to)
|
||||
markAllRead()
|
||||
}
|
||||
return cleanup
|
||||
}, [firstItem, markAllRead])
|
||||
|
||||
const items = React.useMemo(() => {
|
||||
|
@ -83,7 +79,7 @@ export function Feed({
|
|||
const onRefresh = React.useCallback(async () => {
|
||||
try {
|
||||
setIsPTRing(true)
|
||||
await refetch()
|
||||
await checkUnread({invalidate: true})
|
||||
} catch (err) {
|
||||
logger.error('Failed to refresh notifications feed', {
|
||||
error: err,
|
||||
|
@ -91,7 +87,7 @@ export function Feed({
|
|||
} finally {
|
||||
setIsPTRing(false)
|
||||
}
|
||||
}, [refetch, setIsPTRing])
|
||||
}, [checkUnread, setIsPTRing])
|
||||
|
||||
const onEndReached = React.useCallback(async () => {
|
||||
if (isFetching || !hasNextPage || isError) return
|
||||
|
@ -136,21 +132,6 @@ export function Feed({
|
|||
[onPressRetryLoadMore, moderationOpts],
|
||||
)
|
||||
|
||||
const showHeaderSpinner = !isPTRing && isFetching && !isLoading
|
||||
const FeedHeader = React.useCallback(
|
||||
() => (
|
||||
<View>
|
||||
{ListHeaderComponent ? <ListHeaderComponent /> : null}
|
||||
{showHeaderSpinner ? (
|
||||
<View style={{padding: 10}}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
),
|
||||
[ListHeaderComponent, showHeaderSpinner],
|
||||
)
|
||||
|
||||
const FeedFooter = React.useCallback(
|
||||
() =>
|
||||
isFetchingNextPage ? (
|
||||
|
@ -180,7 +161,7 @@ export function Feed({
|
|||
data={items}
|
||||
keyExtractor={item => item._reactKey}
|
||||
renderItem={renderItem}
|
||||
ListHeaderComponent={FeedHeader}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
ListFooterComponent={FeedFooter}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
|
|
|
@ -12,6 +12,9 @@ import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
|||
import {useSession} from '#/state/session'
|
||||
import {TextLink} from '#/view/com/util/Link'
|
||||
import {CenteredView} from '../util/Views'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
|
||||
export function FeedsTabBar(
|
||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
||||
|
@ -79,11 +82,37 @@ function FeedsTabBarPublic() {
|
|||
function FeedsTabBarTablet(
|
||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
||||
) {
|
||||
const feeds = usePinnedFeedsInfos()
|
||||
const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
|
||||
const pal = usePalette('default')
|
||||
const {hasSession} = useSession()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {headerMinimalShellTransform} = useMinimalShellMode()
|
||||
const {headerHeight} = useShellLayout()
|
||||
const items = feeds.map(f => f.displayName)
|
||||
const pinnedDisplayNames = hasSession ? feeds.map(f => f.displayName) : []
|
||||
const showFeedsLinkInTabBar = hasSession && !hasPinnedCustom
|
||||
const items = showFeedsLinkInTabBar
|
||||
? pinnedDisplayNames.concat('Feeds ✨')
|
||||
: pinnedDisplayNames
|
||||
|
||||
const onPressDiscoverFeeds = React.useCallback(() => {
|
||||
if (isWeb) {
|
||||
navigation.navigate('Feeds')
|
||||
} else {
|
||||
navigation.navigate('FeedsTab')
|
||||
navigation.popToTop()
|
||||
}
|
||||
}, [navigation])
|
||||
|
||||
const onSelect = React.useCallback(
|
||||
(index: number) => {
|
||||
if (showFeedsLinkInTabBar && index === items.length - 1) {
|
||||
onPressDiscoverFeeds()
|
||||
} else if (props.onSelect) {
|
||||
props.onSelect(index)
|
||||
}
|
||||
},
|
||||
[items.length, onPressDiscoverFeeds, props, showFeedsLinkInTabBar],
|
||||
)
|
||||
|
||||
return (
|
||||
// @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
|
||||
|
@ -95,6 +124,7 @@ function FeedsTabBarTablet(
|
|||
<TabBar
|
||||
key={items.join(',')}
|
||||
{...props}
|
||||
onSelect={onSelect}
|
||||
items={items}
|
||||
indicatorColor={pal.colors.link}
|
||||
/>
|
||||
|
|
|
@ -18,6 +18,9 @@ import {useSetDrawerOpen} from '#/state/shell/drawer-open'
|
|||
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||
import {useSession} from '#/state/session'
|
||||
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
|
||||
export function FeedsTabBar(
|
||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
||||
|
@ -26,11 +29,36 @@ export function FeedsTabBar(
|
|||
const {isSandbox, hasSession} = useSession()
|
||||
const {_} = useLingui()
|
||||
const setDrawerOpen = useSetDrawerOpen()
|
||||
const feeds = usePinnedFeedsInfos()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
|
||||
const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3)
|
||||
const {headerHeight} = useShellLayout()
|
||||
const {headerMinimalShellTransform} = useMinimalShellMode()
|
||||
const items = feeds.map(f => f.displayName)
|
||||
const pinnedDisplayNames = hasSession ? feeds.map(f => f.displayName) : []
|
||||
const showFeedsLinkInTabBar = hasSession && !hasPinnedCustom
|
||||
const items = showFeedsLinkInTabBar
|
||||
? pinnedDisplayNames.concat('Feeds ✨')
|
||||
: pinnedDisplayNames
|
||||
|
||||
const onPressFeedsLink = React.useCallback(() => {
|
||||
if (isWeb) {
|
||||
navigation.navigate('Feeds')
|
||||
} else {
|
||||
navigation.navigate('FeedsTab')
|
||||
navigation.popToTop()
|
||||
}
|
||||
}, [navigation])
|
||||
|
||||
const onSelect = React.useCallback(
|
||||
(index: number) => {
|
||||
if (showFeedsLinkInTabBar && index === items.length - 1) {
|
||||
onPressFeedsLink()
|
||||
} else if (props.onSelect) {
|
||||
props.onSelect(index)
|
||||
}
|
||||
},
|
||||
[items.length, onPressFeedsLink, props, showFeedsLinkInTabBar],
|
||||
)
|
||||
|
||||
const onPressAvi = React.useCallback(() => {
|
||||
setDrawerOpen(true)
|
||||
|
@ -84,7 +112,7 @@ export function FeedsTabBar(
|
|||
key={items.join(',')}
|
||||
onPressSelected={props.onPressSelected}
|
||||
selectedPage={props.selectedPage}
|
||||
onSelect={props.onSelect}
|
||||
onSelect={onSelect}
|
||||
testID={props.testID}
|
||||
items={items}
|
||||
indicatorColor={pal.colors.link}
|
||||
|
|
|
@ -108,6 +108,7 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
|||
pointerEvents: isHeaderReady ? 'auto' : 'none',
|
||||
}}>
|
||||
<TabBar
|
||||
testID={testID}
|
||||
items={items}
|
||||
selectedPage={currentPage}
|
||||
onSelect={props.onSelect}
|
||||
|
@ -127,6 +128,7 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
|||
isMobile,
|
||||
onTabBarLayout,
|
||||
onHeaderOnlyLayout,
|
||||
testID,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -68,6 +68,7 @@ export function TabBar({
|
|||
return (
|
||||
<View testID={testID} style={[pal.view, styles.outer]}>
|
||||
<DraggableScrollView
|
||||
testID={`${testID}-selector`}
|
||||
horizontal={true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
ref={scrollElRef}
|
||||
|
@ -76,6 +77,7 @@ export function TabBar({
|
|||
const selected = i === selectedPage
|
||||
return (
|
||||
<PressableWithHover
|
||||
testID={`${testID}-selector-${i}`}
|
||||
key={item}
|
||||
onLayout={e => onItemLayout(e, i)}
|
||||
style={[styles.item, selected && indicatorStyle]}
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
FeedDescriptor,
|
||||
FeedParams,
|
||||
usePostFeedQuery,
|
||||
pollLatest,
|
||||
} from '#/state/queries/post-feed'
|
||||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
|
||||
|
@ -84,22 +85,21 @@ let Feed = ({
|
|||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
pollLatest,
|
||||
} = usePostFeedQuery(feed, feedParams, opts)
|
||||
const isEmpty = !isFetching && !data?.pages[0]?.slices.length
|
||||
|
||||
const checkForNew = React.useCallback(async () => {
|
||||
if (!isFetched || isFetching || !onHasNew) {
|
||||
if (!data?.pages[0] || isFetching || !onHasNew) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
if (await pollLatest()) {
|
||||
if (await pollLatest(data.pages[0])) {
|
||||
onHasNew(true)
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('Poll latest failed', {feed, error: String(e)})
|
||||
}
|
||||
}, [feed, isFetched, isFetching, pollLatest, onHasNew])
|
||||
}, [feed, data, isFetching, onHasNew])
|
||||
|
||||
React.useEffect(() => {
|
||||
// we store the interval handler in a ref to avoid needless
|
||||
|
|
|
@ -17,28 +17,15 @@ import {EmptyState} from '../util/EmptyState'
|
|||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useRemoveFeedMutation} from '#/state/queries/preferences'
|
||||
|
||||
enum KnownError {
|
||||
Block,
|
||||
FeedgenDoesNotExist,
|
||||
FeedgenMisconfigured,
|
||||
FeedgenBadResponse,
|
||||
FeedgenOffline,
|
||||
FeedgenUnknown,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
const MESSAGES = {
|
||||
[KnownError.Unknown]: '',
|
||||
[KnownError.Block]: '',
|
||||
[KnownError.FeedgenDoesNotExist]: `Hmmm, we're having trouble finding this feed. It may have been deleted.`,
|
||||
[KnownError.FeedgenMisconfigured]:
|
||||
'Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue.',
|
||||
[KnownError.FeedgenBadResponse]:
|
||||
'Hmm, the feed server gave a bad response. Please let the feed owner know about this issue.',
|
||||
[KnownError.FeedgenOffline]:
|
||||
'Hmm, the feed server appears to be offline. Please let the feed owner know about this issue.',
|
||||
[KnownError.FeedgenUnknown]:
|
||||
'Hmm, some kind of issue occured when contacting the feed server. Please let the feed owner know about this issue.',
|
||||
export enum KnownError {
|
||||
Block = 'Block',
|
||||
FeedgenDoesNotExist = 'FeedgenDoesNotExist',
|
||||
FeedgenMisconfigured = 'FeedgenMisconfigured',
|
||||
FeedgenBadResponse = 'FeedgenBadResponse',
|
||||
FeedgenOffline = 'FeedgenOffline',
|
||||
FeedgenUnknown = 'FeedgenUnknown',
|
||||
FeedNSFPublic = 'FeedNSFPublic',
|
||||
Unknown = 'Unknown',
|
||||
}
|
||||
|
||||
export function FeedErrorMessage({
|
||||
|
@ -90,7 +77,32 @@ function FeedgenErrorMessage({
|
|||
const pal = usePalette('default')
|
||||
const {_: _l} = useLingui()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const msg = MESSAGES[knownError]
|
||||
const msg = React.useMemo(
|
||||
() =>
|
||||
({
|
||||
[KnownError.Unknown]: '',
|
||||
[KnownError.Block]: '',
|
||||
[KnownError.FeedgenDoesNotExist]: _l(
|
||||
msgLingui`Hmmm, we're having trouble finding this feed. It may have been deleted.`,
|
||||
),
|
||||
[KnownError.FeedgenMisconfigured]: _l(
|
||||
msgLingui`Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue.`,
|
||||
),
|
||||
[KnownError.FeedgenBadResponse]: _l(
|
||||
msgLingui`Hmm, the feed server gave a bad response. Please let the feed owner know about this issue.`,
|
||||
),
|
||||
[KnownError.FeedgenOffline]: _l(
|
||||
msgLingui`Hmm, the feed server appears to be offline. Please let the feed owner know about this issue.`,
|
||||
),
|
||||
[KnownError.FeedNSFPublic]: _l(
|
||||
msgLingui`We're sorry, but this content is not viewable without a Bluesky account.`,
|
||||
),
|
||||
[KnownError.FeedgenUnknown]: _l(
|
||||
msgLingui`Hmm, some kind of issue occured when contacting the feed server. Please let the feed owner know about this issue.`,
|
||||
),
|
||||
}[knownError]),
|
||||
[_l, knownError],
|
||||
)
|
||||
const [_, uri] = feedDesc.split('|')
|
||||
const [ownerDid] = safeParseFeedgenUri(uri)
|
||||
const {openModal, closeModal} = useModalControls()
|
||||
|
@ -121,6 +133,36 @@ function FeedgenErrorMessage({
|
|||
})
|
||||
}, [openModal, closeModal, uri, removeFeed, _l])
|
||||
|
||||
const cta = React.useMemo(() => {
|
||||
switch (knownError) {
|
||||
case KnownError.FeedNSFPublic: {
|
||||
return null
|
||||
}
|
||||
case KnownError.FeedgenDoesNotExist:
|
||||
case KnownError.FeedgenMisconfigured:
|
||||
case KnownError.FeedgenBadResponse:
|
||||
case KnownError.FeedgenOffline:
|
||||
case KnownError.FeedgenUnknown: {
|
||||
return (
|
||||
<View style={{flexDirection: 'row', alignItems: 'center', gap: 10}}>
|
||||
{knownError === KnownError.FeedgenDoesNotExist && (
|
||||
<Button
|
||||
type="inverted"
|
||||
label="Remove feed"
|
||||
onPress={onRemoveFeed}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
type="default-light"
|
||||
label="View profile"
|
||||
onPress={onViewProfile}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
}, [knownError, onViewProfile, onRemoveFeed])
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
|
@ -134,16 +176,7 @@ function FeedgenErrorMessage({
|
|||
},
|
||||
]}>
|
||||
<Text style={pal.text}>{msg}</Text>
|
||||
<View style={{flexDirection: 'row', alignItems: 'center', gap: 10}}>
|
||||
{knownError === KnownError.FeedgenDoesNotExist && (
|
||||
<Button type="inverted" label="Remove feed" onPress={onRemoveFeed} />
|
||||
)}
|
||||
<Button
|
||||
type="default-light"
|
||||
label="View profile"
|
||||
onPress={onViewProfile}
|
||||
/>
|
||||
</View>
|
||||
{cta}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
@ -196,5 +229,8 @@ function detectKnownError(
|
|||
if (error.includes('feed provided an invalid response')) {
|
||||
return KnownError.FeedgenBadResponse
|
||||
}
|
||||
if (error.includes(KnownError.FeedNSFPublic)) {
|
||||
return KnownError.FeedNSFPublic
|
||||
}
|
||||
return KnownError.FeedgenUnknown
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ interface Props extends ComponentProps<typeof TouchableOpacity> {
|
|||
noFeedback?: boolean
|
||||
asAnchor?: boolean
|
||||
anchorNoUnderline?: boolean
|
||||
navigationAction?: 'push' | 'replace' | 'navigate'
|
||||
}
|
||||
|
||||
export const Link = memo(function Link({
|
||||
|
@ -58,6 +59,7 @@ export const Link = memo(function Link({
|
|||
asAnchor,
|
||||
accessible,
|
||||
anchorNoUnderline,
|
||||
navigationAction,
|
||||
...props
|
||||
}: Props) {
|
||||
const {closeModal} = useModalControls()
|
||||
|
@ -67,10 +69,16 @@ export const Link = memo(function Link({
|
|||
const onPress = React.useCallback(
|
||||
(e?: Event) => {
|
||||
if (typeof href === 'string') {
|
||||
return onPressInner(closeModal, navigation, sanitizeUrl(href), e)
|
||||
return onPressInner(
|
||||
closeModal,
|
||||
navigation,
|
||||
sanitizeUrl(href),
|
||||
navigationAction,
|
||||
e,
|
||||
)
|
||||
}
|
||||
},
|
||||
[closeModal, navigation, href],
|
||||
[closeModal, navigation, navigationAction, href],
|
||||
)
|
||||
|
||||
if (noFeedback) {
|
||||
|
@ -146,6 +154,7 @@ export const TextLink = memo(function TextLink({
|
|||
title,
|
||||
onPress,
|
||||
warnOnMismatchingLabel,
|
||||
navigationAction,
|
||||
...orgProps
|
||||
}: {
|
||||
testID?: string
|
||||
|
@ -158,6 +167,7 @@ export const TextLink = memo(function TextLink({
|
|||
dataSet?: any
|
||||
title?: string
|
||||
warnOnMismatchingLabel?: boolean
|
||||
navigationAction?: 'push' | 'replace' | 'navigate'
|
||||
} & TextProps) {
|
||||
const {...props} = useLinkProps({to: sanitizeUrl(href)})
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
|
@ -185,7 +195,13 @@ export const TextLink = memo(function TextLink({
|
|||
// @ts-ignore function signature differs by platform -prf
|
||||
return onPress()
|
||||
}
|
||||
return onPressInner(closeModal, navigation, sanitizeUrl(href), e)
|
||||
return onPressInner(
|
||||
closeModal,
|
||||
navigation,
|
||||
sanitizeUrl(href),
|
||||
navigationAction,
|
||||
e,
|
||||
)
|
||||
},
|
||||
[
|
||||
onPress,
|
||||
|
@ -195,6 +211,7 @@ export const TextLink = memo(function TextLink({
|
|||
href,
|
||||
text,
|
||||
warnOnMismatchingLabel,
|
||||
navigationAction,
|
||||
],
|
||||
)
|
||||
const hrefAttrs = useMemo(() => {
|
||||
|
@ -241,6 +258,7 @@ interface TextLinkOnWebOnlyProps extends TextProps {
|
|||
accessibilityLabel?: string
|
||||
accessibilityHint?: string
|
||||
title?: string
|
||||
navigationAction?: 'push' | 'replace' | 'navigate'
|
||||
}
|
||||
export const TextLinkOnWebOnly = memo(function DesktopWebTextLink({
|
||||
testID,
|
||||
|
@ -250,6 +268,7 @@ export const TextLinkOnWebOnly = memo(function DesktopWebTextLink({
|
|||
text,
|
||||
numberOfLines,
|
||||
lineHeight,
|
||||
navigationAction,
|
||||
...props
|
||||
}: TextLinkOnWebOnlyProps) {
|
||||
if (isWeb) {
|
||||
|
@ -263,6 +282,7 @@ export const TextLinkOnWebOnly = memo(function DesktopWebTextLink({
|
|||
numberOfLines={numberOfLines}
|
||||
lineHeight={lineHeight}
|
||||
title={props.title}
|
||||
navigationAction={navigationAction}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
@ -296,6 +316,7 @@ function onPressInner(
|
|||
closeModal = () => {},
|
||||
navigation: NavigationProp,
|
||||
href: string,
|
||||
navigationAction: 'push' | 'replace' | 'navigate' = 'push',
|
||||
e?: Event,
|
||||
) {
|
||||
let shouldHandle = false
|
||||
|
@ -328,8 +349,18 @@ function onPressInner(
|
|||
} else {
|
||||
closeModal() // close any active modals
|
||||
|
||||
// @ts-ignore we're not able to type check on this one -prf
|
||||
navigation.dispatch(StackActions.push(...router.matchPath(href)))
|
||||
if (navigationAction === 'push') {
|
||||
// @ts-ignore we're not able to type check on this one -prf
|
||||
navigation.dispatch(StackActions.push(...router.matchPath(href)))
|
||||
} else if (navigationAction === 'replace') {
|
||||
// @ts-ignore we're not able to type check on this one -prf
|
||||
navigation.dispatch(StackActions.replace(...router.matchPath(href)))
|
||||
} else if (navigationAction === 'navigate') {
|
||||
// @ts-ignore we're not able to type check on this one -prf
|
||||
navigation.navigate(...router.matchPath(href))
|
||||
} else {
|
||||
throw Error('Unsupported navigator action.')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,14 +171,22 @@ export function ProfileCardFeedLoadingPlaceholder() {
|
|||
|
||||
export function FeedLoadingPlaceholder({
|
||||
style,
|
||||
showLowerPlaceholder = true,
|
||||
showTopBorder = true,
|
||||
}: {
|
||||
style?: StyleProp<ViewStyle>
|
||||
showTopBorder?: boolean
|
||||
showLowerPlaceholder?: boolean
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
{paddingHorizontal: 12, paddingVertical: 18, borderTopWidth: 1},
|
||||
{
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 18,
|
||||
borderTopWidth: showTopBorder ? 1 : 0,
|
||||
},
|
||||
pal.border,
|
||||
style,
|
||||
]}>
|
||||
|
@ -193,14 +201,16 @@ export function FeedLoadingPlaceholder({
|
|||
<LoadingPlaceholder width={120} height={8} />
|
||||
</View>
|
||||
</View>
|
||||
<View style={{paddingHorizontal: 5}}>
|
||||
<LoadingPlaceholder
|
||||
width={260}
|
||||
height={8}
|
||||
style={{marginVertical: 12}}
|
||||
/>
|
||||
<LoadingPlaceholder width={120} height={8} />
|
||||
</View>
|
||||
{showLowerPlaceholder && (
|
||||
<View style={{paddingHorizontal: 5}}>
|
||||
<LoadingPlaceholder
|
||||
width={260}
|
||||
height={8}
|
||||
style={{marginVertical: 12}}
|
||||
/>
|
||||
<LoadingPlaceholder width={120} height={8} />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@ export function FeedsScreen(_props: Props) {
|
|||
showSaveBtn={hasSession}
|
||||
showDescription
|
||||
showLikes
|
||||
pinOnSave
|
||||
/>
|
||||
)
|
||||
} else if (item.type === 'popularFeedsNoResults') {
|
||||
|
|
|
@ -40,6 +40,12 @@ function HomeScreenReady({
|
|||
const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
|
||||
const [selectedPage, setSelectedPage] = React.useState(0)
|
||||
|
||||
/**
|
||||
* Used to ensure that we re-compute `customFeeds` AND force a re-render of
|
||||
* the pager with the new order of feeds.
|
||||
*/
|
||||
const pinnedFeedOrderKey = JSON.stringify(preferences.feeds.pinned)
|
||||
|
||||
const customFeeds = React.useMemo(() => {
|
||||
const pinned = preferences.feeds.pinned
|
||||
const feeds: FeedDescriptor[] = []
|
||||
|
@ -83,7 +89,6 @@ function HomeScreenReady({
|
|||
emitSoftReset()
|
||||
}, [])
|
||||
|
||||
// TODO(pwi) may need this in public view
|
||||
const onPageScrollStateChanged = React.useCallback(
|
||||
(state: 'idle' | 'dragging' | 'settling') => {
|
||||
if (state === 'dragging') {
|
||||
|
@ -118,6 +123,7 @@ function HomeScreenReady({
|
|||
|
||||
return hasSession ? (
|
||||
<Pager
|
||||
key={pinnedFeedOrderKey}
|
||||
testID="homeScreen"
|
||||
onPageSelected={onPageSelected}
|
||||
onPageScrollStateChanged={onPageScrollStateChanged}
|
||||
|
|
|
@ -19,7 +19,10 @@ import {logger} from '#/logger'
|
|||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
||||
import {
|
||||
useUnreadNotifications,
|
||||
useUnreadNotificationsApi,
|
||||
} from '#/state/queries/notifications/unread'
|
||||
import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed'
|
||||
import {listenSoftReset, emitSoftReset} from '#/state/events'
|
||||
|
||||
|
@ -35,8 +38,9 @@ export function NotificationsScreen({}: Props) {
|
|||
const {screen} = useAnalytics()
|
||||
const pal = usePalette('default')
|
||||
const {isDesktop} = useWebMediaQueries()
|
||||
const unreadNotifs = useUnreadNotifications()
|
||||
const queryClient = useQueryClient()
|
||||
const unreadNotifs = useUnreadNotifications()
|
||||
const unreadApi = useUnreadNotificationsApi()
|
||||
const hasNew = !!unreadNotifs
|
||||
|
||||
// event handlers
|
||||
|
@ -48,10 +52,16 @@ export function NotificationsScreen({}: Props) {
|
|||
|
||||
const onPressLoadLatest = React.useCallback(() => {
|
||||
scrollToTop()
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: NOTIFS_RQKEY(),
|
||||
})
|
||||
}, [scrollToTop, queryClient])
|
||||
if (hasNew) {
|
||||
// render what we have now
|
||||
queryClient.resetQueries({
|
||||
queryKey: NOTIFS_RQKEY(),
|
||||
})
|
||||
} else {
|
||||
// check with the server
|
||||
unreadApi.checkUnread({invalidate: true})
|
||||
}
|
||||
}, [scrollToTop, queryClient, unreadApi, hasNew])
|
||||
|
||||
// on-visible setup
|
||||
// =
|
||||
|
|
|
@ -267,6 +267,7 @@ function ProfileScreenLoaded({
|
|||
screenDescription="profile"
|
||||
moderation={moderation.account}>
|
||||
<PagerWithHeader
|
||||
testID="profilePager"
|
||||
isHeaderReady={true}
|
||||
items={sectionTitles}
|
||||
onPageSelected={onPageSelected}
|
||||
|
@ -403,7 +404,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
|||
|
||||
const onScrollToTop = React.useCallback(() => {
|
||||
scrollElRef.current?.scrollToOffset({offset: -headerHeight})
|
||||
queryClient.invalidateQueries({queryKey: FEED_RQKEY(feed)})
|
||||
queryClient.resetQueries({queryKey: FEED_RQKEY(feed)})
|
||||
setHasNew(false)
|
||||
}, [scrollElRef, headerHeight, queryClient, feed, setHasNew])
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
|
|
|
@ -353,6 +353,7 @@ export function ProfileFeedScreenInner({
|
|||
style={styles.btn}
|
||||
/>
|
||||
<Button
|
||||
testID={isPinned ? 'unpinBtn' : 'pinBtn'}
|
||||
disabled={isPinPending || isUnpinPending}
|
||||
type={isPinned ? 'default' : 'inverted'}
|
||||
label={isPinned ? 'Unpin' : 'Pin to home'}
|
||||
|
@ -501,7 +502,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
|||
|
||||
const onScrollToTop = useCallback(() => {
|
||||
scrollElRef.current?.scrollToOffset({offset: -headerHeight})
|
||||
queryClient.invalidateQueries({queryKey: FEED_RQKEY(feed)})
|
||||
queryClient.resetQueries({queryKey: FEED_RQKEY(feed)})
|
||||
setHasNew(false)
|
||||
}, [scrollElRef, headerHeight, queryClient, feed, setHasNew])
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ function ProfileListScreenLoaded({
|
|||
list,
|
||||
onChange() {
|
||||
if (isCurateList) {
|
||||
queryClient.invalidateQueries({
|
||||
queryClient.resetQueries({
|
||||
// TODO(eric) should construct these strings with a fn too
|
||||
queryKey: FEED_RQKEY(`list|${list.uri}`),
|
||||
})
|
||||
|
@ -530,7 +530,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
|
|||
|
||||
const onScrollToTop = useCallback(() => {
|
||||
scrollElRef.current?.scrollToOffset({offset: -headerHeight})
|
||||
queryClient.invalidateQueries({queryKey: FEED_RQKEY(feed)})
|
||||
queryClient.resetQueries({queryKey: FEED_RQKEY(feed)})
|
||||
setHasNew(false)
|
||||
}, [scrollElRef, headerHeight, queryClient, feed, setHasNew])
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
ActivityIndicator,
|
||||
Pressable,
|
||||
TouchableOpacity,
|
||||
} from 'react-native'
|
||||
import {StyleSheet, View, ActivityIndicator, Pressable} from 'react-native'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {track} from '#/lib/analytics/analytics'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
|
@ -32,9 +25,8 @@ import {
|
|||
usePinFeedMutation,
|
||||
useUnpinFeedMutation,
|
||||
useSetSaveFeedsMutation,
|
||||
preferencesQueryKey,
|
||||
UsePreferencesQueryResponse,
|
||||
} from '#/state/queries/preferences'
|
||||
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||
|
||||
const HITSLOP_TOP = {
|
||||
top: 20,
|
||||
|
@ -57,6 +49,24 @@ export function SavedFeeds({}: Props) {
|
|||
const {screen} = useAnalytics()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
const {
|
||||
mutateAsync: setSavedFeeds,
|
||||
variables: optimisticSavedFeedsResponse,
|
||||
reset: resetSaveFeedsMutationState,
|
||||
error: setSavedFeedsError,
|
||||
} = useSetSaveFeedsMutation()
|
||||
|
||||
/*
|
||||
* Use optimistic data if exists and no error, otherwise fallback to remote
|
||||
* data
|
||||
*/
|
||||
const currentFeeds =
|
||||
optimisticSavedFeedsResponse && !setSavedFeedsError
|
||||
? optimisticSavedFeedsResponse
|
||||
: preferences?.feeds || {saved: [], pinned: []}
|
||||
const unpinned = currentFeeds.saved.filter(f => {
|
||||
return !currentFeeds.pinned?.includes(f)
|
||||
})
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -80,7 +90,7 @@ export function SavedFeeds({}: Props) {
|
|||
</Text>
|
||||
</View>
|
||||
{preferences?.feeds ? (
|
||||
!preferences.feeds.pinned.length ? (
|
||||
!currentFeeds.pinned.length ? (
|
||||
<View
|
||||
style={[
|
||||
pal.border,
|
||||
|
@ -93,8 +103,15 @@ export function SavedFeeds({}: Props) {
|
|||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
preferences?.feeds?.pinned?.map(uri => (
|
||||
<ListItem key={uri} feedUri={uri} isPinned />
|
||||
currentFeeds.pinned.map(uri => (
|
||||
<ListItem
|
||||
key={uri}
|
||||
feedUri={uri}
|
||||
isPinned
|
||||
setSavedFeeds={setSavedFeeds}
|
||||
resetSaveFeedsMutationState={resetSaveFeedsMutationState}
|
||||
currentFeeds={currentFeeds}
|
||||
/>
|
||||
))
|
||||
)
|
||||
) : (
|
||||
|
@ -106,7 +123,7 @@ export function SavedFeeds({}: Props) {
|
|||
</Text>
|
||||
</View>
|
||||
{preferences?.feeds ? (
|
||||
!preferences.feeds.unpinned.length ? (
|
||||
!unpinned.length ? (
|
||||
<View
|
||||
style={[
|
||||
pal.border,
|
||||
|
@ -119,8 +136,15 @@ export function SavedFeeds({}: Props) {
|
|||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
preferences.feeds.unpinned.map(uri => (
|
||||
<ListItem key={uri} feedUri={uri} isPinned={false} />
|
||||
unpinned.map(uri => (
|
||||
<ListItem
|
||||
key={uri}
|
||||
feedUri={uri}
|
||||
isPinned={false}
|
||||
setSavedFeeds={setSavedFeeds}
|
||||
resetSaveFeedsMutationState={resetSaveFeedsMutationState}
|
||||
currentFeeds={currentFeeds}
|
||||
/>
|
||||
))
|
||||
)
|
||||
) : (
|
||||
|
@ -151,22 +175,30 @@ export function SavedFeeds({}: Props) {
|
|||
function ListItem({
|
||||
feedUri,
|
||||
isPinned,
|
||||
currentFeeds,
|
||||
setSavedFeeds,
|
||||
resetSaveFeedsMutationState,
|
||||
}: {
|
||||
feedUri: string // uri
|
||||
isPinned: boolean
|
||||
currentFeeds: {saved: string[]; pinned: string[]}
|
||||
setSavedFeeds: ReturnType<typeof useSetSaveFeedsMutation>['mutateAsync']
|
||||
resetSaveFeedsMutationState: ReturnType<
|
||||
typeof useSetSaveFeedsMutation
|
||||
>['reset']
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const queryClient = useQueryClient()
|
||||
const {isPending: isPinPending, mutateAsync: pinFeed} = usePinFeedMutation()
|
||||
const {isPending: isUnpinPending, mutateAsync: unpinFeed} =
|
||||
useUnpinFeedMutation()
|
||||
const {isPending: isMovePending, mutateAsync: setSavedFeeds} =
|
||||
useSetSaveFeedsMutation()
|
||||
const isPending = isPinPending || isUnpinPending
|
||||
|
||||
const onTogglePinned = React.useCallback(async () => {
|
||||
Haptics.default()
|
||||
|
||||
try {
|
||||
resetSaveFeedsMutationState()
|
||||
|
||||
if (isPinned) {
|
||||
await unpinFeed({uri: feedUri})
|
||||
} else {
|
||||
|
@ -176,23 +208,20 @@ function ListItem({
|
|||
Toast.show('There was an issue contacting the server')
|
||||
logger.error('Failed to toggle pinned feed', {error: e})
|
||||
}
|
||||
}, [feedUri, isPinned, pinFeed, unpinFeed])
|
||||
}, [feedUri, isPinned, pinFeed, unpinFeed, resetSaveFeedsMutationState])
|
||||
|
||||
const onPressUp = React.useCallback(async () => {
|
||||
if (!isPinned) return
|
||||
|
||||
const feeds =
|
||||
queryClient.getQueryData<UsePreferencesQueryResponse>(
|
||||
preferencesQueryKey,
|
||||
)?.feeds
|
||||
const pinned = feeds?.pinned ?? []
|
||||
// create new array, do not mutate
|
||||
const pinned = [...currentFeeds.pinned]
|
||||
const index = pinned.indexOf(feedUri)
|
||||
|
||||
if (index === -1 || index === 0) return
|
||||
;[pinned[index], pinned[index - 1]] = [pinned[index - 1], pinned[index]]
|
||||
|
||||
try {
|
||||
await setSavedFeeds({saved: feeds?.saved ?? [], pinned})
|
||||
await setSavedFeeds({saved: currentFeeds.saved, pinned})
|
||||
track('CustomFeed:Reorder', {
|
||||
uri: feedUri,
|
||||
index: pinned.indexOf(feedUri),
|
||||
|
@ -201,23 +230,19 @@ function ListItem({
|
|||
Toast.show('There was an issue contacting the server')
|
||||
logger.error('Failed to set pinned feed order', {error: e})
|
||||
}
|
||||
}, [feedUri, isPinned, queryClient, setSavedFeeds])
|
||||
}, [feedUri, isPinned, setSavedFeeds, currentFeeds])
|
||||
|
||||
const onPressDown = React.useCallback(async () => {
|
||||
if (!isPinned) return
|
||||
|
||||
const feeds =
|
||||
queryClient.getQueryData<UsePreferencesQueryResponse>(
|
||||
preferencesQueryKey,
|
||||
)?.feeds
|
||||
const pinned = feeds?.pinned ?? []
|
||||
const pinned = [...currentFeeds.pinned]
|
||||
const index = pinned.indexOf(feedUri)
|
||||
|
||||
if (index === -1 || index >= pinned.length - 1) return
|
||||
;[pinned[index], pinned[index + 1]] = [pinned[index + 1], pinned[index]]
|
||||
|
||||
try {
|
||||
await setSavedFeeds({saved: feeds?.saved ?? [], pinned})
|
||||
await setSavedFeeds({saved: currentFeeds.saved, pinned})
|
||||
track('CustomFeed:Reorder', {
|
||||
uri: feedUri,
|
||||
index: pinned.indexOf(feedUri),
|
||||
|
@ -226,7 +251,7 @@ function ListItem({
|
|||
Toast.show('There was an issue contacting the server')
|
||||
logger.error('Failed to set pinned feed order', {error: e})
|
||||
}
|
||||
}, [feedUri, isPinned, queryClient, setSavedFeeds])
|
||||
}, [feedUri, isPinned, setSavedFeeds, currentFeeds])
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
|
@ -234,24 +259,30 @@ function ListItem({
|
|||
style={[styles.itemContainer, pal.border]}>
|
||||
{isPinned ? (
|
||||
<View style={styles.webArrowButtonsContainer}>
|
||||
<TouchableOpacity
|
||||
disabled={isMovePending}
|
||||
<Pressable
|
||||
disabled={isPending}
|
||||
accessibilityRole="button"
|
||||
onPress={onPressUp}
|
||||
hitSlop={HITSLOP_TOP}>
|
||||
hitSlop={HITSLOP_TOP}
|
||||
style={state => ({
|
||||
opacity: state.hovered || state.focused || isPending ? 0.5 : 1,
|
||||
})}>
|
||||
<FontAwesomeIcon
|
||||
icon="arrow-up"
|
||||
size={12}
|
||||
style={[pal.text, styles.webArrowUpButton]}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
disabled={isMovePending}
|
||||
</Pressable>
|
||||
<Pressable
|
||||
disabled={isPending}
|
||||
accessibilityRole="button"
|
||||
onPress={onPressDown}
|
||||
hitSlop={HITSLOP_BOTTOM}>
|
||||
hitSlop={HITSLOP_BOTTOM}
|
||||
style={state => ({
|
||||
opacity: state.hovered || state.focused || isPending ? 0.5 : 1,
|
||||
})}>
|
||||
<FontAwesomeIcon icon="arrow-down" size={12} style={[pal.text]} />
|
||||
</TouchableOpacity>
|
||||
</Pressable>
|
||||
</View>
|
||||
) : null}
|
||||
<FeedSourceCard
|
||||
|
@ -259,18 +290,28 @@ function ListItem({
|
|||
feedUri={feedUri}
|
||||
style={styles.noBorder}
|
||||
showSaveBtn
|
||||
LoadingComponent={
|
||||
<FeedLoadingPlaceholder
|
||||
style={{flex: 1}}
|
||||
showLowerPlaceholder={false}
|
||||
showTopBorder={false}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
disabled={isPinPending || isUnpinPending}
|
||||
<Pressable
|
||||
disabled={isPending}
|
||||
accessibilityRole="button"
|
||||
hitSlop={10}
|
||||
onPress={onTogglePinned}>
|
||||
onPress={onTogglePinned}
|
||||
style={state => ({
|
||||
opacity: state.hovered || state.focused || isPending ? 0.5 : 1,
|
||||
})}>
|
||||
<FontAwesomeIcon
|
||||
icon="thumb-tack"
|
||||
size={20}
|
||||
color={isPinned ? colors.blue3 : pal.colors.icon}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</Pressable>
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -10,11 +10,7 @@ import {
|
|||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {
|
||||
useFocusEffect,
|
||||
useNavigation,
|
||||
StackActions,
|
||||
} from '@react-navigation/native'
|
||||
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
||||
import {
|
||||
FontAwesomeIcon,
|
||||
FontAwesomeIconStyle,
|
||||
|
@ -73,6 +69,8 @@ import {STATUS_PAGE_URL} from 'lib/constants'
|
|||
import {Plural, Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||
import {useCloseAllActiveElements} from '#/state/util'
|
||||
|
||||
function SettingsAccountCard({account}: {account: SessionAccount}) {
|
||||
const pal = usePalette('default')
|
||||
|
@ -154,13 +152,14 @@ export function SettingsScreen({}: Props) {
|
|||
const {screen, track} = useAnalytics()
|
||||
const {openModal} = useModalControls()
|
||||
const {isSwitchingAccounts, accounts, currentAccount} = useSession()
|
||||
const {clearCurrentAccount} = useSessionApi()
|
||||
const [debugHeaderEnabled, toggleDebugHeader] = useDebugHeaderSetting(
|
||||
getAgent(),
|
||||
)
|
||||
const {mutate: clearPreferences} = useClearPreferencesMutation()
|
||||
const {data: invites} = useInviteCodesQuery()
|
||||
const invitesAvailable = invites?.available?.length ?? 0
|
||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||
const closeAllActiveElements = useCloseAllActiveElements()
|
||||
|
||||
const primaryBg = useCustomPalette<ViewStyle>({
|
||||
light: {backgroundColor: colors.blue0},
|
||||
|
@ -189,10 +188,9 @@ export function SettingsScreen({}: Props) {
|
|||
|
||||
const onPressAddAccount = React.useCallback(() => {
|
||||
track('Settings:AddAccountButtonClicked')
|
||||
navigation.navigate('HomeTab')
|
||||
navigation.dispatch(StackActions.popToTop())
|
||||
clearCurrentAccount()
|
||||
}, [track, navigation, clearCurrentAccount])
|
||||
setShowLoggedOut(true)
|
||||
closeAllActiveElements()
|
||||
}, [track, setShowLoggedOut, closeAllActiveElements])
|
||||
|
||||
const onPressChangeHandle = React.useCallback(() => {
|
||||
track('Settings:ChangeHandleButtonClicked')
|
||||
|
|
|
@ -141,7 +141,7 @@ export function DrawerContent() {
|
|||
} else {
|
||||
if (tab === 'Notifications') {
|
||||
// fetch new notifs on view
|
||||
queryClient.invalidateQueries({
|
||||
queryClient.resetQueries({
|
||||
queryKey: NOTIFS_RQKEY(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
|
|||
} else {
|
||||
if (tab === 'Notifications') {
|
||||
// fetch new notifs on view
|
||||
queryClient.invalidateQueries({
|
||||
queryClient.resetQueries({
|
||||
queryKey: NOTIFS_RQKEY(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ const NavItem: React.FC<{
|
|||
: isTab(currentRoute.name, routeName)
|
||||
|
||||
return (
|
||||
<Link href={href} style={styles.ctrl}>
|
||||
<Link href={href} style={styles.ctrl} navigationAction="navigate">
|
||||
{children({isActive})}
|
||||
</Link>
|
||||
)
|
||||
|
|
|
@ -11,7 +11,7 @@ import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
|||
export function DesktopFeeds() {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const feeds = usePinnedFeedsInfos()
|
||||
const {feeds} = usePinnedFeedsInfos()
|
||||
|
||||
const route = useNavigationState(state => {
|
||||
if (!state) {
|
||||
|
|
|
@ -150,7 +150,7 @@ function NavItem({count, href, icon, iconFilled, label}: NavItemProps) {
|
|||
} else {
|
||||
if (href === '/notifications') {
|
||||
// fetch new notifs on view
|
||||
queryClient.invalidateQueries({
|
||||
queryClient.resetQueries({
|
||||
queryKey: NOTIFS_RQKEY(),
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue