import React from 'react'
import {Pressable, View} from 'react-native'
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
import {
AppBskyGraphDefs,
AppBskyGraphStarterpack,
AtUri,
type ModerationOpts,
} from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isAndroidWeb} from '#/lib/browser'
import {JOINED_THIS_WEEK} from '#/lib/constants'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {logEvent} from '#/lib/statsig/statsig'
import {createStarterPackGooglePlayUri} from '#/lib/strings/starter-pack'
import {isWeb} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useStarterPackQuery} from '#/state/queries/starter-packs'
import {
useActiveStarterPack,
useSetActiveStarterPack,
} from '#/state/shell/starter-pack'
import {LoggedOutScreenState} from '#/view/com/auth/LoggedOut'
import {formatCount} from '#/view/com/util/numeric/format'
import {Logo} from '#/view/icons/Logo'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import * as FeedCard from '#/components/FeedCard'
import {useRichText} from '#/components/hooks/useRichText'
import * as Layout from '#/components/Layout'
import {LinearGradientBackground} from '#/components/LinearGradientBackground'
import {ListMaybePlaceholder} from '#/components/Lists'
import {Default as ProfileCard} from '#/components/ProfileCard'
import * as Prompt from '#/components/Prompt'
import {RichText} from '#/components/RichText'
import {Text} from '#/components/Typography'
import * as bsky from '#/types/bsky'
const AnimatedPressable = Animated.createAnimatedComponent(Pressable)
interface AppClipMessage {
action: 'present' | 'store'
keyToStoreAs?: string
jsonToStore?: string
}
export function postAppClipMessage(message: AppClipMessage) {
// @ts-expect-error safari webview only
window.webkit.messageHandlers.onMessage.postMessage(JSON.stringify(message))
}
export function LandingScreen({
setScreenState,
}: {
setScreenState: (state: LoggedOutScreenState) => void
}) {
const moderationOpts = useModerationOpts()
const activeStarterPack = useActiveStarterPack()
const {
data: starterPack,
isError: isErrorStarterPack,
isFetching,
} = useStarterPackQuery({
uri: activeStarterPack?.uri,
})
const isValid =
starterPack &&
starterPack.list &&
AppBskyGraphDefs.validateStarterPackView(starterPack) &&
AppBskyGraphStarterpack.validateRecord(starterPack.record)
React.useEffect(() => {
if (isErrorStarterPack || (starterPack && !isValid)) {
setScreenState(LoggedOutScreenState.S_LoginOrCreateAccount)
}
}, [isErrorStarterPack, setScreenState, isValid, starterPack])
if (isFetching || !starterPack || !isValid || !moderationOpts) {
return
}
// Just for types, this cannot be hit
if (
!bsky.dangerousIsType(
starterPack.record,
AppBskyGraphStarterpack.isRecord,
)
) {
return null
}
return (
)
}
function LandingScreenLoaded({
starterPack,
starterPackRecord: record,
setScreenState,
// TODO apply this to profile card
moderationOpts,
}: {
starterPack: AppBskyGraphDefs.StarterPackView
starterPackRecord: AppBskyGraphStarterpack.Record
setScreenState: (state: LoggedOutScreenState) => void
moderationOpts: ModerationOpts
}) {
const {creator, listItemsSample, feeds} = starterPack
const {_, i18n} = useLingui()
const t = useTheme()
const activeStarterPack = useActiveStarterPack()
const setActiveStarterPack = useSetActiveStarterPack()
const {isTabletOrDesktop} = useWebMediaQueries()
const androidDialogControl = useDialogControl()
const [descriptionRt] = useRichText(record.description || '')
const [appClipOverlayVisible, setAppClipOverlayVisible] =
React.useState(false)
const listItemsCount = starterPack.list?.listItemCount ?? 0
const onContinue = () => {
setScreenState(LoggedOutScreenState.S_CreateAccount)
}
const onJoinPress = () => {
if (activeStarterPack?.isClip) {
setAppClipOverlayVisible(true)
postAppClipMessage({
action: 'present',
})
} else if (isAndroidWeb) {
androidDialogControl.open()
} else {
onContinue()
}
logEvent('starterPack:ctaPress', {
starterPack: starterPack.uri,
})
}
const onJoinWithoutPress = () => {
if (activeStarterPack?.isClip) {
setAppClipOverlayVisible(true)
postAppClipMessage({
action: 'present',
})
} else {
setActiveStarterPack(undefined)
setScreenState(LoggedOutScreenState.S_CreateAccount)
}
}
return (
{record.name}
Starter pack by {`@${creator.handle}`}
{record.description ? (
) : null}
{formatCount(i18n, JOINED_THIS_WEEK)} joined this week
{Boolean(listItemsSample?.length) && (
{listItemsCount <= 8 ? (
You'll follow these people right away
) : (
You'll follow these people and {listItemsCount - 8} others
)}
{starterPack.listItemsSample
?.filter(p => !p.subject.associated?.labeler)
.slice(0, 8)
.map((item, i) => (
))}
)}
{feeds?.length ? (
You'll stay updated with these feeds
{feeds?.map((feed, i) => (
))}
) : null}
Download Bluesky
The experience is better in the app. Download Bluesky now and we'll
pick back up where you left off.
{
const rkey = new AtUri(starterPack.uri).rkey
if (!rkey) return
const googlePlayUri = createStarterPackGooglePlayUri(
creator.handle,
rkey,
)
if (!googlePlayUri) return
window.location.href = googlePlayUri
}}
/>
{isWeb && (
)}
)
}
export function AppClipOverlay({
visible,
setIsVisible,
}: {
visible: boolean
setIsVisible: (visible: boolean) => void
}) {
if (!visible) return
return (
setIsVisible(false)}>
{/* Webkit needs this to have a zindex of 2? */}
Download Bluesky to get started!
We'll remember the starter pack you chose and use it when you create
an account in the app.
)
}