Merge remote-tracking branch 'origin/main' into date-utils-fix

* origin/main:
  Explore fixes (#4540)
  Is it "newskie" or "newsky" 🤔  (#4557)
  fix keyboard overlaying onboarding inputs (#4558)
This commit is contained in:
Eric Bailey
2024-06-18 13:41:47 -05:00
9 changed files with 128 additions and 16 deletions
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#FFC404" fill-rule="evenodd" d="M11.183 8.561c0 .544.348.984.892.984.545 0 .893-.44.893-.985V6.985c0-.544-.348-.985-.893-.985-.543 0-.892.44-.892.985v1.576Zm5.94 7.481c0 .539-.438.942-.976.942H8.004c-.538 0-.975-.411-.975-.95 0-2.782 2.264-5.021 5.046-5.021 2.783 0 5.047 2.247 5.047 5.03Zm-.43-4.584a.983.983 0 0 1 0-1.393l1.114-1.114a.985.985 0 0 1 1.393 1.393l-1.114 1.114a.985.985 0 0 1-1.393 0Zm2.897 3.741h1.575c.544 0 .985.349.985.892 0 .544-.44.892-.985.892h-1.67a.872.872 0 0 1-.89-.887c0-.543.44-.897.985-.897Zm-14.045.893c0-.544-.44-.892-.985-.892H2.985c-.544 0-.985.349-.985.892 0 .544.44.892.985.892H4.56c.545 0 .985-.349.985-.892Zm1.913-6.027a.985.985 0 0 1-1.393 1.393L4.95 10.344A.985.985 0 0 1 6.344 8.95l1.114 1.114Z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 848 B

+3
View File
@@ -267,6 +267,9 @@ export const atoms = {
font_bold: { font_bold: {
fontWeight: tokens.fontWeight.bold, fontWeight: tokens.fontWeight.bold,
}, },
font_heavy: {
fontWeight: tokens.fontWeight.heavy,
},
italic: { italic: {
fontStyle: 'italic', fontStyle: 'italic',
}, },
+1
View File
@@ -118,6 +118,7 @@ export const fontWeight = {
normal: '400', normal: '400',
semibold: '500', semibold: '500',
bold: '600', bold: '600',
heavy: '700',
} as const } as const
export const gradients = { export const gradients = {
+81
View File
@@ -0,0 +1,81 @@
import React from 'react'
import {View} from 'react-native'
import {AppBskyActorDefs, moderateProfile} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {differenceInSeconds} from 'date-fns'
import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {HITSLOP_10} from 'lib/constants'
import {sanitizeDisplayName} from 'lib/strings/display-names'
import {atoms as a} from '#/alf'
import {Button} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {useDialogControl} from '#/components/Dialog'
import {Newskie} from '#/components/icons/Newskie'
import {Text} from '#/components/Typography'
export function NewskieDialog({
profile,
}: {
profile: AppBskyActorDefs.ProfileViewDetailed
}) {
const {_} = useLingui()
const moderationOpts = useModerationOpts()
const control = useDialogControl()
const profileName = React.useMemo(() => {
const name = profile.displayName || profile.handle
if (!moderationOpts) return name
const moderation = moderateProfile(profile, moderationOpts)
return sanitizeDisplayName(name, moderation.ui('displayName'))
}, [moderationOpts, profile])
const timeAgo = useGetTimeAgo()
const createdAt = profile.createdAt as string | undefined
const daysOld = React.useMemo(() => {
if (!createdAt) return Infinity
return differenceInSeconds(new Date(), new Date(createdAt)) / 86400
}, [createdAt])
if (!createdAt || daysOld > 7) return null
return (
<View style={[a.pr_2xs]}>
<Button
label={_(
msg`This user is new here. Press for more info about when they joined.`,
)}
hitSlop={HITSLOP_10}
onPress={control.open}>
{({hovered, pressed}) => (
<Newskie
size="lg"
fill="#FFC404"
style={{
opacity: hovered || pressed ? 0.5 : 1,
}}
/>
)}
</Button>
<Dialog.Outer control={control}>
<Dialog.Handle />
<Dialog.ScrollableInner
label={_(msg`New user info dialog`)}
style={[{width: 'auto', maxWidth: 400, minWidth: 200}]}>
<View style={[a.gap_sm]}>
<Text style={[a.font_bold, a.text_xl]}>
<Trans>Say hello!</Trans>
</Text>
<Text style={[a.text_md]}>
<Trans>
{profileName} joined Bluesky{' '}
{timeAgo(createdAt, {format: 'long'})} ago
</Trans>
</Text>
</View>
</Dialog.ScrollableInner>
</Dialog.Outer>
</View>
)
}
+5 -2
View File
@@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import {View} from 'react-native' import {Keyboard, View} from 'react-native'
import DatePicker from 'react-native-date-picker' import DatePicker from 'react-native-date-picker'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@@ -49,7 +49,10 @@ export function DateField({
<DateFieldButton <DateFieldButton
label={label} label={label}
value={value} value={value}
onPress={control.open} onPress={() => {
Keyboard.dismiss()
control.open()
}}
isInvalid={isInvalid} isInvalid={isInvalid}
accessibilityHint={accessibilityHint} accessibilityHint={accessibilityHint}
/> />
+5
View File
@@ -0,0 +1,5 @@
import {createSinglePathSVG} from './TEMPLATE'
export const Newskie = createSinglePathSVG({
path: 'M11.183 8.561c0 .544.348.984.892.984.545 0 .893-.44.893-.985V6.985c0-.544-.348-.985-.893-.985-.543 0-.892.44-.892.985v1.576Zm5.94 7.481c0 .539-.438.942-.976.942H8.004c-.538 0-.975-.411-.975-.95 0-2.782 2.264-5.021 5.046-5.021 2.783 0 5.047 2.247 5.047 5.03Zm-.43-4.584a.983.983 0 0 1 0-1.393l1.114-1.114a.985.985 0 0 1 1.393 1.393l-1.114 1.114a.985.985 0 0 1-1.393 0Zm2.897 3.741h1.575c.544 0 .985.349.985.892 0 .544-.44.892-.985.892h-1.67a.872.872 0 0 1-.89-.887c0-.543.44-.897.985-.897Zm-14.045.893c0-.544-.44-.892-.985-.892H2.985c-.544 0-.985.349-.985.892 0 .544.44.892.985.892H4.56c.545 0 .985-.349.985-.892Zm1.913-6.027a.985.985 0 0 1-1.393 1.393L4.95 10.344A.985.985 0 0 1 6.344 8.95l1.114 1.114Z',
})
+6 -1
View File
@@ -5,7 +5,9 @@ import {Trans} from '@lingui/macro'
import {Shadow} from '#/state/cache/types' import {Shadow} from '#/state/cache/types'
import {isInvalidHandle} from 'lib/strings/handles' import {isInvalidHandle} from 'lib/strings/handles'
import {isAndroid} from 'platform/detection'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
import {NewskieDialog} from '#/components/NewskieDialog'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
export function ProfileHeaderHandle({ export function ProfileHeaderHandle({
@@ -17,7 +19,10 @@ export function ProfileHeaderHandle({
const invalidHandle = isInvalidHandle(profile.handle) const invalidHandle = isInvalidHandle(profile.handle)
const blockHide = profile.viewer?.blocking || profile.viewer?.blockedBy const blockHide = profile.viewer?.blocking || profile.viewer?.blockedBy
return ( return (
<View style={[a.flex_row, a.gap_xs, a.align_center]} pointerEvents="none"> <View
style={[a.flex_row, a.gap_xs, a.align_center]}
pointerEvents={isAndroid ? 'box-only' : 'auto'}>
<NewskieDialog profile={profile} />
{profile.viewer?.followedBy && !blockHide ? ( {profile.viewer?.followedBy && !blockHide ? (
<View style={[t.atoms.bg_contrast_25, a.rounded_xs, a.px_sm, a.py_xs]}> <View style={[t.atoms.bg_contrast_25, a.rounded_xs, a.px_sm, a.py_xs]}>
<Text style={[t.atoms.text, a.text_sm]}> <Text style={[t.atoms.text, a.text_sm]}>
@@ -3,6 +3,7 @@ import {ScrollView, StyleSheet, View} from 'react-native'
import {isWeb} from '#/platform/detection' import {isWeb} from '#/platform/detection'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
import {useIsKeyboardVisible} from 'lib/hooks/useIsKeyboardVisible'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
@@ -29,13 +30,18 @@ export const LoggedOutLayout = ({
borderLeftWidth: 1, borderLeftWidth: 1,
}) })
const [isKeyboardVisible] = useIsKeyboardVisible()
if (isMobile) { if (isMobile) {
if (scrollable) { if (scrollable) {
return ( return (
<ScrollView <ScrollView
style={styles.scrollview} style={styles.scrollview}
keyboardShouldPersistTaps="handled" keyboardShouldPersistTaps="handled"
keyboardDismissMode="on-drag"> keyboardDismissMode="none"
contentContainerStyle={[
{paddingBottom: isKeyboardVisible ? 300 : 0},
]}>
<View style={a.pt_md}>{children}</View> <View style={a.pt_md}>{children}</View>
</ScrollView> </ScrollView>
) )
+19 -12
View File
@@ -64,7 +64,7 @@ function SuggestedItemsHeader({
fill={t.palette.primary_500} fill={t.palette.primary_500}
style={{marginLeft: -2}} style={{marginLeft: -2}}
/> />
<Text style={[a.text_2xl, a.font_bold, t.atoms.text]}>{title}</Text> <Text style={[a.text_2xl, a.font_heavy, t.atoms.text]}>{title}</Text>
</View> </View>
<Text style={[t.atoms.text_contrast_high, a.leading_snug]}> <Text style={[t.atoms.text_contrast_high, a.leading_snug]}>
{description} {description}
@@ -119,6 +119,9 @@ function LoadMore({
}) })
.filter(Boolean) as LoadMoreItems[] .filter(Boolean) as LoadMoreItems[]
}, [item.items, moderationOpts]) }, [item.items, moderationOpts])
if (items.length === 0) return null
const type = items[0].type const type = items[0].type
return ( return (
@@ -142,20 +145,20 @@ function LoadMore({
a.relative, a.relative,
{ {
height: 32, height: 32,
width: 32 + 15 * 3, width: 32 + 15 * items.length,
}, },
]}> ]}>
<View <View
style={[ style={[
a.align_center, a.align_center,
a.justify_center, a.justify_center,
a.border,
t.atoms.bg_contrast_25, t.atoms.bg_contrast_25,
a.absolute, a.absolute,
{ {
width: 30, width: 30,
height: 30, height: 30,
left: 0, left: 0,
borderWidth: 1,
backgroundColor: t.palette.primary_500, backgroundColor: t.palette.primary_500,
borderColor: t.atoms.bg.backgroundColor, borderColor: t.atoms.bg.backgroundColor,
borderRadius: type === 'profile' ? 999 : 4, borderRadius: type === 'profile' ? 999 : 4,
@@ -169,13 +172,13 @@ function LoadMore({
<View <View
key={_item.key} key={_item.key}
style={[ style={[
a.border,
t.atoms.bg_contrast_25, t.atoms.bg_contrast_25,
a.absolute, a.absolute,
{ {
width: 30, width: 30,
height: 30, height: 30,
left: (i + 1) * 15, left: (i + 1) * 15,
borderWidth: 1,
borderColor: t.atoms.bg.backgroundColor, borderColor: t.atoms.bg.backgroundColor,
borderRadius: _item.type === 'profile' ? 999 : 4, borderRadius: _item.type === 'profile' ? 999 : 4,
zIndex: 3 - i, zIndex: 3 - i,
@@ -350,13 +353,15 @@ export function Explore() {
} }
} }
i.push({ if (hasNextProfilesPage) {
type: 'loadMore', i.push({
key: 'loadMoreProfiles', type: 'loadMore',
isLoadingMore: isLoadingMoreProfiles, key: 'loadMoreProfiles',
onLoadMore: onLoadMoreProfiles, isLoadingMore: isLoadingMoreProfiles,
items: i.filter(item => item.type === 'profile').slice(-3), onLoadMore: onLoadMoreProfiles,
}) items: i.filter(item => item.type === 'profile').slice(-3),
})
}
} else { } else {
if (profilesError) { if (profilesError) {
i.push({ i.push({
@@ -412,7 +417,7 @@ export function Explore() {
message: _(msg`Failed to load feeds preferences`), message: _(msg`Failed to load feeds preferences`),
error: cleanError(preferencesError), error: cleanError(preferencesError),
}) })
} else { } else if (hasNextFeedsPage) {
i.push({ i.push({
type: 'loadMore', type: 'loadMore',
key: 'loadMoreFeeds', key: 'loadMoreFeeds',
@@ -454,6 +459,8 @@ export function Explore() {
profilesError, profilesError,
feedsError, feedsError,
preferencesError, preferencesError,
hasNextProfilesPage,
hasNextFeedsPage,
]) ])
const renderItem = React.useCallback( const renderItem = React.useCallback(