Add initial Nudge component
This commit is contained in:
@@ -0,0 +1,117 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {Keyboard, StyleProp, View, ViewStyle} from 'react-native'
|
||||||
|
import {LinearGradient} from 'expo-linear-gradient'
|
||||||
|
import {msg, Trans} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
|
import {isWeb} from '#/platform/detection'
|
||||||
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
|
import {FullWindowOverlay} from '#/components/FullWindowOverlay'
|
||||||
|
import {Portal} from '#/components/Portal'
|
||||||
|
import {Button} from './Button'
|
||||||
|
import {Text} from './Typography'
|
||||||
|
|
||||||
|
type Anchor = 'top_left' | 'top_center' | 'top_right'
|
||||||
|
|
||||||
|
interface NudgeProps {
|
||||||
|
testID?: string
|
||||||
|
isOpen: boolean
|
||||||
|
anchor: Anchor
|
||||||
|
onClose: () => void
|
||||||
|
style?: StyleProp<ViewStyle>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Nudge({
|
||||||
|
children,
|
||||||
|
testID,
|
||||||
|
isOpen,
|
||||||
|
anchor,
|
||||||
|
onClose,
|
||||||
|
style,
|
||||||
|
}: React.PropsWithChildren<NudgeProps>) {
|
||||||
|
const t = useTheme()
|
||||||
|
const {_} = useLingui()
|
||||||
|
const anchorPositioningStyle = useAnchorPositioningStyle(anchor)
|
||||||
|
return (
|
||||||
|
isOpen && (
|
||||||
|
<Portal>
|
||||||
|
<FullWindowOverlay>
|
||||||
|
{/* Backdrop overlay */}
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.fixed,
|
||||||
|
a.inset_0,
|
||||||
|
{backgroundColor: '#000', opacity: 0.1},
|
||||||
|
a.pointer_events_none,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
// iOS
|
||||||
|
accessibilityViewIsModal
|
||||||
|
// Android
|
||||||
|
importantForAccessibility="yes"
|
||||||
|
style={[a.fixed, a.px_lg, a.py_md, style]}
|
||||||
|
testID={testID}
|
||||||
|
onTouchMove={() => Keyboard.dismiss()}>
|
||||||
|
{/* Angle pointer up */}
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.absolute,
|
||||||
|
anchorPositioningStyle,
|
||||||
|
{
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
backgroundColor: t.palette.primary_500,
|
||||||
|
transform: [{rotate: '45deg'}],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinearGradient
|
||||||
|
colors={[t.palette.primary_500, t.palette.primary_400]}
|
||||||
|
locations={[0, 1]}
|
||||||
|
start={{x: 0, y: 0}}
|
||||||
|
end={{x: 0, y: 1}}
|
||||||
|
style={[a.absolute, a.inset_0, a.rounded_sm]}
|
||||||
|
/>
|
||||||
|
<View>{children}</View>
|
||||||
|
<Button label={_(msg`Okay`)} onPress={onClose}>
|
||||||
|
{({hovered}) => (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
a.px_lg,
|
||||||
|
a.py_md,
|
||||||
|
a.rounded_xs,
|
||||||
|
a.border,
|
||||||
|
{borderColor: '#fff'},
|
||||||
|
hovered && {backgroundColor: t.palette.primary_400},
|
||||||
|
]}>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.text_md,
|
||||||
|
a.text_center,
|
||||||
|
a.font_semibold,
|
||||||
|
{color: '#fff'},
|
||||||
|
]}>
|
||||||
|
<Trans>Okay</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</FullWindowOverlay>
|
||||||
|
</Portal>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function useAnchorPositioningStyle(anchor: Anchor): ViewStyle {
|
||||||
|
if (anchor === 'top_center') {
|
||||||
|
return {left: isWeb ? '48%' : '52%', top: -8}
|
||||||
|
}
|
||||||
|
if (anchor === 'top_right') {
|
||||||
|
return {right: 20, top: -8}
|
||||||
|
}
|
||||||
|
return {left: 20, top: -8}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {View} from 'react-native'
|
||||||
|
|
||||||
|
import {isWeb} from '#/platform/detection'
|
||||||
|
import {Nudge} from '#/components/Nudge'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
|
export function HomeNewUserNudge() {
|
||||||
|
const [isOpen, setIsOpen] = React.useState(true)
|
||||||
|
return (
|
||||||
|
<Nudge
|
||||||
|
isOpen={isOpen}
|
||||||
|
anchor="top_center"
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
style={
|
||||||
|
isWeb
|
||||||
|
? {
|
||||||
|
left: '50%',
|
||||||
|
top: 105,
|
||||||
|
maxWidth: 380,
|
||||||
|
transform: [{translateX: '-50%'}],
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
top: 155,
|
||||||
|
left: 10,
|
||||||
|
right: 10,
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
paddingTop: 10,
|
||||||
|
paddingBottom: 20,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
gap: 10,
|
||||||
|
}}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: 21,
|
||||||
|
lineHeight: 23,
|
||||||
|
color: '#fff',
|
||||||
|
}}>
|
||||||
|
Welcome to Bluesky!
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontWeight: '500',
|
||||||
|
fontSize: 16,
|
||||||
|
lineHeight: 18,
|
||||||
|
color: '#fff',
|
||||||
|
}}>
|
||||||
|
You're on the Discover algorithm. Swipe right to change your current
|
||||||
|
feed.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</Nudge>
|
||||||
|
)
|
||||||
|
}
|
||||||
+38
-35
@@ -29,6 +29,7 @@ import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState'
|
|||||||
import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed'
|
import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed'
|
||||||
import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
|
import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
|
||||||
import {HomeHeader} from '../com/home/HomeHeader'
|
import {HomeHeader} from '../com/home/HomeHeader'
|
||||||
|
import {HomeNewUserNudge} from '../com/home/HomeNewUserNudge'
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
|
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
|
||||||
export function HomeScreen(props: Props) {
|
export function HomeScreen(props: Props) {
|
||||||
@@ -173,7 +174,6 @@ function HomeScreenReady({
|
|||||||
},
|
},
|
||||||
[setMinimalShellMode],
|
[setMinimalShellMode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const renderTabBar = React.useCallback(
|
const renderTabBar = React.useCallback(
|
||||||
(props: RenderTabBarFnProps) => {
|
(props: RenderTabBarFnProps) => {
|
||||||
return (
|
return (
|
||||||
@@ -209,47 +209,50 @@ function HomeScreenReady({
|
|||||||
}, [preferences])
|
}, [preferences])
|
||||||
|
|
||||||
return hasSession ? (
|
return hasSession ? (
|
||||||
<Pager
|
<>
|
||||||
key={allFeeds.join(',')}
|
<HomeNewUserNudge />
|
||||||
ref={pagerRef}
|
<Pager
|
||||||
testID="homeScreen"
|
key={allFeeds.join(',')}
|
||||||
initialPage={selectedIndex}
|
ref={pagerRef}
|
||||||
onPageSelecting={onPageSelecting}
|
testID="homeScreen"
|
||||||
onPageSelected={onPageSelected}
|
initialPage={selectedIndex}
|
||||||
onPageScrollStateChanged={onPageScrollStateChanged}
|
onPageSelecting={onPageSelecting}
|
||||||
renderTabBar={renderTabBar}>
|
onPageSelected={onPageSelected}
|
||||||
{pinnedFeedInfos.length ? (
|
onPageScrollStateChanged={onPageScrollStateChanged}
|
||||||
pinnedFeedInfos.map(feedInfo => {
|
renderTabBar={renderTabBar}>
|
||||||
const feed = feedInfo.feedDescriptor
|
{pinnedFeedInfos.length ? (
|
||||||
if (feed === 'following') {
|
pinnedFeedInfos.map(feedInfo => {
|
||||||
|
const feed = feedInfo.feedDescriptor
|
||||||
|
if (feed === 'following') {
|
||||||
|
return (
|
||||||
|
<FeedPage
|
||||||
|
key={feed}
|
||||||
|
testID="followingFeedPage"
|
||||||
|
isPageFocused={selectedFeed === feed}
|
||||||
|
feed={feed}
|
||||||
|
feedParams={homeFeedParams}
|
||||||
|
renderEmptyState={renderFollowingEmptyState}
|
||||||
|
renderEndOfFeed={FollowingEndOfFeed}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const savedFeedConfig = feedInfo.savedFeed
|
||||||
return (
|
return (
|
||||||
<FeedPage
|
<FeedPage
|
||||||
key={feed}
|
key={feed}
|
||||||
testID="followingFeedPage"
|
testID="customFeedPage"
|
||||||
isPageFocused={selectedFeed === feed}
|
isPageFocused={selectedFeed === feed}
|
||||||
feed={feed}
|
feed={feed}
|
||||||
feedParams={homeFeedParams}
|
renderEmptyState={renderCustomFeedEmptyState}
|
||||||
renderEmptyState={renderFollowingEmptyState}
|
savedFeedConfig={savedFeedConfig}
|
||||||
renderEndOfFeed={FollowingEndOfFeed}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
const savedFeedConfig = feedInfo.savedFeed
|
) : (
|
||||||
return (
|
<NoFeedsPinned preferences={preferences} />
|
||||||
<FeedPage
|
)}
|
||||||
key={feed}
|
</Pager>
|
||||||
testID="customFeedPage"
|
</>
|
||||||
isPageFocused={selectedFeed === feed}
|
|
||||||
feed={feed}
|
|
||||||
renderEmptyState={renderCustomFeedEmptyState}
|
|
||||||
savedFeedConfig={savedFeedConfig}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<NoFeedsPinned preferences={preferences} />
|
|
||||||
)}
|
|
||||||
</Pager>
|
|
||||||
) : (
|
) : (
|
||||||
<Pager
|
<Pager
|
||||||
testID="homeScreen"
|
testID="homeScreen"
|
||||||
|
|||||||
Reference in New Issue
Block a user