Merge remote-tracking branch 'upstream/main' into Improve-notification-localization

This commit is contained in:
Minseo Lee
2024-07-19 21:33:21 +09:00
12 changed files with 150 additions and 115 deletions
+8 -9
View File
@@ -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__) {
+26 -26
View File
@@ -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 = {
+2
View File
@@ -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
+8 -9
View File
@@ -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
@@ -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<void> => {
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 = ({
<Text
style={[
{
color: canSubscribe
? isSubscribed
? t.palette.contrast_700
: t.palette.white
: t.palette.contrast_400,
color: isSubscribed
? t.palette.contrast_700
: t.palette.white,
},
a.font_bold,
a.text_center,
@@ -317,6 +316,9 @@ let ProfileHeaderLabeler = ({
ProfileHeaderLabeler = memo(ProfileHeaderLabeler)
export {ProfileHeaderLabeler}
/**
* Keep this in sync with the value of {@link MAX_LABELERS}
*/
function CantSubscribePrompt({
control,
}: {
+46 -3
View File
@@ -2,9 +2,13 @@ import {AppBskyLabelerDefs} from '@atproto/api'
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {z} from 'zod'
import {MAX_LABELERS} from '#/lib/constants'
import {labelersDetailedInfoQueryKeyRoot} from '#/lib/react-query'
import {STALE} from '#/state/queries'
import {preferencesQueryKey} from '#/state/queries/preferences'
import {
preferencesQueryKey,
usePreferencesQuery,
} from '#/state/queries/preferences'
import {useAgent} from '#/state/session'
const labelerInfoQueryKeyRoot = 'labeler-info'
@@ -77,6 +81,7 @@ export function useLabelersDetailedInfoQuery({dids}: {dids: string[]}) {
export function useLabelerSubscriptionMutation() {
const queryClient = useQueryClient()
const agent = useAgent()
const preferences = usePreferencesQuery()
return useMutation({
async mutationFn({did, subscribe}: {did: string; subscribe: boolean}) {
@@ -86,14 +91,52 @@ export function useLabelerSubscriptionMutation() {
subscribe: z.boolean(),
}).parse({did, subscribe})
/**
* If a user has invalid/takendown/deactivated labelers, we need to
* remove them. We don't have a great way to do this atm on the server,
* so we do it here.
*
* We also need to push validation into this method, since we need to
* check {@link MAX_LABELERS} _after_ we've removed invalid or takendown
* labelers.
*/
const labelerDids = (
preferences.data?.moderationPrefs?.labelers ?? []
).map(l => 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,
})
},
+24 -34
View File
@@ -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 (
<View style={styles.footer}>
<TouchableOpacity
accessibilityRole="link"
accessibilityLabel={_(msg`Send feedback`)}
accessibilityHint=""
onPress={onPressFeedback}
style={[
styles.footerBtn,
styles.footerBtnFeedback,
theme.colorScheme === 'light'
? styles.footerBtnFeedbackLight
: styles.footerBtnFeedbackDark,
]}>
<FontAwesomeIcon
style={pal.link as FontAwesomeIconStyle}
size={18}
icon={['far', 'message']}
/>
<Text type="lg-medium" style={[pal.link, s.pl10]}>
<Button
label={_(msg`Send feedback`)}
size="small"
variant="solid"
color="secondary"
onPress={onPressFeedback}>
<ButtonIcon icon={Message} position="left" />
<ButtonText>
<Trans>Feedback</Trans>
</Text>
</TouchableOpacity>
<TouchableOpacity
accessibilityRole="link"
accessibilityLabel={_(msg`Send feedback`)}
accessibilityHint=""
</ButtonText>
</Button>
<Button
label={_(msg`Get help`)}
size="small"
variant="outline"
color="secondary"
onPress={onPressHelp}
style={[styles.footerBtn]}>
<Text type="lg-medium" style={[pal.link, s.pl10]}>
style={{
backgroundColor: 'transparent',
}}>
<ButtonText>
<Trans>Help</Trans>
</Text>
</TouchableOpacity>
</ButtonText>
</Button>
</View>
)
}