Replace the FAB with a compose prompt at the top of the feed
This commit is contained in:
parent
39058cd36a
commit
8e3dc52536
5 changed files with 78 additions and 15 deletions
63
src/view/com/composer/Prompt.tsx
Normal file
63
src/view/com/composer/Prompt.tsx
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
|
||||||
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
|
import {colors} from '../../lib/styles'
|
||||||
|
import {useStores} from '../../../state'
|
||||||
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
|
|
||||||
|
export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) {
|
||||||
|
const store = useStores()
|
||||||
|
const onPressAvatar = () => {
|
||||||
|
store.nav.navigate(`/profile/${store.me.handle}`)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<TouchableOpacity style={styles.container} onPress={onPressCompose}>
|
||||||
|
<TouchableOpacity style={styles.avatar} onPress={onPressAvatar}>
|
||||||
|
<UserAvatar
|
||||||
|
size={50}
|
||||||
|
handle={store.me.handle || ''}
|
||||||
|
displayName={store.me.displayName}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<View style={styles.textContainer}>
|
||||||
|
<Text style={styles.text}>What's happening?</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.btn}>
|
||||||
|
<Text style={styles.btnText}>Post</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
borderRadius: 6,
|
||||||
|
margin: 2,
|
||||||
|
marginBottom: 0,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
paddingVertical: 10,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: colors.white,
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
width: 50,
|
||||||
|
},
|
||||||
|
textContainer: {
|
||||||
|
marginLeft: 10,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
color: colors.gray4,
|
||||||
|
fontSize: 17,
|
||||||
|
},
|
||||||
|
btn: {
|
||||||
|
backgroundColor: colors.gray1,
|
||||||
|
paddingVertical: 6,
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
borderRadius: 30,
|
||||||
|
},
|
||||||
|
btnText: {
|
||||||
|
color: colors.gray5,
|
||||||
|
},
|
||||||
|
})
|
|
@ -6,23 +6,34 @@ import {EmptyState} from '../util/EmptyState'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/ErrorMessage'
|
||||||
import {FeedModel, FeedItemModel} from '../../../state/models/feed-view'
|
import {FeedModel, FeedItemModel} from '../../../state/models/feed-view'
|
||||||
import {FeedItem} from './FeedItem'
|
import {FeedItem} from './FeedItem'
|
||||||
|
import {ComposePrompt} from '../composer/Prompt'
|
||||||
|
|
||||||
|
const COMPOSE_PROMPT_ITEM = {_reactKey: '__prompt__'}
|
||||||
|
|
||||||
export const Feed = observer(function Feed({
|
export const Feed = observer(function Feed({
|
||||||
feed,
|
feed,
|
||||||
style,
|
style,
|
||||||
scrollElRef,
|
scrollElRef,
|
||||||
|
onPressCompose,
|
||||||
onPressTryAgain,
|
onPressTryAgain,
|
||||||
}: {
|
}: {
|
||||||
feed: FeedModel
|
feed: FeedModel
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
scrollElRef?: MutableRefObject<FlatList<any> | null>
|
scrollElRef?: MutableRefObject<FlatList<any> | null>
|
||||||
|
onPressCompose?: () => void
|
||||||
onPressTryAgain?: () => void
|
onPressTryAgain?: () => void
|
||||||
}) {
|
}) {
|
||||||
// TODO optimize renderItem or FeedItem, we're getting this notice from RN: -prf
|
// TODO optimize renderItem or FeedItem, we're getting this notice from RN: -prf
|
||||||
// VirtualizedList: You have a large list that is slow to update - make sure your
|
// VirtualizedList: You have a large list that is slow to update - make sure your
|
||||||
// renderItem function renders components that follow React performance best practices
|
// renderItem function renders components that follow React performance best practices
|
||||||
// like PureComponent, shouldComponentUpdate, etc
|
// like PureComponent, shouldComponentUpdate, etc
|
||||||
const renderItem = ({item}: {item: FeedItemModel}) => <FeedItem item={item} />
|
const renderItem = ({item}: {item: FeedItemModel}) => {
|
||||||
|
if (item === COMPOSE_PROMPT_ITEM) {
|
||||||
|
return <ComposePrompt onPressCompose={onPressCompose} />
|
||||||
|
} else {
|
||||||
|
return <FeedItem item={item} />
|
||||||
|
}
|
||||||
|
}
|
||||||
const onRefresh = () => {
|
const onRefresh = () => {
|
||||||
feed.refresh().catch(err => console.error('Failed to refresh', err))
|
feed.refresh().catch(err => console.error('Failed to refresh', err))
|
||||||
}
|
}
|
||||||
|
@ -45,7 +56,7 @@ export const Feed = observer(function Feed({
|
||||||
{feed.hasContent && (
|
{feed.hasContent && (
|
||||||
<FlatList
|
<FlatList
|
||||||
ref={scrollElRef}
|
ref={scrollElRef}
|
||||||
data={feed.feed.slice()}
|
data={[COMPOSE_PROMPT_ITEM].concat(feed.feed.slice())}
|
||||||
keyExtractor={item => item._reactKey}
|
keyExtractor={item => item._reactKey}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
refreshing={feed.isRefreshing}
|
refreshing={feed.isRefreshing}
|
||||||
|
|
|
@ -65,7 +65,7 @@ export const Home = observer(function Home({
|
||||||
}
|
}
|
||||||
}, [visible, store])
|
}, [visible, store])
|
||||||
|
|
||||||
const onComposePress = () => {
|
const onPressCompose = () => {
|
||||||
store.shell.openComposer({onPost: onCreatePost})
|
store.shell.openComposer({onPost: onCreatePost})
|
||||||
}
|
}
|
||||||
const onCreatePost = () => {
|
const onCreatePost = () => {
|
||||||
|
@ -81,12 +81,12 @@ export const Home = observer(function Home({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={s.flex1}>
|
<View style={s.flex1}>
|
||||||
<ViewHeader title="Bluesky" subtitle="Private Beta" />
|
|
||||||
<Feed
|
<Feed
|
||||||
key="default"
|
key="default"
|
||||||
feed={defaultFeedView}
|
feed={defaultFeedView}
|
||||||
scrollElRef={scrollElRef}
|
scrollElRef={scrollElRef}
|
||||||
style={{flex: 1}}
|
style={{flex: 1}}
|
||||||
|
onPressCompose={onPressCompose}
|
||||||
onPressTryAgain={onPressTryAgain}
|
onPressTryAgain={onPressTryAgain}
|
||||||
/>
|
/>
|
||||||
{defaultFeedView.hasNewLatest ? (
|
{defaultFeedView.hasNewLatest ? (
|
||||||
|
@ -95,7 +95,6 @@ export const Home = observer(function Home({
|
||||||
<Text style={styles.loadLatestText}>Load new posts</Text>
|
<Text style={styles.loadLatestText}>Load new posts</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
<FAB icon="pen-nib" onPress={onComposePress} />
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, {useState, useEffect} from 'react'
|
import React, {useState, useEffect} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {ViewHeader} from '../com/util/ViewHeader'
|
import {ViewHeader} from '../com/util/ViewHeader'
|
||||||
import {FAB} from '../com/util/FloatingActionButton'
|
|
||||||
import {Feed} from '../com/notifications/Feed'
|
import {Feed} from '../com/notifications/Feed'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
import {NotificationsViewModel} from '../../state/models/notifications-view'
|
import {NotificationsViewModel} from '../../state/models/notifications-view'
|
||||||
|
@ -37,9 +36,6 @@ export const Notifications = ({navIdx, visible}: ScreenParams) => {
|
||||||
}
|
}
|
||||||
}, [visible, store])
|
}, [visible, store])
|
||||||
|
|
||||||
const onComposePress = () => {
|
|
||||||
store.shell.openComposer({})
|
|
||||||
}
|
|
||||||
const onPressTryAgain = () => {
|
const onPressTryAgain = () => {
|
||||||
notesView?.refresh()
|
notesView?.refresh()
|
||||||
}
|
}
|
||||||
|
@ -48,7 +44,6 @@ export const Notifications = ({navIdx, visible}: ScreenParams) => {
|
||||||
<View style={{flex: 1}}>
|
<View style={{flex: 1}}>
|
||||||
<ViewHeader title="Notifications" />
|
<ViewHeader title="Notifications" />
|
||||||
{notesView && <Feed view={notesView} onPressTryAgain={onPressTryAgain} />}
|
{notesView && <Feed view={notesView} onPressTryAgain={onPressTryAgain} />}
|
||||||
<FAB icon="pen-nib" onPress={onComposePress} />
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {StyleSheet, Text, View} from 'react-native'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {ViewSelector} from '../com/util/ViewSelector'
|
import {ViewSelector} from '../com/util/ViewSelector'
|
||||||
import {FAB} from '../com/util/FloatingActionButton'
|
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {ProfileUiModel, Sections} from '../../state/models/profile-ui'
|
import {ProfileUiModel, Sections} from '../../state/models/profile-ui'
|
||||||
import {MembershipItem} from '../../state/models/memberships-view'
|
import {MembershipItem} from '../../state/models/memberships-view'
|
||||||
|
@ -86,9 +85,6 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const onComposePress = () => {
|
|
||||||
store.shell.openComposer({})
|
|
||||||
}
|
|
||||||
|
|
||||||
// rendering
|
// rendering
|
||||||
// =
|
// =
|
||||||
|
@ -241,7 +237,6 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
||||||
) : (
|
) : (
|
||||||
renderHeader()
|
renderHeader()
|
||||||
)}
|
)}
|
||||||
<FAB icon="pen-nib" onPress={onComposePress} />
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue