From 306f16263905eb8bca4463b735b5987aeb3cc5d1 Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 12 Jul 2024 07:37:43 -0700 Subject: [PATCH 1/4] Make all referrer info sync (#4782) --- .../referrer/ExpoBlueskyReferrerModule.kt | 8 ++++---- .../src/Referrer/index.android.ts | 4 ++-- .../src/Referrer/index.ios.ts | 4 ++-- .../src/Referrer/index.ts | 2 +- .../src/Referrer/index.web.ts | 2 +- src/Navigation.tsx | 17 ++++++++--------- src/lib/hooks/useIntentHandler.ts | 17 ++++++++--------- 7 files changed, 26 insertions(+), 28 deletions(-) diff --git a/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/referrer/ExpoBlueskyReferrerModule.kt b/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/referrer/ExpoBlueskyReferrerModule.kt index bac555233e..52b4daa303 100644 --- a/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/referrer/ExpoBlueskyReferrerModule.kt +++ b/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/referrer/ExpoBlueskyReferrerModule.kt @@ -23,7 +23,7 @@ class ExpoBlueskyReferrerModule : Module() { activityReferrer = appContext.currentActivity?.referrer } - AsyncFunction("getReferrerInfoAsync") { + Function("getReferrerInfo") { val intentReferrer = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { intent?.getParcelableExtra(Intent.EXTRA_REFERRER, Uri::class.java) @@ -40,7 +40,7 @@ class ExpoBlueskyReferrerModule : Module() { "hostname" to intentReferrer.host, ) intent = null - return@AsyncFunction res + return@Function res } // In all other cases, we'll just record the app that sent the intent. @@ -52,10 +52,10 @@ class ExpoBlueskyReferrerModule : Module() { "hostname" to (activityReferrer?.host ?: ""), ) activityReferrer = null - return@AsyncFunction res + return@Function res } - return@AsyncFunction null + return@Function null } AsyncFunction("getGooglePlayReferrerInfoAsync") { promise: Promise -> diff --git a/modules/expo-bluesky-swiss-army/src/Referrer/index.android.ts b/modules/expo-bluesky-swiss-army/src/Referrer/index.android.ts index ec2bcb57db..795c6146c9 100644 --- a/modules/expo-bluesky-swiss-army/src/Referrer/index.android.ts +++ b/modules/expo-bluesky-swiss-army/src/Referrer/index.android.ts @@ -8,6 +8,6 @@ export function getGooglePlayReferrerInfoAsync(): Promise { - return NativeModule.getReferrerInfoAsync() +export function getReferrerInfo(): Promise { + return NativeModule.getReferrerInfo() } diff --git a/modules/expo-bluesky-swiss-army/src/Referrer/index.ios.ts b/modules/expo-bluesky-swiss-army/src/Referrer/index.ios.ts index 2bf1497a0a..e76893d308 100644 --- a/modules/expo-bluesky-swiss-army/src/Referrer/index.ios.ts +++ b/modules/expo-bluesky-swiss-army/src/Referrer/index.ios.ts @@ -6,7 +6,7 @@ export function getGooglePlayReferrerInfoAsync(): Promise { +export function getReferrerInfo(): ReferrerInfo | null { const referrer = SharedPrefs.getString('referrer') if (referrer) { SharedPrefs.removeValue('referrer') @@ -19,7 +19,7 @@ export function getReferrerInfoAsync(): Promise { } catch (e) { return { referrer, - hostname: undefined, + hostname: referrer, } } } diff --git a/modules/expo-bluesky-swiss-army/src/Referrer/index.ts b/modules/expo-bluesky-swiss-army/src/Referrer/index.ts index a60f7b6db8..ac594402b8 100644 --- a/modules/expo-bluesky-swiss-army/src/Referrer/index.ts +++ b/modules/expo-bluesky-swiss-army/src/Referrer/index.ts @@ -5,6 +5,6 @@ export function getGooglePlayReferrerInfoAsync(): Promise { +export function getReferrerInfo(): ReferrerInfo | null { throw new NotImplementedError() } diff --git a/modules/expo-bluesky-swiss-army/src/Referrer/index.web.ts b/modules/expo-bluesky-swiss-army/src/Referrer/index.web.ts index c092a67e73..cfd42642d9 100644 --- a/modules/expo-bluesky-swiss-army/src/Referrer/index.web.ts +++ b/modules/expo-bluesky-swiss-army/src/Referrer/index.web.ts @@ -7,7 +7,7 @@ export function getGooglePlayReferrerInfoAsync(): Promise { +export function getReferrerInfo(): ReferrerInfo | null { if ( Platform.OS === 'web' && // for ssr diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 8c815a3fe2..9e9b49443a 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -771,15 +771,14 @@ function logModuleInitTime() { }) if (isWeb) { - Referrer.getReferrerInfoAsync().then(info => { - if (info && info.hostname !== 'bsky.app') { - logEvent('deepLink:referrerReceived', { - to: window.location.href, - referrer: info?.referrer, - hostname: info?.hostname, - }) - } - }) + const referrerInfo = Referrer.getReferrerInfo() + if (referrerInfo && referrerInfo.hostname !== 'bsky.app') { + logEvent('deepLink:referrerReceived', { + to: window.location.href, + referrer: referrerInfo?.referrer, + hostname: referrerInfo?.hostname, + }) + } } if (__DEV__) { diff --git a/src/lib/hooks/useIntentHandler.ts b/src/lib/hooks/useIntentHandler.ts index 3235e1a6a5..460df3753d 100644 --- a/src/lib/hooks/useIntentHandler.ts +++ b/src/lib/hooks/useIntentHandler.ts @@ -18,15 +18,14 @@ export function useIntentHandler() { React.useEffect(() => { const handleIncomingURL = (url: string) => { - Referrer.getReferrerInfoAsync().then(info => { - if (info && info.hostname !== 'bsky.app') { - logEvent('deepLink:referrerReceived', { - to: url, - referrer: info?.referrer, - hostname: info?.hostname, - }) - } - }) + const referrerInfo = Referrer.getReferrerInfo() + if (referrerInfo && referrerInfo.hostname !== 'bsky.app') { + logEvent('deepLink:referrerReceived', { + to: url, + referrer: referrerInfo?.referrer, + hostname: referrerInfo?.hostname, + }) + } // We want to be able to support bluesky:// deeplinks. It's unnatural for someone to use a deeplink with three // slashes, like bluesky:///intent/follow. However, supporting just two slashes causes us to have to take care From f85df425f7ca1c75e11d307f783eaf6ffc14e5c1 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 12 Jul 2024 10:58:12 -0500 Subject: [PATCH 2/4] Invert saturation scale as well (#4783) --- src/alf/themes.ts | 52 +++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/alf/themes.ts b/src/alf/themes.ts index b430fa65e1..ba18ee0072 100644 --- a/src/alf/themes.ts +++ b/src/alf/themes.ts @@ -261,23 +261,23 @@ export function createThemes({ contrast_950: `hsl(${hues.primary}, 20%, ${dimScale[12]}%)`, contrast_975: `hsl(${hues.primary}, 20%, ${dimScale[13]}%)`, - primary_25: `hsl(${hues.primary}, 99%, ${dimScale[1]}%)`, - primary_50: `hsl(${hues.primary}, 99%, ${dimScale[2]}%)`, - primary_100: `hsl(${hues.primary}, 99%, ${dimScale[3]}%)`, - primary_200: `hsl(${hues.primary}, 99%, ${dimScale[4]}%)`, - primary_300: `hsl(${hues.primary}, 99%, ${dimScale[5]}%)`, - primary_400: `hsl(${hues.primary}, 99%, ${dimScale[6]}%)`, + primary_25: `hsl(${hues.primary}, 50%, ${dimScale[1]}%)`, + primary_50: `hsl(${hues.primary}, 60%, ${dimScale[2]}%)`, + primary_100: `hsl(${hues.primary}, 70%, ${dimScale[3]}%)`, + primary_200: `hsl(${hues.primary}, 82%, ${dimScale[4]}%)`, + primary_300: `hsl(${hues.primary}, 90%, ${dimScale[5]}%)`, + primary_400: `hsl(${hues.primary}, 95%, ${dimScale[6]}%)`, primary_500: `hsl(${hues.primary}, 99%, ${dimScale[7]}%)`, - primary_600: `hsl(${hues.primary}, 95%, ${dimScale[8]}%)`, - primary_700: `hsl(${hues.primary}, 90%, ${dimScale[9]}%)`, - primary_800: `hsl(${hues.primary}, 82%, ${dimScale[10]}%)`, - primary_900: `hsl(${hues.primary}, 70%, ${dimScale[11]}%)`, - primary_950: `hsl(${hues.primary}, 60%, ${dimScale[12]}%)`, - primary_975: `hsl(${hues.primary}, 50%, ${dimScale[13]}%)`, + primary_600: `hsl(${hues.primary}, 99%, ${dimScale[8]}%)`, + primary_700: `hsl(${hues.primary}, 99%, ${dimScale[9]}%)`, + primary_800: `hsl(${hues.primary}, 99%, ${dimScale[10]}%)`, + primary_900: `hsl(${hues.primary}, 99%, ${dimScale[11]}%)`, + primary_950: `hsl(${hues.primary}, 99%, ${dimScale[12]}%)`, + primary_975: `hsl(${hues.primary}, 99%, ${dimScale[13]}%)`, - positive_25: `hsl(${hues.positive}, 82%, ${dimScale[1]}%)`, - positive_50: `hsl(${hues.positive}, 82%, ${dimScale[2]}%)`, - positive_100: `hsl(${hues.positive}, 82%, ${dimScale[3]}%)`, + positive_25: `hsl(${hues.positive}, 50%, ${dimScale[1]}%)`, + positive_50: `hsl(${hues.positive}, 60%, ${dimScale[2]}%)`, + positive_100: `hsl(${hues.positive}, 70%, ${dimScale[3]}%)`, positive_200: `hsl(${hues.positive}, 82%, ${dimScale[4]}%)`, positive_300: `hsl(${hues.positive}, 82%, ${dimScale[5]}%)`, positive_400: `hsl(${hues.positive}, 82%, ${dimScale[6]}%)`, @@ -285,23 +285,23 @@ export function createThemes({ positive_600: `hsl(${hues.positive}, 82%, ${dimScale[8]}%)`, positive_700: `hsl(${hues.positive}, 82%, ${dimScale[9]}%)`, positive_800: `hsl(${hues.positive}, 82%, ${dimScale[10]}%)`, - positive_900: `hsl(${hues.positive}, 70%, ${dimScale[11]}%)`, - positive_950: `hsl(${hues.positive}, 60%, ${dimScale[12]}%)`, - positive_975: `hsl(${hues.positive}, 50%, ${dimScale[13]}%)`, + positive_900: `hsl(${hues.positive}, 82%, ${dimScale[11]}%)`, + positive_950: `hsl(${hues.positive}, 82%, ${dimScale[12]}%)`, + positive_975: `hsl(${hues.positive}, 82%, ${dimScale[13]}%)`, - negative_25: `hsl(${hues.negative}, 91%, ${dimScale[1]}%)`, - negative_50: `hsl(${hues.negative}, 91%, ${dimScale[2]}%)`, - negative_100: `hsl(${hues.negative}, 91%, ${dimScale[3]}%)`, - negative_200: `hsl(${hues.negative}, 91%, ${dimScale[4]}%)`, + negative_25: `hsl(${hues.negative}, 70%, ${dimScale[1]}%)`, + negative_50: `hsl(${hues.negative}, 80%, ${dimScale[2]}%)`, + negative_100: `hsl(${hues.negative}, 84%, ${dimScale[3]}%)`, + negative_200: `hsl(${hues.negative}, 88%, ${dimScale[4]}%)`, negative_300: `hsl(${hues.negative}, 91%, ${dimScale[5]}%)`, negative_400: `hsl(${hues.negative}, 91%, ${dimScale[6]}%)`, negative_500: `hsl(${hues.negative}, 91%, ${dimScale[7]}%)`, negative_600: `hsl(${hues.negative}, 91%, ${dimScale[8]}%)`, negative_700: `hsl(${hues.negative}, 91%, ${dimScale[9]}%)`, - negative_800: `hsl(${hues.negative}, 88%, ${dimScale[10]}%)`, - negative_900: `hsl(${hues.negative}, 84%, ${dimScale[11]}%)`, - negative_950: `hsl(${hues.negative}, 80%, ${dimScale[12]}%)`, - negative_975: `hsl(${hues.negative}, 70%, ${dimScale[13]}%)`, + negative_800: `hsl(${hues.negative}, 91%, ${dimScale[10]}%)`, + negative_900: `hsl(${hues.negative}, 91%, ${dimScale[11]}%)`, + negative_950: `hsl(${hues.negative}, 91%, ${dimScale[12]}%)`, + negative_975: `hsl(${hues.negative}, 91%, ${dimScale[13]}%)`, } as const const light: Theme = { From 7c1c24ef1badb746ecbe707d390bec4934940ced Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 12 Jul 2024 12:12:57 -0500 Subject: [PATCH 3/4] Replace incongruous buttons in drawer (#4772) * Replace incongruous buttons in drawer * Improve a11y help --- src/view/shell/Drawer.tsx | 58 ++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index e858ae2c45..4b765962a6 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -9,10 +9,7 @@ import { View, ViewStyle, } from 'react-native' -import { - FontAwesomeIcon, - FontAwesomeIconStyle, -} from '@fortawesome/react-native-fontawesome' +import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome' import {msg, Plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {StackActions, useNavigation} from '@react-navigation/native' @@ -37,6 +34,7 @@ import {formatCountShortOnly} from 'view/com/util/numeric/format' import {Text} from 'view/com/util/text/Text' import {UserAvatar} from 'view/com/util/UserAvatar' import {useTheme as useAlfTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' import { Bell_Filled_Corner0_Rounded as BellFilled, Bell_Stroke2_Corner0_Rounded as Bell, @@ -52,6 +50,7 @@ import { } from '#/components/icons/HomeOpen' import {MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled} from '#/components/icons/MagnifyingGlass' import {MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass2' +import {Message_Stroke2_Corner0_Rounded as Message} from '#/components/icons/Message' import {SettingsGear2_Stroke2_Corner0_Rounded as Settings} from '#/components/icons/SettingsGear2' import { UserCircle_Filled_Corner0_Rounded as UserCircleFilled, @@ -319,42 +318,33 @@ let DrawerFooter = ({ onPressFeedback: () => void onPressHelp: () => void }): React.ReactNode => { - const theme = useTheme() - const pal = usePalette('default') const {_} = useLingui() return ( - - - + + ) } From 3627a249ffb32e4c0f84597f8f9adf228ee90a8f Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 12 Jul 2024 14:55:34 -0500 Subject: [PATCH 4/4] Remove invalid labelers when subscribing/unsubscribing (#4771) * Remove invalid labelers when subscribing/unsubscribing * Let the async lock cook * Use link to associate, leave copy as is --- src/lib/constants.ts | 2 + .../Profile/Header/ProfileHeaderLabeler.tsx | 50 ++++++++++--------- src/state/queries/labeler.ts | 49 ++++++++++++++++-- 3 files changed, 74 insertions(+), 27 deletions(-) diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 7516c2c285..45b3dfd950 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -133,3 +133,5 @@ export const GIF_SEARCH = (params: string) => `${GIF_SERVICE}/tenor/v2/search?${params}` export const GIF_FEATURED = (params: string) => `${GIF_SERVICE}/tenor/v2/featured?${params}` + +export const MAX_LABELERS = 20 diff --git a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx index d266decb32..51e555a6e2 100644 --- a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx +++ b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx @@ -10,6 +10,8 @@ import { import {msg, Plural, plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import {MAX_LABELERS} from '#/lib/constants' import {isAppLabeler} from '#/lib/moderation' import {logger} from '#/logger' import {Shadow} from '#/state/cache/types' @@ -75,14 +77,14 @@ let ProfileHeaderLabeler = ({ [profile, moderationOpts], ) const {data: preferences} = usePreferencesQuery() - const {mutateAsync: toggleSubscription, variables} = - useLabelerSubscriptionMutation() + const { + mutateAsync: toggleSubscription, + variables, + reset, + } = useLabelerSubscriptionMutation() const isSubscribed = variables?.subscribe ?? preferences?.moderationPrefs.labelers.find(l => l.did === profile.did) - const canSubscribe = - isSubscribed || - (preferences ? preferences?.moderationPrefs.labelers.length <= 20 : false) const {mutateAsync: likeMod, isPending: isLikePending} = useLikeMutation() const {mutateAsync: unlikeMod, isPending: isUnlikePending} = useUnlikeMutation() @@ -130,17 +132,17 @@ let ProfileHeaderLabeler = ({ const onPressSubscribe = React.useCallback( () => requireAuth(async (): Promise => { - if (!canSubscribe) { - cantSubscribePrompt.open() - return - } try { await toggleSubscription({ did: profile.did, subscribe: !isSubscribed, }) } catch (e: any) { - // setSubscriptionError(e.message) + reset() + if (e.message === 'MAX_LABELERS') { + cantSubscribePrompt.open() + return + } logger.error(`Failed to subscribe to labeler`, {message: e.message}) } }), @@ -149,8 +151,8 @@ let ProfileHeaderLabeler = ({ toggleSubscription, isSubscribed, profile, - canSubscribe, cantSubscribePrompt, + reset, ], ) @@ -199,14 +201,13 @@ let ProfileHeaderLabeler = ({ style={[ { paddingVertical: gtMobile ? 12 : 10, - backgroundColor: - isSubscribed || !canSubscribe - ? state.hovered || state.pressed - ? t.palette.contrast_50 - : t.palette.contrast_25 - : state.hovered || state.pressed - ? tokens.color.temp_purple_dark - : tokens.color.temp_purple, + backgroundColor: isSubscribed + ? state.hovered || state.pressed + ? t.palette.contrast_50 + : t.palette.contrast_25 + : state.hovered || state.pressed + ? tokens.color.temp_purple_dark + : tokens.color.temp_purple, }, a.px_lg, a.rounded_sm, @@ -215,11 +216,9 @@ let ProfileHeaderLabeler = ({ l.did) + const invalidLabelers: string[] = [] + if (labelerDids.length) { + const profiles = await agent.getProfiles({actors: labelerDids}) + if (profiles.data) { + for (const did of labelerDids) { + const exists = profiles.data.profiles.find(p => p.did === did) + if (exists) { + // profile came back but it's not a valid labeler + if (exists.associated && !exists.associated.labeler) { + invalidLabelers.push(did) + } + } else { + // no response came back, might be deactivated or takendown + invalidLabelers.push(did) + } + } + } + } + if (invalidLabelers.length) { + await Promise.all(invalidLabelers.map(did => agent.removeLabeler(did))) + } + if (subscribe) { + const labelerCount = labelerDids.length - invalidLabelers.length + if (labelerCount >= MAX_LABELERS) { + throw new Error('MAX_LABELERS') + } await agent.addLabeler(did) } else { await agent.removeLabeler(did) } }, - onSuccess() { - queryClient.invalidateQueries({ + async onSuccess() { + await queryClient.invalidateQueries({ queryKey: preferencesQueryKey, }) },