allow changing pinned feed order on web

zio/stable
Ansh Nanda 2023-05-16 23:38:34 -07:00
parent 99d66679b3
commit 3501fda015
6 changed files with 116 additions and 25 deletions

View File

@ -1,4 +1,4 @@
import {makeAutoObservable} from 'mobx'
import {makeAutoObservable, runInAction} from 'mobx'
import {AppBskyFeedGetSavedFeeds as GetSavedFeeds} from '@atproto/api'
import {RootStoreModel} from '../../root-store'
import {bundleAsync} from 'lib/async/bundle'
@ -103,6 +103,43 @@ export class SavedFeedsModel {
return this.pinned.find(f => f.data.uri === feed.data.uri) ? true : false
}
movePinnedItem(item: AlgoItemModel, direction: 'up' | 'down') {
if (this.pinned.length < 2) {
throw new Error('Array must have at least 2 items')
}
const index = this.pinned.indexOf(item)
if (index === -1) {
throw new Error('Item not found in array')
}
const len = this.pinned.length
runInAction(() => {
if (direction === 'up') {
if (index === 0) {
// Remove the item from the first place and put it at the end
this.pinned.push(this.pinned.shift()!)
} else {
// Swap the item with the one before it
const temp = this.pinned[index]
this.pinned[index] = this.pinned[index - 1]
this.pinned[index - 1] = temp
}
} else if (direction === 'down') {
if (index === len - 1) {
// Remove the item from the last place and put it at the start
this.pinned.unshift(this.pinned.pop()!)
} else {
// Swap the item with the one after it
const temp = this.pinned[index]
this.pinned[index] = this.pinned[index + 1]
this.pinned[index + 1] = temp
}
}
// this.pinned = [...this.pinned]
})
}
// public api
// =

View File

@ -19,6 +19,7 @@ import {useStores} from 'state/index'
import {HeartIconSolid} from 'lib/icons'
import {pluralize} from 'lib/strings/helpers'
import {AtUri} from '@atproto/api'
import {isWeb} from 'platform/detection'
const AlgoItem = observer(
({
@ -37,8 +38,9 @@ const AlgoItem = observer(
const navigation = useNavigation<NavigationProp>()
// TODO: this is pretty hacky, but it works for now
// causes issues on web
useFocusEffect(() => {
if (reloadOnFocus) {
if (reloadOnFocus && !isWeb) {
item.reload()
}
})

View File

@ -1,4 +1,4 @@
import React from 'react'
import React, {useMemo} from 'react'
import {Animated, StyleSheet} from 'react-native'
import {observer} from 'mobx-react-lite'
import {TabBar} from 'view/com/pager/TabBar'
@ -27,6 +27,14 @@ const FeedsTabBarDesktop = observer(
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
) => {
const store = useStores()
const items = useMemo(
() => [
'Following',
"What's hot",
...store.me.savedFeeds.listOfPinnedFeedNames,
],
[store.me.savedFeeds.listOfPinnedFeedNames],
)
const pal = usePalette('default')
const interp = useAnimatedValue(0)
@ -44,12 +52,13 @@ const FeedsTabBarDesktop = observer(
{translateY: Animated.multiply(interp, -100)},
],
}
return (
// @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
<Animated.View style={[pal.view, styles.tabBar, transform]}>
<TabBar
{...props}
items={['Following', "What's hot"]}
items={items}
indicatorPosition="bottom"
indicatorColor={pal.colors.link}
/>

View File

@ -8,6 +8,7 @@ import {faAngleUp} from '@fortawesome/free-solid-svg-icons/faAngleUp'
import {faArrowLeft} from '@fortawesome/free-solid-svg-icons/faArrowLeft'
import {faArrowRight} from '@fortawesome/free-solid-svg-icons/faArrowRight'
import {faArrowUp} from '@fortawesome/free-solid-svg-icons/faArrowUp'
import {faArrowDown} from '@fortawesome/free-solid-svg-icons/faArrowDown'
import {faArrowRightFromBracket} from '@fortawesome/free-solid-svg-icons/faArrowRightFromBracket'
import {faArrowUpFromBracket} from '@fortawesome/free-solid-svg-icons/faArrowUpFromBracket'
import {faArrowUpRightFromSquare} from '@fortawesome/free-solid-svg-icons/faArrowUpRightFromSquare'
@ -87,6 +88,7 @@ export function setup() {
faArrowLeft,
faArrowRight,
faArrowUp,
faArrowDown,
faArrowRightFromBracket,
faArrowUpFromBracket,
faArrowUpRightFromSquare,

View File

@ -38,8 +38,6 @@ export const CustomFeed = withAuthRequired(
return feed
}, [rootStore, uri])
console.log(currentFeed?.data.creator)
const _ListHeaderComponent = () => {
return (
<View style={[styles.headerContainer]}>

View File

@ -5,6 +5,7 @@ import {
View,
ActivityIndicator,
Pressable,
TouchableOpacity,
} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
@ -17,7 +18,7 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from 'view/com/util/ViewHeader'
import {CenteredView} from 'view/com/util/Views'
import {Text} from 'view/com/util/text/Text'
import {isDesktopWeb} from 'platform/detection'
import {isDesktopWeb, isWeb} from 'platform/detection'
import {s} from 'lib/styles'
import DraggableFlatList, {
ShadowDecorator,
@ -25,6 +26,7 @@ import DraggableFlatList, {
} from 'react-native-draggable-flatlist'
import {SavedFeedItem} from 'view/com/algos/SavedFeedItem'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {AlgoItemModel} from 'state/models/feeds/algo/algo-item'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PinnedFeeds'>
@ -73,7 +75,7 @@ export const PinnedFeeds = withAuthRequired(
<ViewHeader title="Arrange Pinned Feeds" showOnDesktop />
<DraggableFlatList
containerStyle={[!isDesktopWeb && s.flex1]}
data={savedFeeds.pinned}
data={[...savedFeeds.pinned]} // make a copy so this FlatList re-renders when pinned changes
keyExtractor={item => item.data.uri}
refreshing={savedFeeds.isRefreshing}
refreshControl={
@ -84,23 +86,7 @@ export const PinnedFeeds = withAuthRequired(
titleColor={pal.colors.text}
/>
}
renderItem={({item, drag}) => (
<ScaleDecorator>
<ShadowDecorator>
<Pressable
accessibilityRole="button"
onLongPress={drag}
style={styles.itemContainer}>
<FontAwesomeIcon
icon="bars"
size={20}
style={[styles.icon, pal.text]}
/>
<SavedFeedItem item={item} savedFeeds={savedFeeds} />
</Pressable>
</ShadowDecorator>
</ScaleDecorator>
)}
renderItem={({item, drag}) => <PinnedItem item={item} drag={drag} />}
initialNumToRender={10}
ListFooterComponent={_ListFooterComponent}
ListEmptyComponent={_ListEmptyComponent}
@ -114,6 +100,58 @@ export const PinnedFeeds = withAuthRequired(
}),
)
const PinnedItem = observer(
({item, drag}: {item: AlgoItemModel; drag: () => void}) => {
const pal = usePalette('default')
const rootStore = useStores()
const savedFeeds = useMemo(() => rootStore.me.savedFeeds, [rootStore])
return (
<ScaleDecorator>
<ShadowDecorator>
<Pressable
accessibilityRole="button"
onLongPress={drag}
style={styles.itemContainer}>
{isWeb ? (
<View style={styles.webArrowButtonsContainer}>
<TouchableOpacity
accessibilityRole="button"
onPress={() => {
savedFeeds.movePinnedItem(item, 'up')
}}>
<FontAwesomeIcon
icon="arrow-up"
size={20}
style={[styles.icon, pal.text, styles.webArrowUpButton]}
/>
</TouchableOpacity>
<TouchableOpacity
accessibilityRole="button"
onPress={() => {
savedFeeds.movePinnedItem(item, 'down')
}}>
<FontAwesomeIcon
icon="arrow-down"
size={20}
style={[styles.icon, pal.text]}
/>
</TouchableOpacity>
</View>
) : (
<FontAwesomeIcon
icon="bars"
size={20}
style={[styles.icon, pal.text]}
/>
)}
<SavedFeedItem item={item} savedFeeds={savedFeeds} />
</Pressable>
</ShadowDecorator>
</ScaleDecorator>
)
},
)
const styles = StyleSheet.create({
footer: {
paddingVertical: 20,
@ -135,4 +173,9 @@ const styles = StyleSheet.create({
borderTopWidth: 0,
},
icon: {marginRight: 10},
webArrowButtonsContainer: {
flexDirection: 'column',
justifyContent: 'space-around',
},
webArrowUpButton: {marginBottom: 10},
})