diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 001cdf8c33..94551e6efc 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -148,3 +148,110 @@ export const HITSLOP_10 = createHitslop(10)
export const HITSLOP_20 = createHitslop(20)
export const HITSLOP_30 = createHitslop(30)
export const BACK_HITSLOP = HITSLOP_30
+
+export const RECOMMENDED_FEEDS = [
+ {
+ did: 'did:plc:hsqwcidfez66lwm3gxhfv5in',
+ rkey: 'aaaf2pqeodmpy',
+ },
+ {
+ did: 'did:plc:gekdk2nd47gkk3utfz2xf7cn',
+ rkey: 'aaap4tbjcfe5y',
+ },
+ {
+ did: 'did:plc:5rw2on4i56btlcajojaxwcat',
+ rkey: 'aaao6g552b33o',
+ },
+ {
+ did: 'did:plc:jfhpnnst6flqway4eaeqzj2a',
+ rkey: 'for-science',
+ },
+ {
+ did: 'did:plc:7q4nnnxawajbfaq7to5dpbsy',
+ rkey: 'bsky-news',
+ },
+ {
+ did: 'did:plc:jcoy7v3a2t4rcfdh6i4kza25',
+ rkey: 'astro',
+ },
+ {
+ did: 'did:plc:tenurhgjptubkk5zf5qhi3og',
+ rkey: 'h-nba',
+ },
+ {
+ did: 'did:plc:vpkhqolt662uhesyj6nxm7ys',
+ rkey: 'devfeed',
+ },
+ {
+ did: 'did:plc:cndfx4udwgvpjaakvxvh7wm5',
+ rkey: 'flipboard-tech',
+ },
+ {
+ did: 'did:plc:w4xbfzo7kqfes5zb7r6qv3rw',
+ rkey: 'blacksky',
+ },
+ {
+ did: 'did:plc:lptjvw6ut224kwrj7ub3sqbe',
+ rkey: 'aaaotfjzjplna',
+ },
+ {
+ did: 'did:plc:gkvpokm7ec5j5yxls6xk4e3z',
+ rkey: 'formula-one',
+ },
+ {
+ did: 'did:plc:q6gjnaw2blty4crticxkmujt',
+ rkey: 'positivifeed',
+ },
+ {
+ did: 'did:plc:l72uci4styb4jucsgcrrj5ap',
+ rkey: 'aaao5dzfm36u4',
+ },
+ {
+ did: 'did:plc:k3jkadxv5kkjgs6boyon7m6n',
+ rkey: 'aaaavlyvqzst2',
+ },
+ {
+ did: 'did:plc:nkahctfdi6bxk72umytfwghw',
+ rkey: 'aaado2uvfsc6w',
+ },
+ {
+ did: 'did:plc:epihigio3d7un7u3gpqiy5gv',
+ rkey: 'aaaekwsc7zsvs',
+ },
+ {
+ did: 'did:plc:qiknc4t5rq7yngvz7g4aezq7',
+ rkey: 'aaaejxlobe474',
+ },
+ {
+ did: 'did:plc:mlq4aycufcuolr7ax6sezpc4',
+ rkey: 'aaaoudweck6uy',
+ },
+ {
+ did: 'did:plc:rcez5hcvq3vzlu5x7xrjyccg',
+ rkey: 'aaadzjxbcddzi',
+ },
+ {
+ did: 'did:plc:lnxbuzaenlwjrncx6sc4cfdr',
+ rkey: 'aaab2vesjtszc',
+ },
+ {
+ did: 'did:plc:x3cya3wkt4n6u4ihmvpsc5if',
+ rkey: 'aaacynbxwimok',
+ },
+ {
+ did: 'did:plc:abv47bjgzjgoh3yrygwoi36x',
+ rkey: 'aaagt6amuur5e',
+ },
+ {
+ did: 'did:plc:ffkgesg3jsv2j7aagkzrtcvt',
+ rkey: 'aaacjerk7gwek',
+ },
+ {
+ did: 'did:plc:geoqe3qls5mwezckxxsewys2',
+ rkey: 'aaai43yetqshu',
+ },
+ {
+ did: 'did:plc:2wqomm3tjqbgktbrfwgvrw34',
+ rkey: 'authors',
+ },
+]
diff --git a/src/state/models/feeds/custom-feed.ts b/src/state/models/feeds/custom-feed.ts
index 3c6d527556..2de4534e72 100644
--- a/src/state/models/feeds/custom-feed.ts
+++ b/src/state/models/feeds/custom-feed.ts
@@ -67,6 +67,19 @@ export class CustomFeedModel {
}
}
+ async pin() {
+ try {
+ await this.rootStore.preferences.addPinnedFeed(this.uri)
+ } catch (error) {
+ this.rootStore.log.error('Failed to pin feed', error)
+ } finally {
+ track('CustomFeed:Pin', {
+ name: this.data.displayName,
+ uri: this.uri,
+ })
+ }
+ }
+
async unsave() {
try {
await this.rootStore.preferences.removeSavedFeed(this.uri)
diff --git a/src/view/com/auth/Onboarding.tsx b/src/view/com/auth/Onboarding.tsx
index f43d5d93a3..2f0acfc473 100644
--- a/src/view/com/auth/Onboarding.tsx
+++ b/src/view/com/auth/Onboarding.tsx
@@ -6,7 +6,6 @@ import {s} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
import {useStores} from 'state/index'
import {useAnalytics} from 'lib/analytics/analytics'
-import {CenteredView} from '../util/Views'
import {Welcome} from './onboarding/Welcome'
import {RecommendedFeeds} from './onboarding/RecommendedFeeds'
@@ -24,17 +23,15 @@ export const Onboarding = observer(() => {
const skip = () => store.onboarding.skip()
return (
-
-
-
- {store.onboarding.step === 'Welcome' && (
-
- )}
- {store.onboarding.step === 'RecommendedFeeds' && (
-
- )}
-
-
-
+
+
+ {store.onboarding.step === 'Welcome' && (
+
+ )}
+ {store.onboarding.step === 'RecommendedFeeds' && (
+
+ )}
+
+
)
})
diff --git a/src/view/com/auth/onboarding/RecommendedFeeds.tsx b/src/view/com/auth/onboarding/RecommendedFeeds.tsx
index fa9d69f98c..f6b40b88de 100644
--- a/src/view/com/auth/onboarding/RecommendedFeeds.tsx
+++ b/src/view/com/auth/onboarding/RecommendedFeeds.tsx
@@ -9,113 +9,7 @@ import {useCustomFeed} from 'lib/hooks/useCustomFeed'
import {makeRecordUri} from 'lib/strings/url-helpers'
import {ViewHeader} from 'view/com/util/ViewHeader'
import {isDesktopWeb} from 'platform/detection'
-
-const TEMPORARY_RECOMMENDED_FEEDS = [
- {
- did: 'did:plc:hsqwcidfez66lwm3gxhfv5in',
- rkey: 'aaaf2pqeodmpy',
- },
- {
- did: 'did:plc:gekdk2nd47gkk3utfz2xf7cn',
- rkey: 'aaap4tbjcfe5y',
- },
- {
- did: 'did:plc:5rw2on4i56btlcajojaxwcat',
- rkey: 'aaao6g552b33o',
- },
- {
- did: 'did:plc:jfhpnnst6flqway4eaeqzj2a',
- rkey: 'for-science',
- },
- {
- did: 'did:plc:7q4nnnxawajbfaq7to5dpbsy',
- rkey: 'bsky-news',
- },
- {
- did: 'did:plc:jcoy7v3a2t4rcfdh6i4kza25',
- rkey: 'astro',
- },
- {
- did: 'did:plc:tenurhgjptubkk5zf5qhi3og',
- rkey: 'h-nba',
- },
- {
- did: 'did:plc:vpkhqolt662uhesyj6nxm7ys',
- rkey: 'devfeed',
- },
- {
- did: 'did:plc:cndfx4udwgvpjaakvxvh7wm5',
- rkey: 'flipboard-tech',
- },
- {
- did: 'did:plc:w4xbfzo7kqfes5zb7r6qv3rw',
- rkey: 'blacksky',
- },
- {
- did: 'did:plc:lptjvw6ut224kwrj7ub3sqbe',
- rkey: 'aaaotfjzjplna',
- },
- {
- did: 'did:plc:gkvpokm7ec5j5yxls6xk4e3z',
- rkey: 'formula-one',
- },
- {
- did: 'did:plc:q6gjnaw2blty4crticxkmujt',
- rkey: 'positivifeed',
- },
- {
- did: 'did:plc:l72uci4styb4jucsgcrrj5ap',
- rkey: 'aaao5dzfm36u4',
- },
- {
- did: 'did:plc:k3jkadxv5kkjgs6boyon7m6n',
- rkey: 'aaaavlyvqzst2',
- },
- {
- did: 'did:plc:nkahctfdi6bxk72umytfwghw',
- rkey: 'aaado2uvfsc6w',
- },
- {
- did: 'did:plc:epihigio3d7un7u3gpqiy5gv',
- rkey: 'aaaekwsc7zsvs',
- },
- {
- did: 'did:plc:qiknc4t5rq7yngvz7g4aezq7',
- rkey: 'aaaejxlobe474',
- },
- {
- did: 'did:plc:mlq4aycufcuolr7ax6sezpc4',
- rkey: 'aaaoudweck6uy',
- },
- {
- did: 'did:plc:rcez5hcvq3vzlu5x7xrjyccg',
- rkey: 'aaadzjxbcddzi',
- },
- {
- did: 'did:plc:lnxbuzaenlwjrncx6sc4cfdr',
- rkey: 'aaab2vesjtszc',
- },
- {
- did: 'did:plc:x3cya3wkt4n6u4ihmvpsc5if',
- rkey: 'aaacynbxwimok',
- },
- {
- did: 'did:plc:abv47bjgzjgoh3yrygwoi36x',
- rkey: 'aaagt6amuur5e',
- },
- {
- did: 'did:plc:ffkgesg3jsv2j7aagkzrtcvt',
- rkey: 'aaacjerk7gwek',
- },
- {
- did: 'did:plc:geoqe3qls5mwezckxxsewys2',
- rkey: 'aaai43yetqshu',
- },
- {
- did: 'did:plc:2wqomm3tjqbgktbrfwgvrw34',
- rkey: 'authors',
- },
-]
+import {RECOMMENDED_FEEDS} from 'lib/constants'
type Props = {
next: () => void
@@ -132,7 +26,7 @@ export const RecommendedFeeds = observer(({next}: Props) => {
}
keyExtractor={item => item.did + item.rkey}
style={{flex: 1}}
diff --git a/src/view/com/auth/onboarding/RecommendedFeeds.web.tsx b/src/view/com/auth/onboarding/RecommendedFeeds.web.tsx
new file mode 100644
index 0000000000..9038c7a5a6
--- /dev/null
+++ b/src/view/com/auth/onboarding/RecommendedFeeds.web.tsx
@@ -0,0 +1,214 @@
+import React from 'react'
+import {FlatList, StyleSheet, View} from 'react-native'
+import {observer} from 'mobx-react-lite'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {Text} from 'view/com/util/text/Text'
+import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout'
+import {UserAvatar} from 'view/com/util/UserAvatar'
+import {Button} from 'view/com/util/forms/Button'
+import * as Toast from 'view/com/util/Toast'
+import {usePalette} from 'lib/hooks/usePalette'
+import {useCustomFeed} from 'lib/hooks/useCustomFeed'
+import {makeRecordUri} from 'lib/strings/url-helpers'
+import {sanitizeHandle} from 'lib/strings/handles'
+import {HeartIcon} from 'lib/icons'
+import {RECOMMENDED_FEEDS} from 'lib/constants'
+
+type Props = {
+ next: () => void
+}
+export const RecommendedFeeds = observer(({next}: Props) => {
+ const pal = usePalette('default')
+
+ const title = (
+ <>
+ Choose your
+ Recomended
+ Feeds
+
+ Feeds are created by users to curate content. Choose some feeds that you
+ find interesting.
+
+
+
+
+ >
+ )
+
+ return (
+
+ }
+ keyExtractor={item => item.did + item.rkey}
+ style={{flex: 1}}
+ />
+
+ )
+})
+
+const Item = observer(({did, rkey}: {did: string; rkey: string}) => {
+ const pal = usePalette('default')
+ const uri = makeRecordUri(did, 'app.bsky.feed.generator', rkey)
+ const item = useCustomFeed(uri)
+ if (!item) return null
+ const onToggle = async () => {
+ if (item.isSaved) {
+ try {
+ await item.unsave()
+ } catch (e) {
+ Toast.show('There was an issue contacting your server')
+ console.error('Failed to unsave feed', {e})
+ }
+ } else {
+ try {
+ await item.save()
+ await item.pin()
+ } catch (e) {
+ Toast.show('There was an issue contacting your server')
+ console.error('Failed to pin feed', {e})
+ }
+ }
+ }
+ return (
+
+
+
+
+
+
+
+ {item.displayName}
+
+
+
+ by {sanitizeHandle(item.data.creator.handle, '@')}
+
+
+ {item.data.description ? (
+
+ {item.data.description}
+
+ ) : null}
+
+
+
+
+
+
+
+ {item.data.likeCount || 0}
+
+
+
+
+
+
+ )
+})
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ marginHorizontal: 16,
+ justifyContent: 'space-between',
+ },
+ title1: {
+ fontSize: 36,
+ fontWeight: '800',
+ textAlign: 'right',
+ },
+ title2: {
+ fontSize: 58,
+ fontWeight: '800',
+ textAlign: 'right',
+ },
+ description: {
+ maxWidth: 400,
+ marginTop: 10,
+ marginLeft: 'auto',
+ textAlign: 'right',
+ },
+})
diff --git a/src/view/com/auth/onboarding/Welcome.tsx b/src/view/com/auth/onboarding/Welcome.tsx
index a322e4d4fc..6f95c08539 100644
--- a/src/view/com/auth/onboarding/Welcome.tsx
+++ b/src/view/com/auth/onboarding/Welcome.tsx
@@ -41,8 +41,10 @@ export const Welcome = observer(({next, skip}: Props) => {
}}
/>
- Welcome to
- Bluesky
+
+ Welcome to{' '}
+ Bluesky
+
@@ -98,7 +100,7 @@ const styles = StyleSheet.create({
justifyContent: 'space-between',
},
title: {
- fontSize: 48,
+ fontSize: 42,
fontWeight: '800',
},
row: {
diff --git a/src/view/com/auth/onboarding/Welcome.web.tsx b/src/view/com/auth/onboarding/Welcome.web.tsx
new file mode 100644
index 0000000000..af3ca7074f
--- /dev/null
+++ b/src/view/com/auth/onboarding/Welcome.web.tsx
@@ -0,0 +1,123 @@
+import React from 'react'
+import {StyleSheet, View} from 'react-native'
+import {useMediaQuery} from 'react-responsive'
+import {Text} from 'view/com/util/text/Text'
+import {s} from 'lib/styles'
+import {usePalette} from 'lib/hooks/usePalette'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout'
+import {Button} from 'view/com/util/forms/Button'
+import {observer} from 'mobx-react-lite'
+
+type Props = {
+ next: () => void
+ skip: () => void
+}
+
+export const Welcome = observer(({next}: Props) => {
+ const pal = usePalette('default')
+ const horizontal = useMediaQuery({
+ query: '(min-width: 1230px)',
+ })
+ const title = (
+ <>
+
+ Welcome to
+
+
+ Bluesky
+
+ >
+ )
+ return (
+
+
+
+
+
+ Bluesky is public.
+
+
+ Your posts, likes, and blocks are public. Mutes are private.
+
+
+
+
+
+
+
+ Bluesky is open.
+
+
+ Never lose access to your followers and data.
+
+
+
+
+
+
+
+ Bluesky is flexible.
+
+
+ Choose the algorithms that power your experience with custom feeds.
+
+
+
+
+
+
+
+
+ )
+})
+
+const styles = StyleSheet.create({
+ row: {
+ flexDirection: 'row',
+ columnGap: 20,
+ alignItems: 'center',
+ marginVertical: 20,
+ },
+ rowText: {
+ flex: 1,
+ },
+ spacer: {
+ height: 20,
+ },
+})
diff --git a/src/view/com/util/layouts/TitleColumnLayout.tsx b/src/view/com/util/layouts/TitleColumnLayout.tsx
new file mode 100644
index 0000000000..3ca10868e0
--- /dev/null
+++ b/src/view/com/util/layouts/TitleColumnLayout.tsx
@@ -0,0 +1,62 @@
+import React from 'react'
+import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
+import {usePalette} from 'lib/hooks/usePalette'
+
+interface Props {
+ testID?: string
+ title: React.Component
+ horizontal: boolean
+ titleStyle?: StyleProp
+ contentStyle?: StyleProp
+}
+
+export function TitleColumnLayout({
+ testID,
+ title,
+ horizontal,
+ children,
+ titleStyle,
+ contentStyle,
+}: React.PropsWithChildren) {
+ const pal = usePalette('default')
+
+ const layoutStyles = horizontal ? styles2Column : styles1Column
+ return (
+
+
+ {title}
+
+ {children}
+
+ )
+}
+
+const styles2Column = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ height: '100%',
+ },
+ title: {
+ flex: 1,
+ paddingHorizontal: 40,
+ paddingBottom: 80,
+ justifyContent: 'center',
+ },
+ content: {
+ flex: 2,
+ paddingHorizontal: 40,
+ justifyContent: 'center',
+ },
+})
+
+const styles1Column = StyleSheet.create({
+ container: {},
+ title: {
+ paddingHorizontal: 40,
+ paddingVertical: 40,
+ },
+ content: {
+ paddingHorizontal: 40,
+ paddingVertical: 40,
+ },
+})
diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx
index 16ed17a5b8..9ad8007f6b 100644
--- a/src/view/shell/index.web.tsx
+++ b/src/view/shell/index.web.tsx
@@ -20,7 +20,6 @@ import {NavigationProp} from 'lib/routes/types'
const ShellInner = observer(() => {
const store = useStores()
const {isDesktop} = useWebMediaQueries()
-
const navigator = useNavigation()
useEffect(() => {
@@ -29,6 +28,8 @@ const ShellInner = observer(() => {
})
}, [navigator, store.shell])
+ const showSideNavs =
+ isDesktop && store.session.hasSession && !store.onboarding.isActive
return (
<>
@@ -36,7 +37,7 @@ const ShellInner = observer(() => {
- {isDesktop && store.session.hasSession && (
+ {showSideNavs && (
<>