diff --git a/src/component/AppNavigator.js b/src/component/AppNavigator.js index 4db09a6..71cf69a 100644 --- a/src/component/AppNavigator.js +++ b/src/component/AppNavigator.js @@ -6,7 +6,6 @@ import DrawerContent from 'component/drawerContent'; import FilePage from 'page/file'; import FirstRunScreen from 'page/firstRun'; import PublishPage from 'page/publish'; -import PublishesPage from 'page/publishes'; import RewardsPage from 'page/rewards'; import TagPage from 'page/tag'; import TrendingPage from 'page/trending'; @@ -163,12 +162,6 @@ const drawer = createDrawerNavigator( drawerIcon: ({ tintColor }) => <Icon name="upload" size={20} style={{ color: tintColor }} />, }, }, - Publishes: { - screen: PublishesPage, - navigationOptions: { - drawerIcon: ({ tintColor }) => <Icon name="cloud-upload-alt" size={20} style={{ color: tintColor }} />, - }, - }, Rewards: { screen: RewardsPage, navigationOptions: { diff --git a/src/component/claimList/index.js b/src/component/claimList/index.js index 2ab44d1..a621546 100644 --- a/src/component/claimList/index.js +++ b/src/component/claimList/index.js @@ -23,14 +23,22 @@ const select = (state, props) => { const perform = dispatch => ({ claimSearch: options => dispatch(doClaimSearch(Constants.DEFAULT_PAGE_SIZE, options)), - searchByTags: (tags, orderBy = Constants.DEFAULT_ORDER_BY, page = 1) => + searchByTags: (tags, orderBy = Constants.DEFAULT_ORDER_BY, page = 1, additionalOptions = {}) => dispatch( - doClaimSearchByTags(tags, Constants.DEFAULT_PAGE_SIZE, { - no_totals: true, - order_by: orderBy, - page, - not_tags: MATURE_TAGS, - }) + doClaimSearchByTags( + tags, + Constants.DEFAULT_PAGE_SIZE, + Object.assign( + {}, + { + no_totals: true, + order_by: orderBy, + page, + not_tags: MATURE_TAGS, + }, + additionalOptions + ) + ) ), }); diff --git a/src/component/claimList/view.js b/src/component/claimList/view.js index 54f6246..33c10ce 100644 --- a/src/component/claimList/view.js +++ b/src/component/claimList/view.js @@ -1,14 +1,16 @@ import React from 'react'; import NavigationActions from 'react-navigation'; -import { ActivityIndicator, FlatList, Text, View } from 'react-native'; +import { ActivityIndicator, FlatList, Text, TouchableOpacity, View } from 'react-native'; import { MATURE_TAGS, normalizeURI } from 'lbry-redux'; import _ from 'lodash'; import FileItem from 'component/fileItem'; import FileListItem from 'component/fileListItem'; +import Icon from 'react-native-vector-icons/FontAwesome5'; import Colors from 'styles/colors'; import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import claimListStyle from 'styles/claimList'; import discoverStyle from 'styles/discover'; +import moment from 'moment'; const horizontalLimit = 10; const softLimit = 500; @@ -30,6 +32,7 @@ class ClaimList extends React.PureComponent { orderBy = Constants.DEFAULT_ORDER_BY, searchByTags, tags, + time, } = this.props; if (channelIds || trendingForAll) { const options = { @@ -47,7 +50,11 @@ class ClaimList extends React.PureComponent { claimSearch(options); } else if (tags && tags.length > 0) { - searchByTags(tags, orderBy, this.state.currentPgae); + const additionalOptions = {}; + if (orderBy && orderBy[0] === Constants.ORDER_BY_EFFECTIVE_AMOUNT && Constants.TIME_ALL !== time) { + additionalOptions.release_time = this.getReleaseTimeOption(time); + } + searchByTags(tags, orderBy, this.state.currentPage, additionalOptions); } } @@ -59,12 +66,14 @@ class ClaimList extends React.PureComponent { tags: prevTags, channelIds: prevChannelIds, trendingForAll: prevTrendingForAll, + time: prevTime, } = this.props; - const { orderBy, tags, channelIds, trendingForAll } = nextProps; + const { orderBy, tags, channelIds, trendingForAll, time } = nextProps; if ( !_.isEqual(orderBy, prevOrderBy) || !_.isEqual(tags, prevTags) || !_.isEqual(channelIds, prevChannelIds) || + time !== prevTime || trendingForAll !== prevTrendingForAll ) { // reset to page 1 because the order, tags or channelIds changed @@ -90,15 +99,27 @@ class ClaimList extends React.PureComponent { claimSearch(options); } else if (tags && tags.length > 0) { this.setState({ subscriptionsView: false, trendingForAllView: false }); - searchByTags(tags, orderBy, this.state.currentPage); + const additionalOptions = {}; + if (orderBy && orderBy[0] === Constants.ORDER_BY_EFFECTIVE_AMOUNT && Constants.TIME_ALL !== time) { + additionalOptions.release_time = this.getReleaseTimeOption(time); + } + searchByTags(tags, orderBy, this.state.currentPage, additionalOptions); } }); } } + getReleaseTimeOption = time => { + return `>${Math.floor( + moment() + .subtract(1, time) + .unix() + )}`; + }; + handleVerticalEndReached = () => { // fetch more content - const { channelIds, claimSearch, claimSearchUris, orderBy, searchByTags, tags, uris } = this.props; + const { channelIds, claimSearch, claimSearchUris, orderBy, searchByTags, tags, time, uris } = this.props; const { subscriptionsView, trendingForAllView } = this.state; if ((claimSearchUris && claimSearchUris.length >= softLimit) || (uris && uris.length >= softLimit)) { // don't fetch more than the specified limit to be displayed @@ -119,17 +140,47 @@ class ClaimList extends React.PureComponent { claimSearch(options); } else { - searchByTags(tags, orderBy, this.state.currentPage); + const additionalOptions = {}; + if (orderBy && orderBy[0] === Constants.ORDER_BY_EFFECTIVE_AMOUNT && Constants.TIME_ALL !== time) { + additionalOptions.release_time = this.getReleaseTimeOption(time); + } + searchByTags(tags, orderBy, this.state.currentPage, additionalOptions); } }); }; + appendMorePlaceholder = items => { + items.push(Constants.MORE_PLACEHOLDER); + return items; + }; + + onMorePressed = () => { + const { navigation, tags } = this.props; + + // tags.length > 1 means this is the Trending list + if (tags.length === 1) { + navigation.navigate({ routeName: Constants.DRAWER_ROUTE_TAG, key: 'tagPage', params: { tag: tags[0] } }); + } else { + navigation.navigate({ routeName: Constants.FULL_ROUTE_NAME_TRENDING }); + } + }; + + renderMorePlaceholder = () => { + return ( + <TouchableOpacity style={discoverStyle.fileItemMore} onPress={this.onMorePressed}> + <Text style={discoverStyle.moreText}>more</Text> + <Icon style={discoverStyle.moreIcon} name={'angle-double-down'} color={Colors.White} size={16} /> + </TouchableOpacity> + ); + }; + render() { const { ListHeaderComponent, loading, claimSearchLoading, claimSearchUris, + morePlaceholder, navigation, orientation = Constants.ORIENTATION_VERTICAL, style, @@ -184,20 +235,24 @@ class ClaimList extends React.PureComponent { initialNumToRender={3} maxToRenderPerBatch={3} removeClippedSubviews - renderItem={({ item }) => ( - <FileItem - style={discoverStyle.fileItem} - mediaStyle={discoverStyle.fileItemMedia} - key={item} - uri={normalizeURI(item)} - navigation={navigation} - showDetails - compactView={false} - /> - )} + renderItem={({ item }) => { + return item === Constants.MORE_PLACEHOLDER ? ( + this.renderMorePlaceholder() + ) : ( + <FileItem + style={discoverStyle.fileItem} + mediaStyle={discoverStyle.fileItemMedia} + key={item} + uri={normalizeURI(item)} + navigation={navigation} + showDetails + compactView={false} + /> + ); + }} horizontal showsHorizontalScrollIndicator={false} - data={uris ? uris.slice(0, horizontalLimit) : []} + data={uris ? this.appendMorePlaceholder(uris.slice(0, horizontalLimit)) : []} keyExtractor={(item, index) => item} /> ); diff --git a/src/component/fileListItem/view.js b/src/component/fileListItem/view.js index 8301581..fa24157 100644 --- a/src/component/fileListItem/view.js +++ b/src/component/fileListItem/view.js @@ -58,7 +58,6 @@ class FileListItem extends React.PureComponent { onPress, navigation, thumbnail, - hideChannel, title, } = this.props; @@ -116,7 +115,7 @@ class FileListItem extends React.PureComponent { {this.formatTitle(title) || this.formatTitle(name)} </Text> )} - {channel && !hideChannel && ( + {channel && ( <Link style={fileListStyle.publisher} text={channel} diff --git a/src/component/modalPicker/view.js b/src/component/modalPicker/view.js index 12dbd7e..b548f1a 100644 --- a/src/component/modalPicker/view.js +++ b/src/component/modalPicker/view.js @@ -45,7 +45,7 @@ export default class ModalPicker extends React.PureComponent { style={modalPickerStyle.listItem} onPress={() => onItemSelected(item)} > - <Icon style={modalPickerStyle.itemIcon} name={item.icon} size={16} /> + {item.icon && <Icon style={modalPickerStyle.itemIcon} name={item.icon} size={16} />} <Text style={modalPickerStyle.itemLabel}>{item.label}</Text> {selectedItem && selectedItem.name === item.name && ( <Icon style={modalPickerStyle.itemSelected} name={'check'} color={Colors.LbryGreen} size={16} /> diff --git a/src/constants.js b/src/constants.js index a1b0e55..1550e92 100644 --- a/src/constants.js +++ b/src/constants.js @@ -2,6 +2,12 @@ const SORT_BY_NEW = 'new'; const SORT_BY_HOT = 'hot'; const SORT_BY_TOP = 'top'; +const TIME_DAY = 'day'; +const TIME_WEEK = 'week'; +const TIME_MONTH = 'month'; +const TIME_YEAR = 'year'; +const TIME_ALL = 'all'; + const Constants = { FIRST_RUN_PAGE_WELCOME: 'welcome', FIRST_RUN_PAGE_EMAIL_COLLECT: 'email-collect', @@ -60,7 +66,6 @@ const Constants = { DRAWER_ROUTE_SUBSCRIPTIONS: 'Subscriptions', DRAWER_ROUTE_MY_LBRY: 'Downloads', DRAWER_ROUTE_PUBLISH: 'Publish', - DRAWER_ROUTE_PUBLISHES: 'Publishes', DRAWER_ROUTE_REWARDS: 'Rewards', DRAWER_ROUTE_WALLET: 'Wallet', DRAWER_ROUTE_SETTINGS: 'Settings', @@ -90,18 +95,36 @@ const Constants = { SORT_BY_NEW, SORT_BY_TOP, + TIME_DAY, + TIME_WEEK, + TIME_MONTH, + TIME_YEAR, + TIME_ALL, + CLAIM_SEARCH_SORT_BY_ITEMS: [ { icon: 'fire-alt', name: SORT_BY_HOT, label: 'Hot content' }, { icon: 'certificate', name: SORT_BY_NEW, label: 'New content' }, { icon: 'chart-line', name: SORT_BY_TOP, label: 'Top content' }, ], + CLAIM_SEARCH_TIME_ITEMS: [ + { name: TIME_DAY, label: 'Past 24 hours' }, + { name: TIME_WEEK, label: 'Past week' }, + { name: TIME_MONTH, label: 'Past month' }, + { name: TIME_YEAR, label: 'Past year' }, + { name: TIME_ALL, label: 'All time' }, + ], + DEFAULT_ORDER_BY: ['trending_global', 'trending_mixed'], + ORDER_BY_EFFECTIVE_AMOUNT: 'effective_amount', + DEFAULT_PAGE_SIZE: 10, ALL_PLACEHOLDER: '_all', + MORE_PLACEHOLDER: '_more', + TRUE_STRING: 'true', }; @@ -112,10 +135,10 @@ export const DrawerRoutes = [ Constants.DRAWER_ROUTE_TRENDING, Constants.DRAWER_ROUTE_SUBSCRIPTIONS, Constants.DRAWER_ROUTE_MY_LBRY, + Constants.DRAWER_ROUTE_TAG, + Constants.DRAWER_ROUTE_PUBLISH, Constants.DRAWER_ROUTE_REWARDS, Constants.DRAWER_ROUTE_WALLET, - Constants.DRAWER_ROUTE_PUBLISH, - Constants.DRAWER_ROUTE_PUBLISHES, Constants.DRAWER_ROUTE_SETTINGS, Constants.DRAWER_ROUTE_ABOUT, Constants.DRAWER_ROUTE_SEARCH, diff --git a/src/page/discover/view.js b/src/page/discover/view.js index 4fcc569..875abc5 100644 --- a/src/page/discover/view.js +++ b/src/page/discover/view.js @@ -267,9 +267,10 @@ class DiscoverPage extends React.PureComponent { removeClippedSubviews renderItem={({ item, index, section }) => ( <ClaimList - key={item.join(',')} + key={item.sort().join(',')} orderBy={item.length > 1 ? Constants.DEFAULT_ORDER_BY : orderBy} tags={item} + morePlaceholder navigation={navigation} orientation={Constants.ORIENTATION_HORIZONTAL} /> @@ -280,7 +281,7 @@ class DiscoverPage extends React.PureComponent { {formatTagTitle(title)} </Text> <TouchableOpacity onPress={() => this.handleTagPress(title)}> - <Icon name={'ellipsis-v'} size={16} /> + <Icon name={'angle-double-down'} size={16} /> </TouchableOpacity> </View> )} diff --git a/src/page/publish/view.js b/src/page/publish/view.js index 3c94144..6b5fa9e 100644 --- a/src/page/publish/view.js +++ b/src/page/publish/view.js @@ -123,17 +123,6 @@ class PublishPage extends React.PureComponent { } } - onComponentFocused = () => { - const { pushDrawerStack, setPlayerVisible } = this.props; - - pushDrawerStack(); - setPlayerVisible(); - - NativeModules.Gallery.canUseCamera().then(canUseCamera => this.setState({ canUseCamera })); - NativeModules.Gallery.getThumbnailPath().then(thumbnailPath => this.setState({ thumbnailPath })); - NativeModules.Gallery.getVideos().then(videos => this.setState({ videos })); - }; - getNewUri(name, channel) { const { resolveUri } = this.props; // If they are midway through a channel creation, treat it as anonymous until it completes @@ -223,6 +212,17 @@ class PublishPage extends React.PureComponent { this.setState({ publishStarted: true }, () => publish(publishParams)); }; + onComponentFocused = () => { + const { pushDrawerStack, setPlayerVisible } = this.props; + + pushDrawerStack(); + setPlayerVisible(); + + NativeModules.Gallery.canUseCamera().then(canUseCamera => this.setState({ canUseCamera })); + NativeModules.Gallery.getThumbnailPath().then(thumbnailPath => this.setState({ thumbnailPath })); + NativeModules.Gallery.getVideos().then(videos => this.setState({ videos })); + }; + componentDidMount() { this.onComponentFocused(); } diff --git a/src/page/publishes/index.js b/src/page/publishes/index.js deleted file mode 100644 index 3989253..0000000 --- a/src/page/publishes/index.js +++ /dev/null @@ -1,21 +0,0 @@ -import { connect } from 'react-redux'; -import { doCheckPendingPublishes, selectMyClaimUrisWithoutChannels, selectIsFetchingClaimListMine } from 'lbry-redux'; -import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; -import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api -import PublishesPage from './view'; - -const select = state => ({ - uris: selectMyClaimUrisWithoutChannels(state), - fetching: selectIsFetchingClaimListMine(state), -}); - -const perform = dispatch => ({ - checkPendingPublishes: () => dispatch(doCheckPendingPublishes()), - pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_PUBLISHES)), - setPlayerVisible: () => dispatch(doSetPlayerVisible(false)), -}); - -export default connect( - select, - perform -)(PublishesPage); diff --git a/src/page/publishes/view.js b/src/page/publishes/view.js deleted file mode 100644 index 3a09d06..0000000 --- a/src/page/publishes/view.js +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; -import { ActivityIndicator, FlatList, Text, TouchableOpacity, View } from 'react-native'; -import Button from 'component/button'; -import Colors from 'styles/colors'; -import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api -import FileListItem from 'component/fileListItem'; -import FloatingWalletBalance from 'component/floatingWalletBalance'; -import UriBar from 'component/uriBar'; -import publishStyle from 'styles/publish'; -import { __ } from 'utils/helper'; - -class PublishesPage extends React.PureComponent { - didFocusListener; - - componentWillMount() { - const { navigation } = this.props; - this.didFocusListener = navigation.addListener('didFocus', this.onComponentFocused); - } - - componentWillUnmount() { - if (this.didFocusListener) { - this.didFocusListener.remove(); - } - } - - onComponentFocused = () => { - const { checkPendingPublishes, pushDrawerStack, setPlayerVisible } = this.props; - - pushDrawerStack(); - setPlayerVisible(); - checkPendingPublishes(); - }; - - render() { - const { fetching, navigation, uris } = this.props; - - return ( - <View style={publishStyle.container}> - <UriBar navigation={navigation} /> - {fetching && ( - <View style={publishStyle.centered}> - <ActivityIndicator size={'small'} color={Colors.LbryGreen} /> - </View> - )} - - {!fetching && (!uris || uris.length === 0) && ( - <View style={publishStyle.noPublishes}> - <Text style={publishStyle.noPublishText}> - {__('It looks like you have not published anything to LBRY yet.')} - </Text> - <Button - style={publishStyle.publishNowButton} - text={__('Publish something new')} - onPress={() => navigation.navigate({ routeName: Constants.DRAWER_ROUTE_PUBLISH })} - /> - </View> - )} - - {uris && uris.length > 0 && ( - <FlatList - style={publishStyle.publishesList} - contentContainerStyle={publishStyle.publishesScrollPadding} - initialNumToRender={8} - maxToRenderPerBatch={24} - removeClippedSubviews - renderItem={({ item }) => ( - <FileListItem hideChannel key={item} uri={item} style={publishStyle.listItem} navigation={navigation} /> - )} - data={uris} - keyExtractor={(item, index) => item} - /> - )} - - <FloatingWalletBalance navigation={navigation} /> - </View> - ); - } -} - -export default PublishesPage; diff --git a/src/page/tag/view.js b/src/page/tag/view.js index 8b685a6..843d4b9 100644 --- a/src/page/tag/view.js +++ b/src/page/tag/view.js @@ -19,8 +19,11 @@ class TagPage extends React.PureComponent { state = { tag: null, showSortPicker: false, + showTimePicker: false, orderBy: Constants.DEFAULT_ORDER_BY, + time: Constants.TIME_WEEK, currentSortByItem: Constants.CLAIM_SEARCH_SORT_BY_ITEMS[0], + currentTimeItem: Constants.CLAIM_SEARCH_TIME_ITEMS[1], }; didFocusListener; @@ -67,24 +70,34 @@ class TagPage extends React.PureComponent { break; case Constants.SORT_BY_TOP: - orderBy = ['effective_amount']; + orderBy = [Constants.ORDER_BY_EFFECTIVE_AMOUNT]; break; } this.setState({ currentSortByItem: item, orderBy, showSortPicker: false }); }; + handleTimeItemSelected = item => { + this.setState({ time: item.name }); + }; + render() { const { navigation } = this.props; - const { tag, currentSortByItem } = this.state; + const { tag, currentSortByItem, currentTimeItem, showSortPicker, showTimePicker } = this.state; return ( <View style={discoverStyle.container}> - <UriBar navigation={navigation} belowOverlay={this.state.showSortPicker} /> + <UriBar navigation={navigation} belowOverlay={showSortPicker || showTimePicker} /> <ClaimList ListHeaderComponent={ <View style={discoverStyle.tagTitleRow}> <Text style={discoverStyle.tagPageTitle}>{formatTagTitle(tag)}</Text> + {Constants.SORT_BY_TOP === currentSortByItem.name && ( + <TouchableOpacity style={discoverStyle.tagTime} onPress={() => this.setState({ showTimePicker: true })}> + <Text style={discoverStyle.tagSortText}>{currentTimeItem.label}</Text> + <Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} /> + </TouchableOpacity> + )} <TouchableOpacity style={discoverStyle.tagSortBy} onPress={() => this.setState({ showSortPicker: true })}> <Text style={discoverStyle.tagSortText}>{currentSortByItem.label.split(' ')[0]}</Text> <Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} /> @@ -93,12 +106,13 @@ class TagPage extends React.PureComponent { } style={discoverStyle.tagPageClaimList} orderBy={this.state.orderBy} + time={this.state.time} tags={[tag]} navigation={navigation} orientation={Constants.ORIENTATION_VERTICAL} /> - {!this.state.showSortPicker && <FloatingWalletBalance navigation={navigation} />} - {this.state.showSortPicker && ( + {!showSortPicker && !showTimePicker && <FloatingWalletBalance navigation={navigation} />} + {showSortPicker && ( <ModalPicker title={__('Sort content by')} onOverlayPress={() => this.setState({ showSortPicker: false })} @@ -107,6 +121,15 @@ class TagPage extends React.PureComponent { items={Constants.CLAIM_SEARCH_SORT_BY_ITEMS} /> )} + {showTimePicker && ( + <ModalPicker + title={__('Content from')} + onOverlayPress={() => this.setState({ showTimePicker: false })} + onItemSelected={this.handleTimeItemSelected} + selectedItem={this.state.currentTimeItem} + items={Constants.CLAIM_SEARCH_TIME_ITEMS} + /> + )} </View> ); } diff --git a/src/styles/discover.js b/src/styles/discover.js index d0ba91b..46d2301 100644 --- a/src/styles/discover.js +++ b/src/styles/discover.js @@ -85,6 +85,25 @@ const discoverStyle = StyleSheet.create({ width: fileItemWidth, marginRight: 12, }, + fileItemMore: { + alignSelf: 'flex-start', + backgroundColor: Colors.LbryGreen, + flexDirection: 'row', + width: fileItemWidth, + height: fileItemMediaHeight, + marginRight: 12, + alignItems: 'center', + justifyContent: 'center', + }, + moreText: { + fontFamily: 'Inter-UI-Regular', + color: Colors.White, + fontSize: 24, + }, + moreIcon: { + marginLeft: 12, + marginBottom: -4, + }, fileItemMedia: { width: fileItemMediaWidth, height: fileItemMediaHeight, @@ -224,6 +243,10 @@ const discoverStyle = StyleSheet.create({ alignItems: 'center', marginRight: 4, }, + tagTime: { + flexDirection: 'row', + alignItems: 'center', + }, tagSortText: { fontFamily: 'Inter-UI-Regular', fontSize: 14, diff --git a/src/styles/modalPicker.js b/src/styles/modalPicker.js index b412cba..41a615a 100644 --- a/src/styles/modalPicker.js +++ b/src/styles/modalPicker.js @@ -49,9 +49,10 @@ const modalPickerStyle = StyleSheet.create({ }, itemIcon: { marginLeft: 8, - marginRight: 12, + marginRight: 4, }, itemLabel: { + marginLeft: 8, alignSelf: 'flex-start', fontFamily: 'Inter-UI-Regular', fontSize: 16, diff --git a/src/styles/publish.js b/src/styles/publish.js index b2db23d..f324d4c 100644 --- a/src/styles/publish.js +++ b/src/styles/publish.js @@ -326,49 +326,6 @@ const publishStyle = StyleSheet.create({ fontSize: 14, marginLeft: 8, }, - centered: { - position: 'absolute', - left: 0, - right: 0, - top: 60, - bottom: 0, - alignItems: 'center', - justifyContent: 'center', - }, - noPublishes: { - position: 'absolute', - left: 0, - right: 0, - top: 60, - bottom: 0, - alignItems: 'center', - justifyContent: 'center', - padding: 16, - }, - noPublishText: { - fontFamily: 'Inter-UI-Regular', - fontSize: 16, - }, - publishNowButton: { - alignSelf: 'center', - backgroundColor: Colors.LbryGreen, - marginTop: 16, - }, - publishesList: { - flex: 1, - marginTop: 60, - }, - publishesScrollPadding: { - paddingBottom: 16, - }, - listItem: { - flex: 1, - flexDirection: 'row', - justifyContent: 'space-between', - marginTop: 8, - marginLeft: 8, - marginRight: 8, - }, }); export default publishStyle;