Merge remote-tracking branch 'origin/main' into samuel/alf-login
This commit is contained in:
commit
d24ffba01d
62 changed files with 30007 additions and 10775 deletions
|
@ -447,7 +447,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
/>
|
||||
)}
|
||||
{quote ? (
|
||||
<View style={[s.mt5, isWeb && s.mb10]}>
|
||||
<View style={[s.mt5, isWeb && s.mb10, {pointerEvents: 'none'}]}>
|
||||
<QuoteEmbed quote={quote} />
|
||||
</View>
|
||||
) : undefined}
|
||||
|
|
45
src/view/com/composer/useExternalLinkFetch.e2e.ts
Normal file
45
src/view/com/composer/useExternalLinkFetch.e2e.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import {useState, useEffect} from 'react'
|
||||
import * as apilib from 'lib/api/index'
|
||||
import {getLinkMeta} from 'lib/link-meta/link-meta'
|
||||
import {ComposerOpts} from 'state/shell/composer'
|
||||
import {getAgent} from '#/state/session'
|
||||
|
||||
export function useExternalLinkFetch({}: {
|
||||
setQuote: (opts: ComposerOpts['quote']) => void
|
||||
}) {
|
||||
const [extLink, setExtLink] = useState<apilib.ExternalEmbedDraft | undefined>(
|
||||
undefined,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
let aborted = false
|
||||
const cleanup = () => {
|
||||
aborted = true
|
||||
}
|
||||
if (!extLink) {
|
||||
return cleanup
|
||||
}
|
||||
if (!extLink.meta) {
|
||||
getLinkMeta(getAgent(), extLink.uri).then(meta => {
|
||||
if (aborted) {
|
||||
return
|
||||
}
|
||||
setExtLink({
|
||||
uri: extLink.uri,
|
||||
isLoading: !!meta.image,
|
||||
meta,
|
||||
})
|
||||
})
|
||||
return cleanup
|
||||
}
|
||||
if (extLink.isLoading) {
|
||||
setExtLink({
|
||||
...extLink,
|
||||
isLoading: false, // done
|
||||
})
|
||||
}
|
||||
return cleanup
|
||||
}, [extLink])
|
||||
|
||||
return {extLink, setExtLink}
|
||||
}
|
|
@ -22,6 +22,7 @@ import {listenSoftReset} from '#/state/events'
|
|||
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||
import {TabState, getTabState, getRootNavigation} from '#/lib/routes/helpers'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
|
||||
const POLL_FREQ = 60e3 // 60sec
|
||||
|
||||
|
@ -68,6 +69,10 @@ export function FeedPage({
|
|||
scrollToTop()
|
||||
truncateAndInvalidate(queryClient, FEED_RQKEY(feed))
|
||||
setHasNew(false)
|
||||
logEvent('feed:refresh', {
|
||||
feedType: feed.split('|')[0],
|
||||
reason: 'soft-reset',
|
||||
})
|
||||
}
|
||||
}, [navigation, isPageFocused, scrollToTop, queryClient, feed, setHasNew])
|
||||
|
||||
|
@ -89,6 +94,10 @@ export function FeedPage({
|
|||
scrollToTop()
|
||||
truncateAndInvalidate(queryClient, FEED_RQKEY(feed))
|
||||
setHasNew(false)
|
||||
logEvent('feed:refresh', {
|
||||
feedType: feed.split('|')[0],
|
||||
reason: 'load-latest',
|
||||
})
|
||||
}, [scrollToTop, feed, queryClient, setHasNew])
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,7 +6,13 @@
|
|||
*
|
||||
*/
|
||||
import React from 'react'
|
||||
import {SafeAreaView, Text, TouchableOpacity, StyleSheet} from 'react-native'
|
||||
import {
|
||||
SafeAreaView,
|
||||
TouchableOpacity,
|
||||
StyleSheet,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
|
@ -23,14 +29,14 @@ const ImageDefaultHeader = ({onRequestClose}: Props) => {
|
|||
return (
|
||||
<SafeAreaView style={styles.root}>
|
||||
<TouchableOpacity
|
||||
style={styles.closeButton}
|
||||
style={[styles.closeButton, styles.blurredBackground]}
|
||||
onPress={onRequestClose}
|
||||
hitSlop={HIT_SLOP}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Close image`)}
|
||||
accessibilityHint={_(msg`Closes viewer for header image`)}
|
||||
onAccessibilityEscape={onRequestClose}>
|
||||
<Text style={styles.closeText}>✕</Text>
|
||||
<FontAwesomeIcon icon="close" color={'#fff'} size={22} />
|
||||
</TouchableOpacity>
|
||||
</SafeAreaView>
|
||||
)
|
||||
|
@ -42,8 +48,8 @@ const styles = StyleSheet.create({
|
|||
pointerEvents: 'box-none',
|
||||
},
|
||||
closeButton: {
|
||||
marginRight: 8,
|
||||
marginTop: 8,
|
||||
marginRight: 10,
|
||||
marginTop: 10,
|
||||
width: 44,
|
||||
height: 44,
|
||||
alignItems: 'center',
|
||||
|
@ -51,13 +57,10 @@ const styles = StyleSheet.create({
|
|||
borderRadius: 22,
|
||||
backgroundColor: '#00000077',
|
||||
},
|
||||
closeText: {
|
||||
lineHeight: 22,
|
||||
fontSize: 19,
|
||||
textAlign: 'center',
|
||||
color: '#FFF',
|
||||
includeFontPadding: false,
|
||||
},
|
||||
blurredBackground: {
|
||||
backdropFilter: 'blur(10px)',
|
||||
WebkitBackdropFilter: 'blur(10px)',
|
||||
} as ViewStyle,
|
||||
})
|
||||
|
||||
export default ImageDefaultHeader
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
StyleSheet,
|
||||
View,
|
||||
Pressable,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {
|
||||
FontAwesomeIcon,
|
||||
|
@ -24,6 +25,7 @@ import {
|
|||
ProfileImageLightbox,
|
||||
} from '#/state/lightbox'
|
||||
import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
|
||||
interface Img {
|
||||
uri: string
|
||||
|
@ -111,6 +113,14 @@ function LightboxInner({
|
|||
return () => window.removeEventListener('keydown', onKeyDown)
|
||||
}, [onKeyDown])
|
||||
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
const btnStyle = React.useMemo(() => {
|
||||
return isTabletOrDesktop ? styles.btnTablet : styles.btnMobile
|
||||
}, [isTabletOrDesktop])
|
||||
const iconSize = React.useMemo(() => {
|
||||
return isTabletOrDesktop ? 32 : 24
|
||||
}, [isTabletOrDesktop])
|
||||
|
||||
return (
|
||||
<View style={styles.mask}>
|
||||
<TouchableWithoutFeedback
|
||||
|
@ -130,28 +140,38 @@ function LightboxInner({
|
|||
{canGoLeft && (
|
||||
<TouchableOpacity
|
||||
onPress={onPressLeft}
|
||||
style={[styles.btn, styles.leftBtn]}
|
||||
style={[
|
||||
styles.btn,
|
||||
btnStyle,
|
||||
styles.leftBtn,
|
||||
styles.blurredBackground,
|
||||
]}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Previous image`)}
|
||||
accessibilityHint="">
|
||||
<FontAwesomeIcon
|
||||
icon="angle-left"
|
||||
style={styles.icon as FontAwesomeIconStyle}
|
||||
size={40}
|
||||
size={iconSize}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{canGoRight && (
|
||||
<TouchableOpacity
|
||||
onPress={onPressRight}
|
||||
style={[styles.btn, styles.rightBtn]}
|
||||
style={[
|
||||
styles.btn,
|
||||
btnStyle,
|
||||
styles.rightBtn,
|
||||
styles.blurredBackground,
|
||||
]}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Next image`)}
|
||||
accessibilityHint="">
|
||||
<FontAwesomeIcon
|
||||
icon="angle-right"
|
||||
style={styles.icon as FontAwesomeIconStyle}
|
||||
size={40}
|
||||
size={iconSize}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
@ -213,20 +233,30 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
btn: {
|
||||
position: 'absolute',
|
||||
backgroundColor: '#000',
|
||||
width: 50,
|
||||
height: 50,
|
||||
backgroundColor: '#00000077',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
btnTablet: {
|
||||
width: 50,
|
||||
height: 50,
|
||||
borderRadius: 25,
|
||||
left: 30,
|
||||
right: 30,
|
||||
},
|
||||
btnMobile: {
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 22,
|
||||
left: 20,
|
||||
right: 20,
|
||||
},
|
||||
leftBtn: {
|
||||
left: 30,
|
||||
right: 'auto',
|
||||
top: '50%',
|
||||
},
|
||||
rightBtn: {
|
||||
position: 'absolute',
|
||||
right: 30,
|
||||
left: 'auto',
|
||||
top: '50%',
|
||||
},
|
||||
footer: {
|
||||
|
@ -234,4 +264,8 @@ const styles = StyleSheet.create({
|
|||
paddingVertical: 24,
|
||||
backgroundColor: colors.black,
|
||||
},
|
||||
blurredBackground: {
|
||||
backdropFilter: 'blur(10px)',
|
||||
WebkitBackdropFilter: 'blur(10px)',
|
||||
} as ViewStyle,
|
||||
})
|
||||
|
|
|
@ -39,7 +39,7 @@ function SwitchAccountCard({account}: {account: SessionAccount}) {
|
|||
track('Settings:SignOutButtonClicked')
|
||||
closeAllActiveElements()
|
||||
// needs to be in timeout or the modal re-opens
|
||||
setTimeout(() => logout(), 0)
|
||||
setTimeout(() => logout('SwitchAccount'), 0)
|
||||
}, [track, logout, closeAllActiveElements])
|
||||
|
||||
const contents = (
|
||||
|
@ -95,7 +95,9 @@ function SwitchAccountCard({account}: {account: SessionAccount}) {
|
|||
key={account.did}
|
||||
style={[isSwitchingAccounts && styles.dimmed]}
|
||||
onPress={
|
||||
isSwitchingAccounts ? undefined : () => onPressSwitchAccount(account)
|
||||
isSwitchingAccounts
|
||||
? undefined
|
||||
: () => onPressSwitchAccount(account, 'SwitchAccount')
|
||||
}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Switch to ${account.handle}`)}
|
||||
|
|
|
@ -108,7 +108,8 @@ export function PostThread({
|
|||
?.ui('contentList')
|
||||
.blurs.find(
|
||||
cause =>
|
||||
cause.type === 'label' && cause.labelDef.id === '!no-unauthenticated',
|
||||
cause.type === 'label' &&
|
||||
cause.labelDef.identifier === '!no-unauthenticated',
|
||||
)
|
||||
}, [rootPost, moderationOpts])
|
||||
|
||||
|
|
|
@ -90,6 +90,7 @@ let Feed = ({
|
|||
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||
const checkForNewRef = React.useRef<(() => void) | null>(null)
|
||||
const lastFetchRef = React.useRef<number>(Date.now())
|
||||
const feedType = feed.split('|')[0]
|
||||
|
||||
const opts = React.useMemo(
|
||||
() => ({enabled, ignoreFilterFor}),
|
||||
|
@ -214,6 +215,10 @@ let Feed = ({
|
|||
|
||||
const onRefresh = React.useCallback(async () => {
|
||||
track('Feed:onRefresh')
|
||||
logEvent('feed:refresh', {
|
||||
feedType: feedType,
|
||||
reason: 'pull-to-refresh',
|
||||
})
|
||||
setIsPTRing(true)
|
||||
try {
|
||||
await refetch()
|
||||
|
@ -222,9 +227,8 @@ let Feed = ({
|
|||
logger.error('Failed to refresh posts feed', {message: err})
|
||||
}
|
||||
setIsPTRing(false)
|
||||
}, [refetch, track, setIsPTRing, onHasNew])
|
||||
}, [refetch, track, setIsPTRing, onHasNew, feedType])
|
||||
|
||||
const feedType = feed.split('|')[0]
|
||||
const onEndReached = React.useCallback(async () => {
|
||||
if (isFetching || !hasNextPage || isError) return
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ export function FeedErrorMessage({
|
|||
if (
|
||||
typeof knownError !== 'undefined' &&
|
||||
knownError !== KnownError.Unknown &&
|
||||
feedDesc.startsWith('feedgen')
|
||||
(feedDesc.startsWith('feedgen') || knownError === KnownError.FeedNSFPublic)
|
||||
) {
|
||||
return (
|
||||
<FeedgenErrorMessage
|
||||
|
@ -240,6 +240,9 @@ function detectKnownError(
|
|||
if (typeof error !== 'string') {
|
||||
error = error.toString()
|
||||
}
|
||||
if (error.includes(KnownError.FeedNSFPublic)) {
|
||||
return KnownError.FeedNSFPublic
|
||||
}
|
||||
if (!feedDesc.startsWith('feedgen')) {
|
||||
return KnownError.Unknown
|
||||
}
|
||||
|
@ -263,8 +266,5 @@ function detectKnownError(
|
|||
if (error.includes('feed provided an invalid response')) {
|
||||
return KnownError.FeedgenBadResponse
|
||||
}
|
||||
if (error.includes(KnownError.FeedNSFPublic)) {
|
||||
return KnownError.FeedNSFPublic
|
||||
}
|
||||
return KnownError.FeedgenUnknown
|
||||
}
|
||||
|
|
|
@ -22,18 +22,24 @@ export function TestCtrls() {
|
|||
const {mutate: setFeedViewPref} = useSetFeedViewPreferencesMutation()
|
||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||
const onPressSignInAlice = async () => {
|
||||
await login({
|
||||
service: 'http://localhost:3000',
|
||||
identifier: 'alice.test',
|
||||
password: 'hunter2',
|
||||
})
|
||||
await login(
|
||||
{
|
||||
service: 'http://localhost:3000',
|
||||
identifier: 'alice.test',
|
||||
password: 'hunter2',
|
||||
},
|
||||
'LoginForm',
|
||||
)
|
||||
}
|
||||
const onPressSignInBob = async () => {
|
||||
await login({
|
||||
service: 'http://localhost:3000',
|
||||
identifier: 'bob.test',
|
||||
password: 'hunter2',
|
||||
})
|
||||
await login(
|
||||
{
|
||||
service: 'http://localhost:3000',
|
||||
identifier: 'bob.test',
|
||||
password: 'hunter2',
|
||||
},
|
||||
'LoginForm',
|
||||
)
|
||||
}
|
||||
return (
|
||||
<View style={{position: 'absolute', top: 100, right: 0, zIndex: 100}}>
|
||||
|
@ -51,7 +57,7 @@ export function TestCtrls() {
|
|||
/>
|
||||
<Pressable
|
||||
testID="e2eSignOut"
|
||||
onPress={() => logout()}
|
||||
onPress={() => logout('Settings')}
|
||||
accessibilityRole="button"
|
||||
style={BTN}
|
||||
/>
|
||||
|
|
|
@ -298,7 +298,10 @@ let EditableUserAvatar = ({
|
|||
<Menu.Root>
|
||||
<Menu.Trigger label={_(msg`Edit avatar`)}>
|
||||
{({props}) => (
|
||||
<TouchableOpacity {...props} activeOpacity={0.8}>
|
||||
<TouchableOpacity
|
||||
{...props}
|
||||
activeOpacity={0.8}
|
||||
testID="changeAvatarBtn">
|
||||
{avatar ? (
|
||||
<HighPriorityImage
|
||||
testID="userAvatarImage"
|
||||
|
|
|
@ -84,7 +84,10 @@ export function UserBanner({
|
|||
<Menu.Root>
|
||||
<Menu.Trigger label={_(msg`Edit avatar`)}>
|
||||
{({props}) => (
|
||||
<TouchableOpacity {...props} activeOpacity={0.8}>
|
||||
<TouchableOpacity
|
||||
{...props}
|
||||
activeOpacity={0.8}
|
||||
testID="changeBannerBtn">
|
||||
{banner ? (
|
||||
<Image
|
||||
testID="userBannerImage"
|
||||
|
|
|
@ -237,7 +237,7 @@ const styles = StyleSheet.create({
|
|||
paddingRight: 12,
|
||||
borderRadius: 8,
|
||||
fontFamily:
|
||||
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
||||
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif',
|
||||
outline: 0,
|
||||
border: 0,
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import {Text as RNText, TextProps} from 'react-native'
|
||||
import {s, lh} from 'lib/styles'
|
||||
import {useTheme, TypographyVariant} from 'lib/ThemeContext'
|
||||
import {isIOS} from 'platform/detection'
|
||||
import {isIOS, isWeb} from 'platform/detection'
|
||||
import {UITextView} from 'react-native-ui-text-view'
|
||||
|
||||
export type CustomTextProps = TextProps & {
|
||||
|
@ -13,6 +13,11 @@ export type CustomTextProps = TextProps & {
|
|||
selectable?: boolean
|
||||
}
|
||||
|
||||
const fontFamilyStyle = {
|
||||
fontFamily:
|
||||
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif',
|
||||
}
|
||||
|
||||
export function Text({
|
||||
type = 'md',
|
||||
children,
|
||||
|
@ -39,7 +44,13 @@ export function Text({
|
|||
|
||||
return (
|
||||
<RNText
|
||||
style={[s.black, typography, lineHeightStyle, style]}
|
||||
style={[
|
||||
s.black,
|
||||
typography,
|
||||
isWeb && fontFamilyStyle,
|
||||
lineHeightStyle,
|
||||
style,
|
||||
]}
|
||||
// @ts-ignore web only -esb
|
||||
dataSet={Object.assign({tooltip: title}, dataSet || {})}
|
||||
selectable={selectable}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue