merge main

This commit is contained in:
Hailey
2024-06-10 13:20:05 -07:00
11 changed files with 101 additions and 33 deletions
+3 -11
View File
@@ -12,7 +12,8 @@ import {
isExternalUrl,
linkRequiresWarning,
} from '#/lib/strings/url-helpers'
import {isNative, isWeb} from '#/platform/detection'
import {isNative} from '#/platform/detection'
import {shouldClickOpenNewTab} from '#/platform/urls'
import {useModalControls} from '#/state/modals'
import {useOpenLink} from '#/state/preferences/in-app-browser'
import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped'
@@ -116,16 +117,7 @@ export function useLink({
if (isExternal) {
openLink(href)
} else {
/**
* A `GestureResponderEvent`, but cast to `any` to avoid using a bunch
* of @ts-ignore below.
*/
const event = e as any
const isMiddleClick = isWeb && event.button === 1
const isMetaKey =
isWeb &&
(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey)
const shouldOpenInNewTab = isMetaKey || isMiddleClick
const shouldOpenInNewTab = shouldClickOpenNewTab(e)
if (isBskyDownloadUrl(href)) {
shareUrl(BSKY_DOWNLOAD_URL)
+8 -2
View File
@@ -72,14 +72,17 @@ export function useNotificationsRegistration() {
export function useRequestNotificationsPermission() {
const gate = useGate()
const {currentAccount} = useSession()
return async (context: 'StartOnboarding' | 'AfterOnboarding' | 'Login') => {
return async (
context: 'StartOnboarding' | 'AfterOnboarding' | 'Login' | 'Home',
) => {
const permissions = await Notifications.getPermissionsAsync()
if (
!isNative ||
permissions?.status === 'granted' ||
permissions?.status === 'denied'
(permissions?.status === 'denied' && !permissions.canAskAgain)
) {
return
}
@@ -95,6 +98,9 @@ export function useRequestNotificationsPermission() {
) {
return
}
if (context === 'Home' && !currentAccount) {
return
}
const res = await Notifications.requestPermissionsAsync()
logEvent('notifications:request', {
+1 -1
View File
@@ -17,7 +17,7 @@ export type LogEvents = {
}
'notifications:openApp': {}
'notifications:request': {
context: 'StartOnboarding' | 'AfterOnboarding' | 'Login'
context: 'StartOnboarding' | 'AfterOnboarding' | 'Login' | 'Home'
status: 'granted' | 'denied' | 'undetermined'
}
'state:background:sampled': {
+18 -1
View File
@@ -116,7 +116,7 @@ export function sanitizeAppLanguageSetting(appLanguage: string): AppLanguage {
const langs = appLanguage.split(',').filter(Boolean)
for (const lang of langs) {
switch (lang) {
switch (fixLegacyLanguageCode(lang)) {
case 'en':
return AppLanguage.en
case 'ca':
@@ -157,3 +157,20 @@ export function sanitizeAppLanguageSetting(appLanguage: string): AppLanguage {
}
return AppLanguage.en
}
export function fixLegacyLanguageCode(code: string | null): string | null {
// handle some legacy code conversions, see https://xml.coverpages.org/iso639a.html
if (code === 'in') {
// indonesian
return 'id'
}
if (code === 'iw') {
// hebrew
return 'he'
}
if (code === 'ji') {
// yiddish
return 'yi'
}
return code
}
+2 -1
View File
@@ -2,6 +2,7 @@ import {Platform} from 'react-native'
import {isReducedMotion} from 'react-native-reanimated'
import {getLocales} from 'expo-localization'
import {fixLegacyLanguageCode} from '#/locale/helpers'
import {dedupArray} from 'lib/functions'
export const isIOS = Platform.OS === 'ios'
@@ -17,7 +18,7 @@ export const isMobileWeb =
export const deviceLocales = dedupArray(
getLocales?.()
.map?.(locale => locale.languageCode)
.map?.(locale => fixLegacyLanguageCode(locale.languageCode))
.filter(code => typeof code === 'string'),
) as string[]
+14 -1
View File
@@ -1,4 +1,5 @@
import {Linking} from 'react-native'
import {GestureResponderEvent, Linking} from 'react-native'
import {isNative, isWeb} from './detection'
export async function getInitialURL(): Promise<string | undefined> {
@@ -23,3 +24,15 @@ export function clearHash() {
window.location.hash = ''
}
}
export function shouldClickOpenNewTab(e: GestureResponderEvent) {
/**
* A `GestureResponderEvent`, but cast to `any` to avoid using a bunch
* of @ts-ignore below.
*/
const event = e as any
const isMiddleClick = isWeb && event.button === 1
const isMetaKey =
isWeb && (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey)
return isMetaKey || isMiddleClick
}
+6
View File
@@ -13,7 +13,13 @@ export async function write(value: Schema) {
export async function read(): Promise<Schema | undefined> {
const rawData = await AsyncStorage.getItem(BSKY_STORAGE)
const objData = rawData ? JSON.parse(rawData) : undefined
// new user
if (!objData) return undefined
// existing user, validate
const parsed = schema.safeParse(objData)
if (parsed.success) {
return objData
} else {
+10 -3
View File
@@ -305,7 +305,13 @@ export const ComposePost = observer(function ComposePost({
localThumb: undefined,
} as apilib.ExternalEmbedDraft)
}
setError(cleanError(e.message))
let err = cleanError(e.message)
if (err.includes('not locate record')) {
err = _(
msg`We're sorry! The post you are replying to has been deleted.`,
)
}
setError(err)
setIsProcessing(false)
return
} finally {
@@ -785,11 +791,12 @@ const styles = StyleSheet.create({
},
errorLine: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.red1,
borderRadius: 6,
marginHorizontal: 16,
paddingHorizontal: 8,
paddingVertical: 6,
paddingHorizontal: 12,
paddingVertical: 10,
marginBottom: 8,
},
reminderLine: {
+31 -10
View File
@@ -1,5 +1,12 @@
import React from 'react'
import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {
Linking,
Pressable,
StyleProp,
StyleSheet,
View,
ViewStyle,
} from 'react-native'
import {AtUri} from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Plural, Trans} from '@lingui/macro'
@@ -26,6 +33,7 @@ import {RichText} from '#/components/RichText'
import {Text} from '../util/text/Text'
import {UserAvatar} from '../util/UserAvatar'
import hairlineWidth = StyleSheet.hairlineWidth
import {shouldClickOpenNewTab} from '#/platform/urls'
export function FeedSourceCard({
feedUri,
@@ -203,17 +211,30 @@ export function FeedSourceCardLoaded({
style,
{borderTopWidth: hideTopBorder ? 0 : hairlineWidth},
]}
onPress={() => {
onPress={e => {
const shouldOpenInNewTab = shouldClickOpenNewTab(e)
if (feed.type === 'feed') {
navigation.push('ProfileFeed', {
name: feed.creatorDid,
rkey: new AtUri(feed.uri).rkey,
})
if (shouldOpenInNewTab) {
Linking.openURL(
`/profile/${feed.creatorDid}/feed/${new AtUri(feed.uri).rkey}`,
)
} else {
navigation.push('ProfileFeed', {
name: feed.creatorDid,
rkey: new AtUri(feed.uri).rkey,
})
}
} else if (feed.type === 'list') {
navigation.push('ProfileList', {
name: feed.creatorDid,
rkey: new AtUri(feed.uri).rkey,
})
if (shouldOpenInNewTab) {
Linking.openURL(
`/profile/${feed.creatorDid}/lists/${new AtUri(feed.uri).rkey}`,
)
} else {
navigation.push('ProfileList', {
name: feed.creatorDid,
rkey: new AtUri(feed.uri).rkey,
})
}
}
}}
key={feed.uri}>
+2 -3
View File
@@ -49,8 +49,7 @@ export function FeedErrorMessage({
if (
typeof knownError !== 'undefined' &&
knownError !== KnownError.Unknown &&
(savedFeedConfig?.type === 'feed' ||
knownError === KnownError.FeedNSFPublic)
feedDesc.startsWith('feedgen')
) {
return (
<FeedgenErrorMessage
@@ -272,7 +271,7 @@ function detectKnownError(
) {
return KnownError.FeedgenMisconfigured
}
if (error.includes('feed provided an invalid response')) {
if (error.includes('invalid response')) {
return KnownError.FeedgenBadResponse
}
return KnownError.FeedgenUnknown
+6
View File
@@ -20,6 +20,7 @@ import {
} from '#/state/shell'
import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed'
import {useOTAUpdates} from 'lib/hooks/useOTAUpdates'
import {useRequestNotificationsPermission} from 'lib/notifications/notifications'
import {HomeTabNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
import {
useSetUsedStarterPack,
@@ -71,12 +72,17 @@ function HomeScreenReady({
const maybeFoundIndex = allFeeds.indexOf(rawSelectedFeed)
const selectedIndex = Math.max(0, maybeFoundIndex)
const selectedFeed = allFeeds[selectedIndex]
const requestNotificationsPermission = useRequestNotificationsPermission()
const usedStarterPack = useUsedStarterPack()
const setUsedStarterPack = useSetUsedStarterPack()
useSetTitle(pinnedFeedInfos[selectedIndex]?.displayName)
useOTAUpdates()
React.useEffect(() => {
requestNotificationsPermission('Home')
}, [requestNotificationsPermission])
const pagerRef = React.useRef<PagerRef>(null)
const lastPagerReportedIndexRef = React.useRef(selectedIndex)
React.useLayoutEffect(() => {