{
+ const nearScreen = useContext(NearScreenContext)
+
+ return nearScreen ? children : null
+}
+
function VideoError({error, retry}: {error: unknown; retry: () => void}) {
const {_} = useLingui()
diff --git a/src/components/Toast/index.web.tsx b/src/components/Toast/index.web.tsx
index f6ceda568e..f2517e28da 100644
--- a/src/components/Toast/index.web.tsx
+++ b/src/components/Toast/index.web.tsx
@@ -78,7 +78,12 @@ export const ToastContainer: React.FC
= ({}) => {
setActiveToast(undefined)}
/>
diff --git a/src/lib/api/feed-manip.ts b/src/lib/api/feed-manip.ts
index 3309191f34..335bf28c84 100644
--- a/src/lib/api/feed-manip.ts
+++ b/src/lib/api/feed-manip.ts
@@ -41,6 +41,7 @@ export class FeedViewPostsSlice {
isIncompleteThread: boolean
isFallbackMarker: boolean
isOrphan: boolean
+ isThreadMuted: boolean
rootUri: string
feedPostUri: string
@@ -50,6 +51,7 @@ export class FeedViewPostsSlice {
this.isIncompleteThread = false
this.isFallbackMarker = false
this.isOrphan = false
+ this.isThreadMuted = post.viewer?.threadMuted ?? false
this.feedPostUri = post.uri
if (AppBskyFeedDefs.isPostView(reply?.root)) {
this.rootUri = reply.root.uri
@@ -361,6 +363,20 @@ export class FeedTuner {
return slices
}
+ static removeMutedThreads(
+ tuner: FeedTuner,
+ slices: FeedViewPostsSlice[],
+ _dryRun: boolean,
+ ) {
+ for (let i = 0; i < slices.length; i++) {
+ if (slices[i].isThreadMuted) {
+ slices.splice(i, 1)
+ i--
+ }
+ }
+ return slices
+ }
+
static dedupThreads(
tuner: FeedTuner,
slices: FeedViewPostsSlice[],
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 3f0d499894..ab52b87107 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -207,3 +207,10 @@ export const PUBLIC_APPVIEW_DID = 'did:web:api.bsky.app'
export const PUBLIC_STAGING_APPVIEW_DID = 'did:web:api.staging.bsky.dev'
export const DEV_ENV_APPVIEW = `http://localhost:2584` // always the same
+
+export const webLinks = {
+ tos: `https://bsky.social/about/support/tos`,
+ privacy: `https://bsky.social/about/support/privacy-policy`,
+ community: `https://bsky.social/about/support/community-guidelines`,
+ communityDeprecated: `https://bsky.social/about/support/community-guidelines-deprecated`,
+}
diff --git a/src/lib/hooks/useNotificationHandler.ts b/src/lib/hooks/useNotificationHandler.ts
index ddee11fb5d..3370a72081 100644
--- a/src/lib/hooks/useNotificationHandler.ts
+++ b/src/lib/hooks/useNotificationHandler.ts
@@ -400,9 +400,7 @@ export function getNotificationPayload(
}
}
-export function notificationToURL(
- payload: NotificationPayload,
-): string | undefined {
+export function notificationToURL(payload: NotificationPayload): string | null {
switch (payload?.reason) {
case 'like':
case 'repost':
@@ -433,10 +431,12 @@ export function notificationToURL(
}
case 'chat-message':
// should be handled separately
- return undefined
+ return null
case 'verified':
case 'unverified':
- default:
return '/notifications'
+ default:
+ // do nothing if we don't know what to do with it
+ return null
}
}
diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts
index 3b1106480d..c3bd1a7cbf 100644
--- a/src/lib/statsig/gates.ts
+++ b/src/lib/statsig/gates.ts
@@ -3,6 +3,7 @@ export type Gate =
| 'alt_share_icon'
| 'debug_show_feedcontext'
| 'debug_subscriptions'
+ | 'disable_onboarding_policy_update_notice'
| 'explore_show_suggested_feeds'
| 'old_postonboarding'
| 'onboarding_add_video_feed'
diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po
index 91699294d3..ce8db630fe 100644
--- a/src/locale/locales/en/messages.po
+++ b/src/locale/locales/en/messages.po
@@ -657,8 +657,8 @@ msgstr ""
#: src/view/com/composer/GifAltText.tsx:76
#: src/view/com/composer/GifAltText.tsx:144
#: src/view/com/composer/GifAltText.tsx:210
-#: src/view/com/composer/photos/Gallery.tsx:169
-#: src/view/com/composer/photos/Gallery.tsx:216
+#: src/view/com/composer/photos/Gallery.tsx:170
+#: src/view/com/composer/photos/Gallery.tsx:217
#: src/view/com/composer/photos/ImageAltTextDialog.tsx:88
#: src/view/com/composer/photos/ImageAltTextDialog.tsx:93
msgid "Add alt text"
@@ -870,7 +870,7 @@ msgstr ""
#: src/components/Post/Embed/ExternalEmbed/Gif.tsx:186
#: src/view/com/composer/GifAltText.tsx:100
-#: src/view/com/composer/photos/Gallery.tsx:187
+#: src/view/com/composer/photos/Gallery.tsx:188
msgid "ALT"
msgstr ""
@@ -888,7 +888,7 @@ msgstr ""
msgid "Alt Text"
msgstr ""
-#: src/view/com/composer/photos/Gallery.tsx:255
+#: src/view/com/composer/photos/Gallery.tsx:260
msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone."
msgstr ""
@@ -921,11 +921,11 @@ msgstr ""
msgid "An error occurred while generating your starter pack. Want to try again?"
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/index.tsx:160
+#: src/components/Post/Embed/VideoEmbed/index.tsx:157
msgid "An error occurred while loading the video. Please try again later."
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/index.web.tsx:199
+#: src/components/Post/Embed/VideoEmbed/index.web.tsx:226
msgid "An error occurred while loading the video. Please try again."
msgstr ""
@@ -1423,7 +1423,7 @@ msgstr ""
msgid "Books"
msgstr ""
-#: src/components/FeedInterstitials.tsx:436
+#: src/components/FeedInterstitials.tsx:435
msgid "Browse more accounts on the Explore page"
msgstr ""
@@ -2309,12 +2309,9 @@ msgstr ""
msgid "Create Account"
msgstr ""
-#: src/screens/Search/SearchResults.tsx:266
-msgid "create an account"
-msgstr ""
-
#: src/components/dialogs/Signin.tsx:86
#: src/components/dialogs/Signin.tsx:88
+#: src/screens/Search/SearchResults.tsx:266
msgid "Create an account"
msgstr ""
@@ -2662,6 +2659,11 @@ msgstr ""
msgid "Dismiss interests"
msgstr ""
+#. Accessibility label for dismissing a toast notification
+#: src/components/Toast/index.web.tsx:82
+msgid "Dismiss message"
+msgstr ""
+
#: src/components/interstitials/TrendingVideos.tsx:89
msgid "Dismiss this section"
msgstr ""
@@ -2846,7 +2848,7 @@ msgstr ""
#: src/view/com/composer/photos/EditImageDialog.web.tsx:85
#: src/view/com/composer/photos/EditImageDialog.web.tsx:89
-#: src/view/com/composer/photos/Gallery.tsx:194
+#: src/view/com/composer/photos/Gallery.tsx:195
msgid "Edit image"
msgstr ""
@@ -3100,7 +3102,7 @@ msgstr ""
msgid "Enter your username and password"
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:135
+#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:132
msgid "Enters full screen"
msgstr ""
@@ -5114,7 +5116,7 @@ msgstr ""
msgid "Music"
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:156
+#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:153
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx:96
msgctxt "video"
msgid "Mute"
@@ -5515,7 +5517,7 @@ msgstr ""
msgid "No results for \"{0}\"."
msgstr ""
-#: src/components/Lists.tsx:189
+#: src/components/Lists.tsx:190
msgid "No results found"
msgstr ""
@@ -5740,11 +5742,11 @@ msgstr ""
msgid "Only WebVTT (.vtt) files are supported"
msgstr ""
-#: src/components/Lists.tsx:94
+#: src/components/Lists.tsx:95
msgid "Oops, something went wrong!"
msgstr ""
-#: src/components/Lists.tsx:173
+#: src/components/Lists.tsx:174
#: src/components/StarterPack/ProfileStarterPacks.tsx:332
#: src/components/StarterPack/ProfileStarterPacks.tsx:341
#: src/screens/Settings/AppPasswords.tsx:59
@@ -5980,7 +5982,7 @@ msgstr ""
msgid "Our moderators have reviewed reports and decided to disable your access to chats on Bluesky."
msgstr ""
-#: src/components/Lists.tsx:190
+#: src/components/Lists.tsx:191
#: src/view/screens/NotFound.tsx:47
msgid "Page not found"
msgstr ""
@@ -6015,7 +6017,7 @@ msgid "Password updated!"
msgstr ""
#: src/components/Post/Embed/ExternalEmbed/Gif.tsx:43
-#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:140
+#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:137
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx:369
msgid "Pause"
msgstr ""
@@ -6104,7 +6106,7 @@ msgid "Pinned to your feeds"
msgstr ""
#: src/components/Post/Embed/ExternalEmbed/Gif.tsx:43
-#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:140
+#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:137
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx:370
msgid "Play"
msgstr ""
@@ -6113,7 +6115,7 @@ msgstr ""
msgid "Play {0}"
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/index.tsx:134
+#: src/components/Post/Embed/VideoEmbed/index.tsx:132
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx:321
msgid "Play video"
msgstr ""
@@ -6126,7 +6128,7 @@ msgstr ""
msgid "Plays or pauses the GIF"
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:141
+#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:138
msgid "Plays or pauses the video"
msgstr ""
@@ -6408,7 +6410,7 @@ msgid "Press to attempt reconnection"
msgstr ""
#: src/components/Error.tsx:60
-#: src/components/Lists.tsx:99
+#: src/components/Lists.tsx:100
#: src/screens/Messages/components/MessageListError.tsx:24
#: src/screens/Signup/BackNextButtons.tsx:47
msgid "Press to retry"
@@ -6765,7 +6767,7 @@ msgstr ""
msgid "Remove from your feeds?"
msgstr ""
-#: src/view/com/composer/photos/Gallery.tsx:203
+#: src/view/com/composer/photos/Gallery.tsx:204
msgid "Remove image"
msgstr ""
@@ -7151,7 +7153,7 @@ msgstr ""
#: src/components/dms/MessageItem.tsx:321
#: src/components/Error.tsx:65
-#: src/components/Lists.tsx:110
+#: src/components/Lists.tsx:111
#: src/components/moderation/ReportDialog/index.tsx:229
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:55
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:57
@@ -7901,15 +7903,12 @@ msgstr ""
msgid "Shows the content"
msgstr ""
-#: src/screens/Search/SearchResults.tsx:258
-msgid "sign in"
-msgstr ""
-
#: src/components/dialogs/Signin.tsx:97
#: src/components/dialogs/Signin.tsx:99
#: src/screens/Login/index.tsx:122
#: src/screens/Login/index.tsx:143
#: src/screens/Login/LoginForm.tsx:181
+#: src/screens/Search/SearchResults.tsx:258
#: src/view/com/auth/SplashScreen.tsx:61
#: src/view/com/auth/SplashScreen.tsx:69
#: src/view/com/auth/SplashScreen.web.tsx:123
@@ -8051,7 +8050,7 @@ msgstr ""
msgid "Something went wrong, please try again."
msgstr ""
-#: src/components/Lists.tsx:174
+#: src/components/Lists.tsx:175
msgid "Something went wrong!"
msgstr ""
@@ -8941,7 +8940,7 @@ msgstr ""
msgid "Toggle to enable or disable adult content"
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:158
+#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:155
msgid "Toggles the sound"
msgstr ""
@@ -9127,7 +9126,7 @@ msgstr ""
msgid "Unlike ({0, plural, one {# like} other {# likes}})"
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:155
+#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:152
#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx:95
msgctxt "video"
msgid "Unmute"
@@ -9520,8 +9519,8 @@ msgstr ""
msgid "Version {0}"
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:83
-#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:134
+#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:81
+#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:131
msgid "Video"
msgstr ""
@@ -9551,7 +9550,7 @@ msgstr ""
msgid "Video is playing"
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/index.web.tsx:192
+#: src/components/Post/Embed/VideoEmbed/index.web.tsx:219
msgid "Video not found."
msgstr ""
@@ -9563,7 +9562,7 @@ msgstr ""
msgid "Video uploaded"
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:83
+#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:81
msgid "Video: {0}"
msgstr ""
@@ -9841,7 +9840,7 @@ msgstr ""
msgid "We're sorry! The post you are replying to has been deleted."
msgstr ""
-#: src/components/Lists.tsx:194
+#: src/components/Lists.tsx:195
#: src/view/screens/NotFound.tsx:50
msgid "We're sorry! We can't find the page you were looking for."
msgstr ""
@@ -10194,7 +10193,7 @@ msgstr ""
msgid "You have not muted any accounts yet. To mute an account, go to their profile and select \"Mute account\" from the menu on their account."
msgstr ""
-#: src/components/Lists.tsx:57
+#: src/components/Lists.tsx:58
msgid "You have reached the end"
msgstr ""
@@ -10423,7 +10422,7 @@ msgstr ""
msgid "Your birth date"
msgstr ""
-#: src/components/Post/Embed/VideoEmbed/index.web.tsx:196
+#: src/components/Post/Embed/VideoEmbed/index.web.tsx:223
msgid "Your browser does not support the video format. Please try a different browser."
msgstr ""
diff --git a/src/logger/types.ts b/src/logger/types.ts
index 4743e866c4..ee3069a083 100644
--- a/src/logger/types.ts
+++ b/src/logger/types.ts
@@ -13,6 +13,7 @@ export enum LogContext {
FeedFeedback = 'feed-feedback',
PostSource = 'post-source',
AgeAssurance = 'age-assurance',
+ PolicyUpdate = 'policy-update',
/**
* METRIC IS FOR INTERNAL USE ONLY, don't create any other loggers using this
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
index 719bbf9a29..5023982eb1 100644
--- a/src/screens/Settings/Settings.tsx
+++ b/src/screens/Settings/Settings.tsx
@@ -25,6 +25,7 @@ import {clearStorage} from '#/state/persisted'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useDeleteActorDeclaration} from '#/state/queries/messages/actor-declaration'
import {useProfileQuery, useProfilesQuery} from '#/state/queries/profile'
+import {useAgent} from '#/state/session'
import {type SessionAccount, useSession, useSessionApi} from '#/state/session'
import {useOnboardingDispatch} from '#/state/shell'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
@@ -35,6 +36,7 @@ import * as SettingsList from '#/screens/Settings/components/SettingsList'
import {atoms as a, platform, tokens, useBreakpoints, useTheme} from '#/alf'
import {AgeAssuranceDismissibleNotice} from '#/components/ageAssurance/AgeAssuranceDismissibleNotice'
import {AvatarStackWithFetch} from '#/components/AvatarStack'
+import {Button, ButtonText} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {SwitchAccountDialog} from '#/components/dialogs/SwitchAccount'
import {Accessibility_Stroke2_Corner2_Rounded as AccessibilityIcon} from '#/components/icons/Accessibility'
@@ -58,6 +60,7 @@ import {Window_Stroke2_Corner2_Rounded as WindowIcon} from '#/components/icons/W
import * as Layout from '#/components/Layout'
import {Loader} from '#/components/Loader'
import * as Menu from '#/components/Menu'
+import {ID as PolicyUpdate202508} from '#/components/PolicyUpdateOverlay/updates/202508/config'
import * as Prompt from '#/components/Prompt'
import {Text} from '#/components/Typography'
import {useFullVerificationState} from '#/components/verification'
@@ -66,6 +69,7 @@ import {
VerificationCheckButton,
} from '#/components/verification/VerificationCheckButton'
import {IS_INTERNAL} from '#/env'
+import {device, useStorage} from '#/storage'
import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscriptions-nudged'
type Props = NativeStackScreenProps
@@ -363,6 +367,10 @@ function ProfilePreview({
function DevOptions() {
const {_} = useLingui()
+ const agent = useAgent()
+ const [override, setOverride] = useStorage(device, [
+ 'policyUpdateDebugOverride',
+ ])
const onboardingDispatch = useOnboardingDispatch()
const navigation = useNavigation()
const {mutate: deleteChatDeclarationRecord} = useDeleteActorDeclaration()
@@ -502,6 +510,40 @@ function DevOptions() {
) : null}
+
+
+
+ PolicyUpdate202508 Debug
+
+
+ {
+ setOverride(!override)
+ }}
+ label="Toggle"
+ color={override ? 'primary' : 'secondary'}
+ size="small"
+ style={[a.flex_1]}>
+
+ {override ? 'Disable debug mode' : 'Enable debug mode'}
+
+
+
+ {
+ device.set([PolicyUpdate202508], false)
+ agent.bskyAppRemoveNuxs([PolicyUpdate202508])
+ Toast.show(`Done`, 'info')
+ }}
+ label="Reset policy update nux"
+ color="secondary"
+ size="small"
+ disabled={!override}>
+ Reset state
+
+
+
+
>
)
}
diff --git a/src/screens/Signup/StepInfo/Policies.tsx b/src/screens/Signup/StepInfo/Policies.tsx
index 17980172de..0bc14fa6ab 100644
--- a/src/screens/Signup/StepInfo/Policies.tsx
+++ b/src/screens/Signup/StepInfo/Policies.tsx
@@ -4,11 +4,42 @@ import {type ComAtprotoServerDescribeServer} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
+import {webLinks} from '#/lib/constants'
+import {useGate} from '#/lib/statsig/statsig'
import {atoms as a, useTheme} from '#/alf'
-import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
+import {Admonition} from '#/components/Admonition'
import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography'
+function CommunityGuidelinesNotice({}: {}) {
+ const {_} = useLingui()
+ const gate = useGate()
+
+ if (gate('disable_onboarding_policy_update_notice')) return null
+
+ return (
+
+
+
+ You also agree to{' '}
+
+ Bluesky’s Community Guidelines
+
+ . An{' '}
+
+ updated version of our Community Guidelines
+ {' '}
+ will take effect on October 13th.
+
+
+
+ )
+}
+
export const Policies = ({
serviceDescription,
needsGuardian,
@@ -30,14 +61,13 @@ export const Policies = ({
if (!tos && !pp) {
return (
-
-
-
-
+
+
This service has not provided terms of service or a privacy policy.
-
+
+
)
}
@@ -102,19 +132,21 @@ export const Policies = ({
) : null}
{under13 ? (
-
+
You must be 13 years of age or older to create an account.
-
+
) : needsGuardian ? (
-
+
If you are not yet an adult according to the laws of your country,
your parent or legal guardian must read these Terms on your behalf.
-
+
) : undefined}
+
+
)
}
diff --git a/src/screens/Signup/state.ts b/src/screens/Signup/state.ts
index 48ea4ccd93..ae0b20f1c5 100644
--- a/src/screens/Signup/state.ts
+++ b/src/screens/Signup/state.ts
@@ -15,6 +15,7 @@ import {getAge} from '#/lib/strings/time'
import {logger} from '#/logger'
import {useSessionApi} from '#/state/session'
import {useOnboardingDispatch} from '#/state/shell'
+import {usePreemptivelyCompleteActivePolicyUpdate} from '#/components/PolicyUpdateOverlay/usePreemptivelyCompleteActivePolicyUpdate'
export type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
@@ -252,6 +253,8 @@ export function useSubmitSignup() {
const {_} = useLingui()
const {createAccount} = useSessionApi()
const onboardingDispatch = useOnboardingDispatch()
+ const preemptivelyCompleteActivePolicyUpdate =
+ usePreemptivelyCompleteActivePolicyUpdate()
return useCallback(
async (state: SignupState, dispatch: (action: SignupAction) => void) => {
@@ -325,6 +328,12 @@ export function useSubmitSignup() {
},
)
+ /**
+ * Marks any active policy update as completed, since user just agreed
+ * to TOS/privacy during sign up
+ */
+ preemptivelyCompleteActivePolicyUpdate()
+
/*
* Must happen last so that if the user has multiple tabs open and
* createAccount fails, one tab is not stuck in onboarding — Eric
@@ -363,6 +372,11 @@ export function useSubmitSignup() {
dispatch({type: 'setIsLoading', value: false})
}
},
- [_, onboardingDispatch, createAccount],
+ [
+ _,
+ onboardingDispatch,
+ createAccount,
+ preemptivelyCompleteActivePolicyUpdate,
+ ],
)
}
diff --git a/src/state/persisted/index.ts b/src/state/persisted/index.ts
index 51d757ad8b..8c043a3420 100644
--- a/src/state/persisted/index.ts
+++ b/src/state/persisted/index.ts
@@ -3,11 +3,12 @@ import AsyncStorage from '@react-native-async-storage/async-storage'
import {logger} from '#/logger'
import {
defaults,
- Schema,
+ type Schema,
tryParse,
tryStringify,
} from '#/state/persisted/schema'
-import {PersistedApi} from './types'
+import {device} from '#/storage'
+import {type PersistedApi} from './types'
import {normalizeData} from './util'
export type {PersistedAccount, Schema} from '#/state/persisted/schema'
@@ -53,6 +54,7 @@ onUpdate satisfies PersistedApi['onUpdate']
export async function clearStorage() {
try {
await AsyncStorage.removeItem(BSKY_STORAGE)
+ device.removeAll()
} catch (e: any) {
logger.error(`persisted store: failed to clear`, {message: e.toString()})
}
diff --git a/src/state/preferences/feed-tuners.tsx b/src/state/preferences/feed-tuners.tsx
index 3ed60e5988..feeab6f9af 100644
--- a/src/state/preferences/feed-tuners.tsx
+++ b/src/state/preferences/feed-tuners.tsx
@@ -1,7 +1,7 @@
import {useMemo} from 'react'
import {FeedTuner} from '#/lib/api/feed-manip'
-import {FeedDescriptor} from '../queries/post-feed'
+import {type FeedDescriptor} from '../queries/post-feed'
import {usePreferencesQuery} from '../queries/preferences'
import {useSession} from '../session'
import {useLanguagePrefs} from './languages'
@@ -19,7 +19,10 @@ export function useFeedTuners(feedDesc: FeedDescriptor) {
}
}
if (feedDesc.startsWith('feedgen')) {
- return [FeedTuner.preferredLangOnly(langPrefs.contentLanguages)]
+ return [
+ FeedTuner.preferredLangOnly(langPrefs.contentLanguages),
+ FeedTuner.removeMutedThreads,
+ ]
}
if (feedDesc === 'following' || feedDesc.startsWith('list')) {
const feedTuners = [FeedTuner.removeOrphans]
@@ -40,6 +43,7 @@ export function useFeedTuners(feedDesc: FeedDescriptor) {
feedTuners.push(FeedTuner.removeQuotePosts)
}
feedTuners.push(FeedTuner.dedupThreads)
+ feedTuners.push(FeedTuner.removeMutedThreads)
return feedTuners
}
diff --git a/src/state/queries/nuxs/__mocks__/index.ts b/src/state/queries/nuxs/__mocks__/index.ts
new file mode 100644
index 0000000000..c718b15940
--- /dev/null
+++ b/src/state/queries/nuxs/__mocks__/index.ts
@@ -0,0 +1,25 @@
+import {jest} from '@jest/globals'
+
+export {Nux} from '#/state/queries/nuxs/definitions'
+
+export const useNuxs = jest.fn(() => {
+ return {
+ nuxs: undefined,
+ status: 'loading' as const,
+ }
+})
+
+export const useNux = jest.fn((id: string) => {
+ return {
+ nux: undefined,
+ status: 'loading' as const,
+ }
+})
+
+export const useSaveNux = jest.fn(() => {
+ return {}
+})
+
+export const useResetNuxs = jest.fn(() => {
+ return {}
+})
diff --git a/src/state/queries/nuxs/definitions.ts b/src/state/queries/nuxs/definitions.ts
index 3d5c132f26..7577d6b205 100644
--- a/src/state/queries/nuxs/definitions.ts
+++ b/src/state/queries/nuxs/definitions.ts
@@ -9,6 +9,11 @@ export enum Nux {
ActivitySubscriptions = 'ActivitySubscriptions',
AgeAssuranceDismissibleNotice = 'AgeAssuranceDismissibleNotice',
AgeAssuranceDismissibleFeedBanner = 'AgeAssuranceDismissibleFeedBanner',
+
+ /*
+ * Blocking announcements. New IDs are required for each new announcement.
+ */
+ PolicyUpdate202508 = 'PolicyUpdate202508',
}
export const nuxNames = new Set(Object.values(Nux))
@@ -38,6 +43,10 @@ export type AppNux = BaseNux<
id: Nux.AgeAssuranceDismissibleFeedBanner
data: undefined
}
+ | {
+ id: Nux.PolicyUpdate202508
+ data: undefined
+ }
>
export const NuxSchemas: Record | undefined> = {
@@ -47,4 +56,5 @@ export const NuxSchemas: Record | undefined> = {
[Nux.ActivitySubscriptions]: undefined,
[Nux.AgeAssuranceDismissibleNotice]: undefined,
[Nux.AgeAssuranceDismissibleFeedBanner]: undefined,
+ [Nux.PolicyUpdate202508]: undefined,
}
diff --git a/src/storage/index.ts b/src/storage/index.ts
index 4d45134e1b..4c42005104 100644
--- a/src/storage/index.ts
+++ b/src/storage/index.ts
@@ -66,6 +66,13 @@ export class Storage {
keys.forEach(key => this.remove([...scopes, key]))
}
+ /**
+ * For debugging purposes
+ */
+ removeAll() {
+ this.store.clearAll()
+ }
+
/**
* Fires a callback when the storage associated with a given key changes
*
diff --git a/src/storage/schema.ts b/src/storage/schema.ts
index c05a7531df..421264ac1e 100644
--- a/src/storage/schema.ts
+++ b/src/storage/schema.ts
@@ -1,3 +1,5 @@
+import {type ID as PolicyUpdate202508} from '#/components/PolicyUpdateOverlay/updates/202508/config'
+
/**
* Device data that's specific to the device and does not vary based account
*/
@@ -13,6 +15,12 @@ export type Device = {
devMode: boolean
demoMode: boolean
activitySubscriptionsNudged?: boolean
+
+ /**
+ * Policy update overlays. New IDs are required for each new announcement.
+ */
+ policyUpdateDebugOverride?: boolean
+ [PolicyUpdate202508]?: boolean
}
export type Account = {
diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx
index c44461b60d..a66d65d97a 100644
--- a/src/view/com/composer/photos/Gallery.tsx
+++ b/src/view/com/composer/photos/Gallery.tsx
@@ -21,6 +21,7 @@ import {type ComposerImage, cropImage} from '#/state/gallery'
import {Text} from '#/view/com/util/text/Text'
import {useTheme} from '#/alf'
import * as Dialog from '#/components/Dialog'
+import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import {type PostAction} from '../state/composer'
import {EditImageDialog} from './EditImageDialog'
import {ImageAltTextDialog} from './ImageAltTextDialog'
@@ -227,8 +228,12 @@ const GalleryItem = ({
}}
accessible={true}
accessibilityIgnoresInvertColors
+ cachePolicy="none"
+ autoplay={false}
/>
+
+
}
>
)}
+
+ {/* Only shown after logged in and onboaring etc are complete */}
+ {hasSession && }
)
}
diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx
index 4d1a8c51b6..0d8c245665 100644
--- a/src/view/shell/index.tsx
+++ b/src/view/shell/index.tsx
@@ -31,6 +31,10 @@ import {InAppBrowserConsentDialog} from '#/components/dialogs/InAppBrowserConsen
import {LinkWarningDialog} from '#/components/dialogs/LinkWarning'
import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
import {SigninDialog} from '#/components/dialogs/Signin'
+import {
+ Outlet as PolicyUpdateOverlayPortalOutlet,
+ usePolicyUpdateStateContext,
+} from '#/components/PolicyUpdateOverlay'
import {Outlet as PortalOutlet} from '#/components/Portal'
import {RoutesContainer, TabsNavigator} from '#/Navigation'
import {BottomSheetOutlet} from '../../../modules/bottom-sheet'
@@ -45,6 +49,7 @@ function ShellInner() {
const setIsDrawerOpen = useSetDrawerOpen()
const winDim = useWindowDimensions()
const insets = useSafeAreaInsets()
+ const policyUpdateState = usePolicyUpdateStateContext()
const renderDrawerContent = useCallback(() => , [])
const onOpenDrawer = useCallback(
@@ -151,6 +156,7 @@ function ShellInner() {
+
@@ -160,8 +166,16 @@ function ShellInner() {
-
-
+
+ {/* Until policy update has been completed by the user, don't render anything that is portaled */}
+ {policyUpdateState.completed && (
+ <>
+
+
+ >
+ )}
+
+
>
)
}
diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx
index 77c3f45f62..c1565e8ee7 100644
--- a/src/view/shell/index.web.tsx
+++ b/src/view/shell/index.web.tsx
@@ -22,6 +22,10 @@ import {EmailDialog} from '#/components/dialogs/EmailDialog'
import {LinkWarningDialog} from '#/components/dialogs/LinkWarning'
import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
import {SigninDialog} from '#/components/dialogs/Signin'
+import {
+ Outlet as PolicyUpdateOverlayPortalOutlet,
+ usePolicyUpdateStateContext,
+} from '#/components/PolicyUpdateOverlay'
import {Outlet as PortalOutlet} from '#/components/Portal'
import {FlatNavigator, RoutesContainer} from '#/Navigation'
import {Composer} from './Composer.web'
@@ -37,6 +41,7 @@ function ShellInner() {
const {_} = useLingui()
const showDrawer = !isDesktop && isDrawerOpen
const [showDrawerDelayedExit, setShowDrawerDelayedExit] = useState(showDrawer)
+ const policyUpdateState = usePolicyUpdateStateContext()
useLayoutEffect(() => {
if (showDrawer !== showDrawerDelayedExit) {
@@ -74,7 +79,13 @@ function ShellInner() {
-
+
+ {/* Until policy update has been completed by the user, don't render anything that is portaled */}
+ {policyUpdateState.completed && (
+ <>
+
+ >
+ )}
{showDrawerDelayedExit && (
<>
@@ -113,6 +124,8 @@ function ShellInner() {
>
)}
+
+
>
)
}
diff --git a/yarn.lock b/yarn.lock
index 8079c90bfc..bc7e35f67d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -11301,10 +11301,10 @@ expo-image-picker@~16.1.4:
dependencies:
expo-image-loader "~5.1.0"
-expo-image@~2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-2.2.1.tgz#b4aa706a25f7e8902ac854a8da249caf4a90cd67"
- integrity sha512-5ZSggMi0X2G9AN0aM+sdkCyyZ6YcWvGs9KYLYrRBVUN3ph6RBiu6mKGpaNN1TAscySRnH1eHbUE1H+Qeq7qm1g==
+expo-image@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-2.4.0.tgz#02f7fd743387206914cd431a6367f5be53509e3e"
+ integrity sha512-TQ/LvrtJ9JBr+Tf198CAqflxcvdhuj7P24n0LQ1jHaWIVA7Z+zYKbYHnSMPSDMul/y0U46Z5bFLbiZiSidgcNw==
expo-json-utils@~0.15.0:
version "0.15.0"