More prefs cleanup (#1882)
* Update useHomeTabs * Use queryClient to fetch * Replace useHomeTabs and useDesktopRightNavItems * Fix type error * Some cleanupzio/stable
parent
37a2204483
commit
c3edde8ac6
|
@ -1,51 +0,0 @@
|
||||||
import {useEffect, useState} from 'react'
|
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import isEqual from 'lodash.isequal'
|
|
||||||
import {AtUri} from '@atproto/api'
|
|
||||||
import {FeedSourceModel} from 'state/models/content/feed-source'
|
|
||||||
|
|
||||||
interface RightNavItem {
|
|
||||||
uri: string
|
|
||||||
href: string
|
|
||||||
hostname: string
|
|
||||||
collection: string
|
|
||||||
rkey: string
|
|
||||||
displayName: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useDesktopRightNavItems(uris: string[]): RightNavItem[] {
|
|
||||||
const store = useStores()
|
|
||||||
const [items, setItems] = useState<RightNavItem[]>([])
|
|
||||||
const [lastUris, setLastUris] = useState<string[]>([])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isEqual(uris, lastUris)) {
|
|
||||||
// no changes
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchFeedInfo() {
|
|
||||||
const models = uris
|
|
||||||
.slice(0, 25)
|
|
||||||
.map(uri => new FeedSourceModel(store, uri))
|
|
||||||
await Promise.all(models.map(m => m.setup()))
|
|
||||||
setItems(
|
|
||||||
models.map(model => {
|
|
||||||
const {hostname, collection, rkey} = new AtUri(model.uri)
|
|
||||||
return {
|
|
||||||
uri: model.uri,
|
|
||||||
href: model.href,
|
|
||||||
hostname,
|
|
||||||
collection,
|
|
||||||
rkey,
|
|
||||||
displayName: model.displayName,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
setLastUris(uris)
|
|
||||||
}
|
|
||||||
fetchFeedInfo()
|
|
||||||
}, [store, uris, lastUris, setLastUris, setItems])
|
|
||||||
|
|
||||||
return items
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
import {useEffect, useState} from 'react'
|
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import isEqual from 'lodash.isequal'
|
|
||||||
import {FeedSourceModel} from 'state/models/content/feed-source'
|
|
||||||
|
|
||||||
export function useHomeTabs(uris: string[]): string[] {
|
|
||||||
const store = useStores()
|
|
||||||
const [tabs, setTabs] = useState<string[]>(['Following'])
|
|
||||||
const [lastUris, setLastUris] = useState<string[]>([])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isEqual(uris, lastUris)) {
|
|
||||||
// no changes
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchFeedInfo() {
|
|
||||||
const models = uris
|
|
||||||
.slice(0, 25)
|
|
||||||
.map(uri => new FeedSourceModel(store, uri))
|
|
||||||
await Promise.all(models.map(m => m.setup()))
|
|
||||||
setTabs(['Following'].concat(models.map(f => f.displayName)))
|
|
||||||
setLastUris(uris)
|
|
||||||
}
|
|
||||||
fetchFeedInfo()
|
|
||||||
}, [store, uris, lastUris, setLastUris, setTabs])
|
|
||||||
|
|
||||||
return tabs
|
|
||||||
}
|
|
|
@ -61,7 +61,7 @@ export class FeedSourceModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
get isPinned() {
|
get isPinned() {
|
||||||
return this.rootStore.preferences.isPinnedFeed(this.uri)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
get isLiked() {
|
get isLiked() {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import {
|
||||||
BskyFeedViewPreference,
|
BskyFeedViewPreference,
|
||||||
BskyThreadViewPreference,
|
BskyThreadViewPreference,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import AwaitLock from 'await-lock'
|
|
||||||
import {isObj, hasProp} from 'lib/type-guards'
|
import {isObj, hasProp} from 'lib/type-guards'
|
||||||
import {RootStoreModel} from '../root-store'
|
import {RootStoreModel} from '../root-store'
|
||||||
import {ModerationOpts} from '@atproto/api'
|
import {ModerationOpts} from '@atproto/api'
|
||||||
|
@ -33,30 +32,17 @@ export class LabelPreferencesModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PreferencesModel {
|
export class PreferencesModel {
|
||||||
adultContentEnabled = false
|
|
||||||
contentLabels = new LabelPreferencesModel()
|
contentLabels = new LabelPreferencesModel()
|
||||||
savedFeeds: string[] = []
|
savedFeeds: string[] = []
|
||||||
pinnedFeeds: string[] = []
|
pinnedFeeds: string[] = []
|
||||||
birthDate: Date | undefined = undefined
|
|
||||||
homeFeed: FeedViewPreference = {
|
|
||||||
hideReplies: false,
|
|
||||||
hideRepliesByUnfollowed: false,
|
|
||||||
hideRepliesByLikeCount: 0,
|
|
||||||
hideReposts: false,
|
|
||||||
hideQuotePosts: false,
|
|
||||||
lab_mergeFeedEnabled: false, // experimental
|
|
||||||
}
|
|
||||||
thread: ThreadViewPreference = {
|
thread: ThreadViewPreference = {
|
||||||
sort: 'oldest',
|
sort: 'oldest',
|
||||||
prioritizeFollowedUsers: true,
|
prioritizeFollowedUsers: true,
|
||||||
lab_treeViewEnabled: false, // experimental
|
lab_treeViewEnabled: false, // experimental
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to linearize async modifications to state
|
|
||||||
lock = new AwaitLock()
|
|
||||||
|
|
||||||
constructor(public rootStore: RootStoreModel) {
|
constructor(public rootStore: RootStoreModel) {
|
||||||
makeAutoObservable(this, {lock: false}, {autoBind: true})
|
makeAutoObservable(this, {}, {autoBind: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize() {
|
serialize() {
|
||||||
|
@ -106,7 +92,7 @@ export class PreferencesModel {
|
||||||
get moderationOpts(): ModerationOpts {
|
get moderationOpts(): ModerationOpts {
|
||||||
return {
|
return {
|
||||||
userDid: this.rootStore.session.currentSession?.did || '',
|
userDid: this.rootStore.session.currentSession?.did || '',
|
||||||
adultContentEnabled: this.adultContentEnabled,
|
adultContentEnabled: false,
|
||||||
labels: {
|
labels: {
|
||||||
// TEMP translate old settings until this UI can be migrated -prf
|
// TEMP translate old settings until this UI can be migrated -prf
|
||||||
porn: tempfixLabelPref(this.contentLabels.nsfw),
|
porn: tempfixLabelPref(this.contentLabels.nsfw),
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
import React from 'react'
|
||||||
import {
|
import {
|
||||||
useQuery,
|
useQuery,
|
||||||
useInfiniteQuery,
|
useInfiniteQuery,
|
||||||
InfiniteData,
|
InfiniteData,
|
||||||
QueryKey,
|
QueryKey,
|
||||||
useMutation,
|
useMutation,
|
||||||
|
useQueryClient,
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
import {
|
import {
|
||||||
AtUri,
|
AtUri,
|
||||||
|
@ -13,16 +15,22 @@ import {
|
||||||
AppBskyUnspeccedGetPopularFeedGenerators,
|
AppBskyUnspeccedGetPopularFeedGenerators,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
|
|
||||||
|
import {router} from '#/routes'
|
||||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||||
|
|
||||||
type FeedSourceInfo =
|
export type FeedSourceInfo =
|
||||||
| {
|
| {
|
||||||
type: 'feed'
|
type: 'feed'
|
||||||
uri: string
|
uri: string
|
||||||
cid: string
|
route: {
|
||||||
href: string
|
href: string
|
||||||
|
name: string
|
||||||
|
params: Record<string, string>
|
||||||
|
}
|
||||||
|
cid: string
|
||||||
avatar: string | undefined
|
avatar: string | undefined
|
||||||
displayName: string
|
displayName: string
|
||||||
description: RichText
|
description: RichText
|
||||||
|
@ -34,8 +42,12 @@ type FeedSourceInfo =
|
||||||
| {
|
| {
|
||||||
type: 'list'
|
type: 'list'
|
||||||
uri: string
|
uri: string
|
||||||
cid: string
|
route: {
|
||||||
href: string
|
href: string
|
||||||
|
name: string
|
||||||
|
params: Record<string, string>
|
||||||
|
}
|
||||||
|
cid: string
|
||||||
avatar: string | undefined
|
avatar: string | undefined
|
||||||
displayName: string
|
displayName: string
|
||||||
description: RichText
|
description: RichText
|
||||||
|
@ -43,7 +55,7 @@ type FeedSourceInfo =
|
||||||
creatorHandle: string
|
creatorHandle: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useFeedSourceInfoQueryKey = ({uri}: {uri: string}) => [
|
export const feedSourceInfoQueryKey = ({uri}: {uri: string}) => [
|
||||||
'getFeedSourceInfo',
|
'getFeedSourceInfo',
|
||||||
uri,
|
uri,
|
||||||
]
|
]
|
||||||
|
@ -53,19 +65,24 @@ const feedSourceNSIDs = {
|
||||||
list: 'app.bsky.graph.list',
|
list: 'app.bsky.graph.list',
|
||||||
}
|
}
|
||||||
|
|
||||||
function hydrateFeedGenerator(
|
export function hydrateFeedGenerator(
|
||||||
view: AppBskyFeedDefs.GeneratorView,
|
view: AppBskyFeedDefs.GeneratorView,
|
||||||
): FeedSourceInfo {
|
): FeedSourceInfo {
|
||||||
const urip = new AtUri(view.uri)
|
const urip = new AtUri(view.uri)
|
||||||
const collection =
|
const collection =
|
||||||
urip.collection === 'app.bsky.feed.generator' ? 'feed' : 'lists'
|
urip.collection === 'app.bsky.feed.generator' ? 'feed' : 'lists'
|
||||||
const href = `/profile/${urip.hostname}/${collection}/${urip.rkey}`
|
const href = `/profile/${urip.hostname}/${collection}/${urip.rkey}`
|
||||||
|
const route = router.matchPath(href)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'feed',
|
type: 'feed',
|
||||||
uri: view.uri,
|
uri: view.uri,
|
||||||
cid: view.cid,
|
cid: view.cid,
|
||||||
|
route: {
|
||||||
href,
|
href,
|
||||||
|
name: route[0],
|
||||||
|
params: route[1],
|
||||||
|
},
|
||||||
avatar: view.avatar,
|
avatar: view.avatar,
|
||||||
displayName: view.displayName
|
displayName: view.displayName
|
||||||
? sanitizeDisplayName(view.displayName)
|
? sanitizeDisplayName(view.displayName)
|
||||||
|
@ -81,17 +98,22 @@ function hydrateFeedGenerator(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo {
|
export function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo {
|
||||||
const urip = new AtUri(view.uri)
|
const urip = new AtUri(view.uri)
|
||||||
const collection =
|
const collection =
|
||||||
urip.collection === 'app.bsky.feed.generator' ? 'feed' : 'lists'
|
urip.collection === 'app.bsky.feed.generator' ? 'feed' : 'lists'
|
||||||
const href = `/profile/${urip.hostname}/${collection}/${urip.rkey}`
|
const href = `/profile/${urip.hostname}/${collection}/${urip.rkey}`
|
||||||
|
const route = router.matchPath(href)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'list',
|
type: 'list',
|
||||||
uri: view.uri,
|
uri: view.uri,
|
||||||
cid: view.cid,
|
route: {
|
||||||
href,
|
href,
|
||||||
|
name: route[0],
|
||||||
|
params: route[1],
|
||||||
|
},
|
||||||
|
cid: view.cid,
|
||||||
avatar: view.avatar,
|
avatar: view.avatar,
|
||||||
description: new RichText({
|
description: new RichText({
|
||||||
text: view.description || '',
|
text: view.description || '',
|
||||||
|
@ -105,13 +127,17 @@ function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFeedTypeFromUri(uri: string) {
|
||||||
|
const {pathname} = new AtUri(uri)
|
||||||
|
return pathname.includes(feedSourceNSIDs.feed) ? 'feed' : 'list'
|
||||||
|
}
|
||||||
|
|
||||||
export function useFeedSourceInfoQuery({uri}: {uri: string}) {
|
export function useFeedSourceInfoQuery({uri}: {uri: string}) {
|
||||||
const {agent} = useSession()
|
const {agent} = useSession()
|
||||||
const {pathname} = new AtUri(uri)
|
const type = getFeedTypeFromUri(uri)
|
||||||
const type = pathname.includes(feedSourceNSIDs.feed) ? 'feed' : 'list'
|
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: useFeedSourceInfoQueryKey({uri}),
|
queryKey: feedSourceInfoQueryKey({uri}),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
let view: FeedSourceInfo
|
let view: FeedSourceInfo
|
||||||
|
|
||||||
|
@ -170,3 +196,87 @@ export function useSearchPopularFeedsMutation() {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FOLLOWING_FEED_STUB: FeedSourceInfo = {
|
||||||
|
type: 'feed',
|
||||||
|
displayName: 'Following',
|
||||||
|
uri: '',
|
||||||
|
route: {
|
||||||
|
href: '/',
|
||||||
|
name: 'Home',
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
cid: '',
|
||||||
|
avatar: '',
|
||||||
|
description: new RichText({text: ''}),
|
||||||
|
creatorDid: '',
|
||||||
|
creatorHandle: '',
|
||||||
|
likeCount: 0,
|
||||||
|
likeUri: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePinnedFeedsInfos(): FeedSourceInfo[] {
|
||||||
|
const {agent} = useSession()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const [tabs, setTabs] = React.useState<FeedSourceInfo[]>([
|
||||||
|
FOLLOWING_FEED_STUB,
|
||||||
|
])
|
||||||
|
const {data: preferences} = usePreferencesQuery()
|
||||||
|
const pinnedFeedsKey = JSON.stringify(preferences?.feeds?.pinned)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!preferences?.feeds?.pinned) return
|
||||||
|
const uris = preferences.feeds.pinned
|
||||||
|
|
||||||
|
async function fetchFeedInfo() {
|
||||||
|
const reqs = []
|
||||||
|
|
||||||
|
for (const uri of uris) {
|
||||||
|
const cached = queryClient.getQueryData<FeedSourceInfo>(
|
||||||
|
feedSourceInfoQueryKey({uri}),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
reqs.push(cached)
|
||||||
|
} else {
|
||||||
|
reqs.push(
|
||||||
|
queryClient.fetchQuery({
|
||||||
|
queryKey: feedSourceInfoQueryKey({uri}),
|
||||||
|
queryFn: async () => {
|
||||||
|
const type = getFeedTypeFromUri(uri)
|
||||||
|
|
||||||
|
if (type === 'feed') {
|
||||||
|
const res = await agent.app.bsky.feed.getFeedGenerator({
|
||||||
|
feed: uri,
|
||||||
|
})
|
||||||
|
return hydrateFeedGenerator(res.data.view)
|
||||||
|
} else {
|
||||||
|
const res = await agent.app.bsky.graph.getList({
|
||||||
|
list: uri,
|
||||||
|
limit: 1,
|
||||||
|
})
|
||||||
|
return hydrateList(res.data.list)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const views = await Promise.all(reqs)
|
||||||
|
|
||||||
|
setTabs([FOLLOWING_FEED_STUB].concat(views))
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchFeedInfo()
|
||||||
|
}, [
|
||||||
|
agent,
|
||||||
|
queryClient,
|
||||||
|
setTabs,
|
||||||
|
preferences?.feeds?.pinned,
|
||||||
|
// ensure we react to re-ordering
|
||||||
|
pinnedFeedsKey,
|
||||||
|
])
|
||||||
|
|
||||||
|
return tabs
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {
|
||||||
BskyPreferences,
|
BskyPreferences,
|
||||||
LabelPreference,
|
LabelPreference,
|
||||||
BskyThreadViewPreference,
|
BskyThreadViewPreference,
|
||||||
|
BskyFeedViewPreference,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
|
|
||||||
export type ConfigurableLabelGroup =
|
export type ConfigurableLabelGroup =
|
||||||
|
@ -29,7 +30,9 @@ export type UsePreferencesQueryResponse = Omit<
|
||||||
* we clean up the data in `usePreferencesQuery`.
|
* we clean up the data in `usePreferencesQuery`.
|
||||||
*/
|
*/
|
||||||
contentLabels: Record<ConfigurableLabelGroup, LabelPreference>
|
contentLabels: Record<ConfigurableLabelGroup, LabelPreference>
|
||||||
feedViewPrefs: BskyPreferences['feedViewPrefs']['home']
|
feedViewPrefs: BskyFeedViewPreference & {
|
||||||
|
lab_mergeFeedEnabled: boolean
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* User thread-view prefs, including newer fields that may not be typed yet.
|
* User thread-view prefs, including newer fields that may not be typed yet.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {BskyPreferences, LabelPreference} from '@atproto/api'
|
import {LabelPreference} from '@atproto/api'
|
||||||
import {StyleSheet, Pressable, View} from 'react-native'
|
import {StyleSheet, Pressable, View} from 'react-native'
|
||||||
import LinearGradient from 'react-native-linear-gradient'
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
|
@ -23,6 +23,7 @@ import {
|
||||||
usePreferencesSetAdultContentMutation,
|
usePreferencesSetAdultContentMutation,
|
||||||
ConfigurableLabelGroup,
|
ConfigurableLabelGroup,
|
||||||
CONFIGURABLE_LABEL_GROUPS,
|
CONFIGURABLE_LABEL_GROUPS,
|
||||||
|
UsePreferencesQueryResponse,
|
||||||
} from '#/state/queries/preferences'
|
} from '#/state/queries/preferences'
|
||||||
|
|
||||||
export const snapPoints = ['90%']
|
export const snapPoints = ['90%']
|
||||||
|
@ -175,7 +176,7 @@ const ContentLabelPref = observer(function ContentLabelPrefImpl({
|
||||||
labelGroup,
|
labelGroup,
|
||||||
disabled,
|
disabled,
|
||||||
}: {
|
}: {
|
||||||
preferences?: BskyPreferences
|
preferences?: UsePreferencesQueryResponse
|
||||||
labelGroup: ConfigurableLabelGroup
|
labelGroup: ConfigurableLabelGroup
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}) {
|
}) {
|
||||||
|
|
|
@ -4,13 +4,12 @@ import Animated from 'react-native-reanimated'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {TabBar} from 'view/com/pager/TabBar'
|
import {TabBar} from 'view/com/pager/TabBar'
|
||||||
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import {useHomeTabs} from 'lib/hooks/useHomeTabs'
|
|
||||||
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 {FeedsTabBar as FeedsTabBarMobile} from './FeedsTabBarMobile'
|
import {FeedsTabBar as FeedsTabBarMobile} from './FeedsTabBarMobile'
|
||||||
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||||
|
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
||||||
|
|
||||||
export const FeedsTabBar = observer(function FeedsTabBarImpl(
|
export const FeedsTabBar = observer(function FeedsTabBarImpl(
|
||||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
||||||
|
@ -28,11 +27,11 @@ export const FeedsTabBar = observer(function FeedsTabBarImpl(
|
||||||
const FeedsTabBarTablet = observer(function FeedsTabBarTabletImpl(
|
const FeedsTabBarTablet = observer(function FeedsTabBarTabletImpl(
|
||||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
||||||
) {
|
) {
|
||||||
const store = useStores()
|
const feeds = usePinnedFeedsInfos()
|
||||||
const items = useHomeTabs(store.preferences.pinnedFeeds)
|
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {headerMinimalShellTransform} = useMinimalShellMode()
|
const {headerMinimalShellTransform} = useMinimalShellMode()
|
||||||
const {headerHeight} = useShellLayout()
|
const {headerHeight} = useShellLayout()
|
||||||
|
const items = feeds.map(f => f.displayName)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
|
// @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
|
||||||
|
|
|
@ -3,8 +3,6 @@ import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {TabBar} from 'view/com/pager/TabBar'
|
import {TabBar} from 'view/com/pager/TabBar'
|
||||||
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import {useHomeTabs} from 'lib/hooks/useHomeTabs'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
|
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
|
@ -20,19 +18,20 @@ import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||||
import {useSetDrawerOpen} from '#/state/shell/drawer-open'
|
import {useSetDrawerOpen} from '#/state/shell/drawer-open'
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
|
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
||||||
|
|
||||||
export const FeedsTabBar = observer(function FeedsTabBarImpl(
|
export const FeedsTabBar = observer(function FeedsTabBarImpl(
|
||||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
||||||
) {
|
) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const store = useStores()
|
|
||||||
const {isSandbox} = useSession()
|
const {isSandbox} = useSession()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const setDrawerOpen = useSetDrawerOpen()
|
const setDrawerOpen = useSetDrawerOpen()
|
||||||
const items = useHomeTabs(store.preferences.pinnedFeeds)
|
const feeds = usePinnedFeedsInfos()
|
||||||
const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3)
|
const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3)
|
||||||
const {headerHeight} = useShellLayout()
|
const {headerHeight} = useShellLayout()
|
||||||
const {headerMinimalShellTransform} = useMinimalShellMode()
|
const {headerMinimalShellTransform} = useMinimalShellMode()
|
||||||
|
const items = feeds.map(f => f.displayName)
|
||||||
|
|
||||||
const onPressAvi = React.useCallback(() => {
|
const onPressAvi = React.useCallback(() => {
|
||||||
setDrawerOpen(true)
|
setDrawerOpen(true)
|
||||||
|
|
|
@ -508,7 +508,7 @@ function SavedFeed({feedUri}: {feedUri: string}) {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
testID={`saved-feed-${info.displayName}`}
|
testID={`saved-feed-${info.displayName}`}
|
||||||
href={info.href}
|
href={info.route.href}
|
||||||
style={[pal.border, styles.savedFeed, isMobile && styles.savedFeedMobile]}
|
style={[pal.border, styles.savedFeed, isMobile && styles.savedFeedMobile]}
|
||||||
hoverStyle={pal.viewLight}
|
hoverStyle={pal.viewLight}
|
||||||
accessibilityLabel={info.displayName}
|
accessibilityLabel={info.displayName}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {useFocusEffect} from '@react-navigation/native'
|
import {useFocusEffect} from '@react-navigation/native'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import isEqual from 'lodash.isequal'
|
|
||||||
import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types'
|
import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types'
|
||||||
import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
|
import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
|
||||||
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
|
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
|
||||||
|
@ -13,6 +12,7 @@ import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||||
import {useStores} from 'state/index'
|
import {useStores} from 'state/index'
|
||||||
import {FeedPage} from 'view/com/feeds/FeedPage'
|
import {FeedPage} from 'view/com/feeds/FeedPage'
|
||||||
import {useSetMinimalShellMode, useSetDrawerSwipeDisabled} from '#/state/shell'
|
import {useSetMinimalShellMode, useSetDrawerSwipeDisabled} from '#/state/shell'
|
||||||
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
|
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
|
||||||
export const HomeScreen = withAuthRequired(
|
export const HomeScreen = withAuthRequired(
|
||||||
|
@ -23,19 +23,15 @@ export const HomeScreen = withAuthRequired(
|
||||||
const pagerRef = React.useRef<PagerRef>(null)
|
const pagerRef = React.useRef<PagerRef>(null)
|
||||||
const [selectedPage, setSelectedPage] = React.useState(0)
|
const [selectedPage, setSelectedPage] = React.useState(0)
|
||||||
const [customFeeds, setCustomFeeds] = React.useState<FeedDescriptor[]>([])
|
const [customFeeds, setCustomFeeds] = React.useState<FeedDescriptor[]>([])
|
||||||
const [requestedCustomFeeds, setRequestedCustomFeeds] = React.useState<
|
const {data: preferences} = usePreferencesQuery()
|
||||||
string[]
|
|
||||||
>([])
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const pinned = store.preferences.pinnedFeeds
|
if (!preferences?.feeds?.pinned) return
|
||||||
|
|
||||||
if (isEqual(pinned, requestedCustomFeeds)) {
|
const pinned = preferences.feeds.pinned
|
||||||
// no changes
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const feeds: FeedDescriptor[] = []
|
const feeds: FeedDescriptor[] = []
|
||||||
|
|
||||||
for (const uri of pinned) {
|
for (const uri of pinned) {
|
||||||
if (uri.includes('app.bsky.feed.generator')) {
|
if (uri.includes('app.bsky.feed.generator')) {
|
||||||
feeds.push(`feedgen|${uri}`)
|
feeds.push(`feedgen|${uri}`)
|
||||||
|
@ -43,31 +39,20 @@ export const HomeScreen = withAuthRequired(
|
||||||
feeds.push(`list|${uri}`)
|
feeds.push(`list|${uri}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pagerRef.current?.setPage(0)
|
|
||||||
setCustomFeeds(feeds)
|
setCustomFeeds(feeds)
|
||||||
setRequestedCustomFeeds(pinned)
|
|
||||||
}, [
|
pagerRef.current?.setPage(0)
|
||||||
store,
|
}, [preferences?.feeds?.pinned, setCustomFeeds, pagerRef])
|
||||||
store.preferences.pinnedFeeds,
|
|
||||||
customFeeds,
|
|
||||||
setCustomFeeds,
|
|
||||||
pagerRef,
|
|
||||||
requestedCustomFeeds,
|
|
||||||
setRequestedCustomFeeds,
|
|
||||||
])
|
|
||||||
|
|
||||||
const homeFeedParams = React.useMemo<FeedParams>(() => {
|
const homeFeedParams = React.useMemo<FeedParams>(() => {
|
||||||
if (!store.preferences.homeFeed.lab_mergeFeedEnabled) {
|
if (!preferences) return {}
|
||||||
return {}
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
mergeFeedEnabled: true,
|
mergeFeedEnabled: preferences.feedViewPrefs.lab_mergeFeedEnabled,
|
||||||
mergeFeedSources: store.preferences.savedFeeds,
|
mergeFeedSources: preferences.feeds.saved,
|
||||||
}
|
}
|
||||||
}, [
|
}, [preferences])
|
||||||
store.preferences.homeFeed.lab_mergeFeedEnabled,
|
|
||||||
store.preferences.savedFeeds,
|
|
||||||
])
|
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
React.useCallback(() => {
|
React.useCallback(() => {
|
||||||
|
|
|
@ -2,16 +2,14 @@ import React from 'react'
|
||||||
import {View, StyleSheet} from 'react-native'
|
import {View, StyleSheet} from 'react-native'
|
||||||
import {useNavigationState} from '@react-navigation/native'
|
import {useNavigationState} from '@react-navigation/native'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useDesktopRightNavItems} from 'lib/hooks/useDesktopRightNavItems'
|
|
||||||
import {TextLink} from 'view/com/util/Link'
|
import {TextLink} from 'view/com/util/Link'
|
||||||
import {getCurrentRoute} from 'lib/routes/helpers'
|
import {getCurrentRoute} from 'lib/routes/helpers'
|
||||||
|
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
||||||
|
|
||||||
export const DesktopFeeds = observer(function DesktopFeeds() {
|
export const DesktopFeeds = observer(function DesktopFeeds() {
|
||||||
const store = useStores()
|
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const items = useDesktopRightNavItems(store.preferences.pinnedFeeds)
|
const feeds = usePinnedFeedsInfos()
|
||||||
|
|
||||||
const route = useNavigationState(state => {
|
const route = useNavigationState(state => {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
|
@ -23,22 +21,22 @@ export const DesktopFeeds = observer(function DesktopFeeds() {
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, pal.view, pal.border]}>
|
<View style={[styles.container, pal.view, pal.border]}>
|
||||||
<FeedItem href="/" title="Following" current={route.name === 'Home'} />
|
<FeedItem href="/" title="Following" current={route.name === 'Home'} />
|
||||||
{items.map(item => {
|
{feeds
|
||||||
|
.filter(f => f.displayName !== 'Following')
|
||||||
|
.map(feed => {
|
||||||
try {
|
try {
|
||||||
const params = route.params as Record<string, string>
|
const params = route.params as Record<string, string>
|
||||||
const routeName =
|
const routeName =
|
||||||
item.collection === 'app.bsky.feed.generator'
|
feed.type === 'feed' ? 'ProfileFeed' : 'ProfileList'
|
||||||
? 'ProfileFeed'
|
|
||||||
: 'ProfileList'
|
|
||||||
return (
|
return (
|
||||||
<FeedItem
|
<FeedItem
|
||||||
key={item.uri}
|
key={feed.uri}
|
||||||
href={item.href}
|
href={feed.route.href}
|
||||||
title={item.displayName}
|
title={feed.displayName}
|
||||||
current={
|
current={
|
||||||
route.name === routeName &&
|
route.name === routeName &&
|
||||||
params.name === item.hostname &&
|
params.name === feed.route.params.name &&
|
||||||
params.rkey === item.rkey
|
params.rkey === feed.route.params.rkey
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue