Channel page redesign (#528)
* channel page redesign changes * add constants. change publisher thumbnail resize mode.
This commit is contained in:
parent
7a7e96388b
commit
07f23ba927
11 changed files with 234 additions and 39 deletions
BIN
app/src/assets/default_avatar.jpg
Normal file
BIN
app/src/assets/default_avatar.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
app/src/assets/default_channel_cover.png
Normal file
BIN
app/src/assets/default_channel_cover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 354 KiB |
|
@ -4,6 +4,9 @@ const Constants = {
|
||||||
FIRST_RUN_PAGE_WALLET: "wallet",
|
FIRST_RUN_PAGE_WALLET: "wallet",
|
||||||
FIRST_RUN_PAGE_SKIP_ACCOUNT: "skip-account",
|
FIRST_RUN_PAGE_SKIP_ACCOUNT: "skip-account",
|
||||||
|
|
||||||
|
CONTENT_TAB: "content",
|
||||||
|
ABOUT_TAB: "about",
|
||||||
|
|
||||||
KEY_FIRST_RUN_EMAIL: "firstRunEmail",
|
KEY_FIRST_RUN_EMAIL: "firstRunEmail",
|
||||||
KEY_FIRST_RUN_PASSWORD: "firstRunPassword",
|
KEY_FIRST_RUN_PASSWORD: "firstRunPassword",
|
||||||
KEY_SHOULD_VERIFY_EMAIL: "shouldVerifyEmail",
|
KEY_SHOULD_VERIFY_EMAIL: "shouldVerifyEmail",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
doFetchClaimsByChannel,
|
doFetchClaimsByChannel,
|
||||||
doFetchClaimCountByChannel,
|
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
makeSelectClaimsInChannelForCurrentPageState,
|
makeSelectClaimsInChannelForCurrentPageState,
|
||||||
makeSelectFetchingChannelClaims,
|
makeSelectFetchingChannelClaims,
|
||||||
|
@ -21,7 +20,6 @@ const select = (state, props) => ({
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
|
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
|
||||||
fetchClaimCount: uri => dispatch(doFetchClaimCountByChannel(uri)),
|
|
||||||
popDrawerStack: () => dispatch(doPopDrawerStack())
|
popDrawerStack: () => dispatch(doPopDrawerStack())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,20 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ActivityIndicator, Text, View } from 'react-native';
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Dimensions,
|
||||||
|
Image,
|
||||||
|
ScrollView,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
View
|
||||||
|
} from 'react-native';
|
||||||
|
import { TabView, SceneMap } from 'react-native-tab-view';
|
||||||
import { navigateBack } from 'utils/helper';
|
import { navigateBack } from 'utils/helper';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
|
import Constants from 'constants';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
|
import Link from 'component/link';
|
||||||
import FileList from 'component/fileList';
|
import FileList from 'component/fileList';
|
||||||
import PageHeader from 'component/pageHeader';
|
import PageHeader from 'component/pageHeader';
|
||||||
import SubscribeButton from 'component/subscribeButton';
|
import SubscribeButton from 'component/subscribeButton';
|
||||||
|
@ -13,7 +24,8 @@ import channelPageStyle from 'styles/channelPage';
|
||||||
class ChannelPage extends React.PureComponent {
|
class ChannelPage extends React.PureComponent {
|
||||||
state = {
|
state = {
|
||||||
page: 1,
|
page: 1,
|
||||||
showPageButtons: false
|
showPageButtons: false,
|
||||||
|
activeTab: Constants.CONTENT_TAB
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -21,7 +33,6 @@ class ChannelPage extends React.PureComponent {
|
||||||
|
|
||||||
if (!claimsInChannel || !claimsInChannel.length) {
|
if (!claimsInChannel || !claimsInChannel.length) {
|
||||||
fetchClaims(uri, page || this.state.page);
|
fetchClaims(uri, page || this.state.page);
|
||||||
fetchClaimCount(uri);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,18 +54,8 @@ class ChannelPage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
renderContent = () => {
|
||||||
const {
|
const { fetching, claimsInChannel, totalPages, navigation } = this.props;
|
||||||
fetching,
|
|
||||||
claimsInChannel,
|
|
||||||
claim,
|
|
||||||
navigation,
|
|
||||||
totalPages,
|
|
||||||
uri,
|
|
||||||
drawerStack,
|
|
||||||
popDrawerStack
|
|
||||||
} = this.props;
|
|
||||||
const { name, permanent_url: permanentUrl } = claim;
|
|
||||||
|
|
||||||
let contentList;
|
let contentList;
|
||||||
if (fetching) {
|
if (fetching) {
|
||||||
|
@ -81,15 +82,9 @@ class ChannelPage extends React.PureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
let pageButtons;
|
||||||
<View style={channelPageStyle.container}>
|
if (totalPages > 1 && this.state.showPageButtons) {
|
||||||
<UriBar value={uri} navigation={navigation} />
|
pageButtons = (
|
||||||
<View style={channelPageStyle.channelHeader}>
|
|
||||||
<Text style={channelPageStyle.channelName}>{name}</Text>
|
|
||||||
<SubscribeButton style={channelPageStyle.subscribeButton} uri={uri} name={name} />
|
|
||||||
</View>
|
|
||||||
{contentList}
|
|
||||||
{(totalPages > 1) && this.state.showPageButtons &&
|
|
||||||
<View style={channelPageStyle.pageButtons}>
|
<View style={channelPageStyle.pageButtons}>
|
||||||
<View>
|
<View>
|
||||||
{(this.state.page > 1) && <Button
|
{(this.state.page > 1) && <Button
|
||||||
|
@ -103,7 +98,127 @@ class ChannelPage extends React.PureComponent {
|
||||||
text={"Next"}
|
text={"Next"}
|
||||||
disabled={!!fetching}
|
disabled={!!fetching}
|
||||||
onPress={this.handleNextPage} />}
|
onPress={this.handleNextPage} />}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={channelPageStyle.contentTab}>
|
||||||
|
{contentList}
|
||||||
|
{pageButtons}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAbout = () => {
|
||||||
|
const { claim } = this.props;
|
||||||
|
|
||||||
|
if (!claim) {
|
||||||
|
return (
|
||||||
|
<View style={channelPageStyle.aboutTab}>
|
||||||
|
<View style={channelPageStyle.busyContainer}>
|
||||||
|
<Text style={channelPageStyle.infoText}>No information to display.</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { cover, description, thumbnail, email, website_url, title } = claim.value;
|
||||||
|
return (
|
||||||
|
<View style={channelPageStyle.aboutTab}>
|
||||||
|
{(!website_url && !email && !description) &&
|
||||||
|
<View style={channelPageStyle.busyContainer}>
|
||||||
|
<Text style={channelPageStyle.infoText}>Nothing here yet. Please check back later.</Text>
|
||||||
</View>}
|
</View>}
|
||||||
|
|
||||||
|
{(website_url || email || description) &&
|
||||||
|
<ScrollView style={channelPageStyle.aboutScroll} contentContainerStyle={channelPageStyle.aboutScrollContent}>
|
||||||
|
{(website_url && website_url.trim().length > 0) &&
|
||||||
|
<View style={channelPageStyle.aboutItem}>
|
||||||
|
<Text style={channelPageStyle.aboutTitle}>Website</Text>
|
||||||
|
<Link style={channelPageStyle.aboutText} text={website_url} href={website_url} />
|
||||||
|
</View>}
|
||||||
|
|
||||||
|
{(email && email.trim().length > 0) &&
|
||||||
|
<View style={channelPageStyle.aboutItem}>
|
||||||
|
<Text style={channelPageStyle.aboutTitle}>Email</Text>
|
||||||
|
<Link style={channelPageStyle.aboutText} text={email} href={`mailto:${email}`} />
|
||||||
|
</View>}
|
||||||
|
|
||||||
|
{(description && description.trim().length > 0) &&
|
||||||
|
<View style={channelPageStyle.aboutItem}>
|
||||||
|
<Text style={channelPageStyle.aboutText}>{description}</Text>
|
||||||
|
</View>}
|
||||||
|
</ScrollView>}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
fetching,
|
||||||
|
claimsInChannel,
|
||||||
|
claim,
|
||||||
|
navigation,
|
||||||
|
totalPages,
|
||||||
|
uri,
|
||||||
|
drawerStack,
|
||||||
|
popDrawerStack
|
||||||
|
} = this.props;
|
||||||
|
const { name, permanent_url: permanentUrl } = claim;
|
||||||
|
|
||||||
|
let thumbnailUrl, coverUrl, title;
|
||||||
|
if (claim && claim.value) {
|
||||||
|
title = claim.value.title;
|
||||||
|
if (claim.value.cover) {
|
||||||
|
coverUrl = claim.value.cover.url;
|
||||||
|
}
|
||||||
|
if (claim.value.thumbnail) {
|
||||||
|
thumbnailUrl = claim.value.thumbnail.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={channelPageStyle.container}>
|
||||||
|
<UriBar value={uri} navigation={navigation} />
|
||||||
|
|
||||||
|
|
||||||
|
<View style={channelPageStyle.viewContainer}>
|
||||||
|
<View style={channelPageStyle.cover}>
|
||||||
|
<Image
|
||||||
|
style={channelPageStyle.coverImage}
|
||||||
|
resizeMode={'cover'}
|
||||||
|
source={(coverUrl && coverUrl.trim().length > 0) ? { uri: coverUrl } : require('../../assets/default_channel_cover.png')} />
|
||||||
|
|
||||||
|
<View style={channelPageStyle.channelHeader}>
|
||||||
|
<Text style={channelPageStyle.channelName}>{(title && title.trim().length > 0) ? title : name}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={channelPageStyle.avatarImageContainer}>
|
||||||
|
<Image
|
||||||
|
style={channelPageStyle.avatarImage}
|
||||||
|
resizeMode={'cover'}
|
||||||
|
source={(thumbnailUrl && thumbnailUrl.trim().length > 0) ? { uri: thumbnailUrl } : require('../../assets/default_avatar.jpg')} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<SubscribeButton style={channelPageStyle.subscribeButton} uri={uri} name={name} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={channelPageStyle.tabBar}>
|
||||||
|
<TouchableOpacity style={channelPageStyle.tab} onPress={() => this.setState({ activeTab: Constants.CONTENT_TAB })}>
|
||||||
|
<Text style={channelPageStyle.tabTitle}>CONTENT</Text>
|
||||||
|
{Constants.CONTENT_TAB === this.state.activeTab && <View style={channelPageStyle.activeTabHint} />}
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity style={channelPageStyle.tab} onPress={() => this.setState({ activeTab: Constants.ABOUT_TAB })}>
|
||||||
|
<Text style={channelPageStyle.tabTitle}>ABOUT</Text>
|
||||||
|
{Constants.ABOUT_TAB === this.state.activeTab && <View style={channelPageStyle.activeTabHint} />}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{Constants.CONTENT_TAB === this.state.activeTab && this.renderContent()}
|
||||||
|
{Constants.ABOUT_TAB === this.state.activeTab && this.renderAbout()}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableWithoutFeedback,
|
||||||
View,
|
View,
|
||||||
WebView
|
WebView
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
@ -444,7 +444,7 @@ class FilePage extends React.PureComponent {
|
||||||
const { height, channel_name: channelName, value } = claim;
|
const { height, channel_name: channelName, value } = claim;
|
||||||
const showActions = !this.state.fullscreenMode && !this.state.showImageViewer && !this.state.showWebView;
|
const showActions = !this.state.fullscreenMode && !this.state.showImageViewer && !this.state.showWebView;
|
||||||
const showFileActions = (completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes));
|
const showFileActions = (completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes));
|
||||||
const channelClaimId = value && value.publisherSignature && value.publisherSignature.certificateId;
|
const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id;
|
||||||
const canSendTip = this.state.tipAmount > 0;
|
const canSendTip = this.state.tipAmount > 0;
|
||||||
const fullChannelUri = channelClaimId && channelClaimId.trim().length > 0 ? `${channelName}#${channelClaimId}` : channelName;
|
const fullChannelUri = channelClaimId && channelClaimId.trim().length > 0 ? `${channelName}#${channelClaimId}` : channelName;
|
||||||
|
|
||||||
|
@ -582,10 +582,10 @@ class FilePage extends React.PureComponent {
|
||||||
ref={(ref) => { this.scrollView = ref; }}>
|
ref={(ref) => { this.scrollView = ref; }}>
|
||||||
<View style={filePageStyle.titleRow}>
|
<View style={filePageStyle.titleRow}>
|
||||||
<Text style={filePageStyle.title} selectable={true}>{title}</Text>
|
<Text style={filePageStyle.title} selectable={true}>{title}</Text>
|
||||||
<TouchableOpacity style={filePageStyle.descriptionToggle}
|
<TouchableWithoutFeedback style={filePageStyle.descriptionToggle}
|
||||||
onPress={() => this.setState({ showDescription: !this.state.showDescription })}>
|
onPress={() => this.setState({ showDescription: !this.state.showDescription })}>
|
||||||
<Icon name={this.state.showDescription ? "caret-up" : "caret-down"} size={24} />
|
<Icon name={this.state.showDescription ? "caret-up" : "caret-down"} size={24} />
|
||||||
</TouchableOpacity>
|
</TouchableWithoutFeedback>
|
||||||
</View>
|
</View>
|
||||||
{channelName &&
|
{channelName &&
|
||||||
<View style={filePageStyle.channelRow}>
|
<View style={filePageStyle.channelRow}>
|
||||||
|
|
|
@ -10,6 +10,10 @@ const channelPageStyle = StyleSheet.create({
|
||||||
content: {
|
content: {
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
|
viewContainer: {
|
||||||
|
flex: 1,
|
||||||
|
marginTop: 60
|
||||||
|
},
|
||||||
fileList: {
|
fileList: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
paddingTop: 30
|
paddingTop: 30
|
||||||
|
@ -53,22 +57,95 @@ const channelPageStyle = StyleSheet.create({
|
||||||
alignSelf: 'flex-end'
|
alignSelf: 'flex-end'
|
||||||
},
|
},
|
||||||
channelHeader: {
|
channelHeader: {
|
||||||
marginTop: 60,
|
position: 'absolute',
|
||||||
marginLeft: 16,
|
left: 120,
|
||||||
marginRight: 16
|
bottom: 4
|
||||||
},
|
},
|
||||||
channelName: {
|
channelName: {
|
||||||
fontFamily: 'Inter-UI-SemiBold',
|
color: Colors.White,
|
||||||
fontSize: 24,
|
fontFamily: 'Inter-UI-Regular',
|
||||||
marginTop: 16,
|
fontSize: 18
|
||||||
marginBottom: 8
|
|
||||||
},
|
},
|
||||||
subscribeButton: {
|
subscribeButton: {
|
||||||
alignSelf: 'flex-start',
|
alignSelf: 'flex-start',
|
||||||
backgroundColor: Colors.White,
|
backgroundColor: Colors.White,
|
||||||
paddingLeft: 16,
|
paddingLeft: 16,
|
||||||
paddingRight: 16
|
paddingRight: 16,
|
||||||
|
position: 'absolute',
|
||||||
|
right: 8,
|
||||||
|
bottom: -88,
|
||||||
|
zIndex: 100
|
||||||
},
|
},
|
||||||
|
cover: {
|
||||||
|
width: '100%',
|
||||||
|
height: '20%',
|
||||||
|
},
|
||||||
|
coverImage: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%'
|
||||||
|
},
|
||||||
|
tabBar: {
|
||||||
|
height: 45,
|
||||||
|
backgroundColor: Colors.LbryGreen,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'flex-end'
|
||||||
|
},
|
||||||
|
tabTitle: {
|
||||||
|
fontFamily: 'Inter-UI-SemiBold',
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.White,
|
||||||
|
},
|
||||||
|
tab: {
|
||||||
|
width: '30%',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
activeTabHint: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
backgroundColor: Colors.White,
|
||||||
|
height: 3,
|
||||||
|
width: '100%'
|
||||||
|
},
|
||||||
|
contentTab: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
aboutTab: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
aboutScroll: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
aboutItem: {
|
||||||
|
marginBottom: 24
|
||||||
|
},
|
||||||
|
aboutScrollContent: {
|
||||||
|
padding: 24
|
||||||
|
},
|
||||||
|
aboutTitle: {
|
||||||
|
fontFamily: 'Inter-UI-SemiBold',
|
||||||
|
fontSize: 16,
|
||||||
|
lineHeight: 24
|
||||||
|
},
|
||||||
|
aboutText: {
|
||||||
|
fontFamily: 'Inter-UI-Regular',
|
||||||
|
fontSize: 16,
|
||||||
|
lineHeight: 24
|
||||||
|
},
|
||||||
|
avatarImageContainer: {
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
borderRadius: 160,
|
||||||
|
position: 'absolute',
|
||||||
|
overflow: 'hidden',
|
||||||
|
left: 24,
|
||||||
|
bottom: -40,
|
||||||
|
zIndex: 100
|
||||||
|
},
|
||||||
|
avatarImage: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default channelPageStyle;
|
export default channelPageStyle;
|
||||||
|
|
|
@ -306,6 +306,7 @@ const filePageStyle = StyleSheet.create({
|
||||||
tagContainer: {
|
tagContainer: {
|
||||||
marginLeft: 12,
|
marginLeft: 12,
|
||||||
marginRight: 12,
|
marginRight: 12,
|
||||||
|
marginBottom: 16,
|
||||||
flexDirection: 'row'
|
flexDirection: 'row'
|
||||||
},
|
},
|
||||||
tagTitle: {
|
tagTitle: {
|
||||||
|
|
|
@ -84,6 +84,7 @@ ext {
|
||||||
buildToolsVersion = '{{ build_tools_version }}'
|
buildToolsVersion = '{{ build_tools_version }}'
|
||||||
minSdkVersion = {{ args.min_sdk_version }}
|
minSdkVersion = {{ args.min_sdk_version }}
|
||||||
targetSdkVersion = {{ android_api }}
|
targetSdkVersion = {{ android_api }}
|
||||||
|
supportLibVersion = '27.1.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
|
BIN
src/main/res/drawable-mdpi/src_assets_default_avatar.jpg
Normal file
BIN
src/main/res/drawable-mdpi/src_assets_default_avatar.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
src/main/res/drawable-mdpi/src_assets_default_channel_cover.png
Normal file
BIN
src/main/res/drawable-mdpi/src_assets_default_channel_cover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 354 KiB |
Loading…
Reference in a new issue