diff options
-rw-r--r-- | src/state/models/feeds/algo/saved.ts | 39 | ||||
-rw-r--r-- | src/view/com/algos/AlgoItem.tsx | 4 | ||||
-rw-r--r-- | src/view/com/pager/FeedsTabBar.web.tsx | 13 | ||||
-rw-r--r-- | src/view/index.ts | 2 | ||||
-rw-r--r-- | src/view/screens/CustomFeed.tsx | 2 | ||||
-rw-r--r-- | src/view/screens/PinnedFeeds.tsx | 81 |
6 files changed, 116 insertions, 25 deletions
diff --git a/src/state/models/feeds/algo/saved.ts b/src/state/models/feeds/algo/saved.ts index 97d75820d..cb2015ccb 100644 --- a/src/state/models/feeds/algo/saved.ts +++ b/src/state/models/feeds/algo/saved.ts @@ -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 // = diff --git a/src/view/com/algos/AlgoItem.tsx b/src/view/com/algos/AlgoItem.tsx index f2c36d7e9..56ee6d1d2 100644 --- a/src/view/com/algos/AlgoItem.tsx +++ b/src/view/com/algos/AlgoItem.tsx @@ -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() } }) diff --git a/src/view/com/pager/FeedsTabBar.web.tsx b/src/view/com/pager/FeedsTabBar.web.tsx index 0fc1b7310..a5e596143 100644 --- a/src/view/com/pager/FeedsTabBar.web.tsx +++ b/src/view/com/pager/FeedsTabBar.web.tsx @@ -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} /> diff --git a/src/view/index.ts b/src/view/index.ts index 253735e81..9c8d023dd 100644 --- a/src/view/index.ts +++ b/src/view/index.ts @@ -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, diff --git a/src/view/screens/CustomFeed.tsx b/src/view/screens/CustomFeed.tsx index 5644c16e0..63cbfcca8 100644 --- a/src/view/screens/CustomFeed.tsx +++ b/src/view/screens/CustomFeed.tsx @@ -38,8 +38,6 @@ export const CustomFeed = withAuthRequired( return feed }, [rootStore, uri]) - console.log(currentFeed?.data.creator) - const _ListHeaderComponent = () => { return ( <View style={[styles.headerContainer]}> diff --git a/src/view/screens/PinnedFeeds.tsx b/src/view/screens/PinnedFeeds.tsx index f87f8d284..ac901ba71 100644 --- a/src/view/screens/PinnedFeeds.tsx +++ b/src/view/screens/PinnedFeeds.tsx @@ -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}, }) |