implement my LBRY downloads page #273
15 changed files with 395 additions and 133 deletions
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import AboutPage from '../page/about';
|
||||
import DiscoverPage from '../page/discover';
|
||||
import DownloadsPage from '../page/downloads';
|
||||
import FilePage from '../page/file';
|
||||
import FirstRunScreen from '../page/firstRun';
|
||||
import RewardsPage from '../page/rewards';
|
||||
|
@ -40,8 +41,10 @@ import {
|
|||
} from 'lbryinc';
|
||||
import { makeSelectClientSetting } from '../redux/selectors/settings';
|
||||
import { decode as atob } from 'base-64';
|
||||
import NavigationButton from '../component/navigationButton';
|
||||
import Colors from '../styles/colors';
|
||||
import Constants from '../constants';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import NavigationButton from '../component/navigationButton';
|
||||
import discoverStyle from '../styles/discover';
|
||||
import searchStyle from '../styles/search';
|
||||
import SearchRightHeaderIcon from '../component/searchRightHeaderIcon';
|
||||
|
@ -88,6 +91,26 @@ const trendingStack = StackNavigator({
|
|||
}
|
||||
});
|
||||
|
||||
const myLbryStack = StackNavigator({
|
||||
Downloads: {
|
||||
screen: DownloadsPage,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
title: 'My LBRY',
|
||||
headerLeft: menuNavigationButton(navigation),
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const rewardsStack = StackNavigator({
|
||||
Rewards: {
|
||||
screen: RewardsPage,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
title: 'Rewards',
|
||||
headerLeft: menuNavigationButton(navigation),
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const walletStack = StackNavigator({
|
||||
Wallet: {
|
||||
screen: WalletPage,
|
||||
|
@ -107,26 +130,36 @@ const walletStack = StackNavigator({
|
|||
headerMode: 'screen'
|
||||
});
|
||||
|
||||
const rewardsStack = StackNavigator({
|
||||
Rewards: {
|
||||
screen: RewardsPage,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
title: 'Rewards',
|
||||
headerLeft: menuNavigationButton(navigation),
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const drawer = DrawerNavigator({
|
||||
DiscoverStack: { screen: discoverStack },
|
||||
TrendingStack: { screen: trendingStack },
|
||||
WalletStack: { screen: walletStack },
|
||||
Rewards: { screen: rewardsStack },
|
||||
Settings: { screen: SettingsPage, navigationOptions: { drawerLockMode: 'locked-closed' } },
|
||||
About: { screen: AboutPage, navigationOptions: { drawerLockMode: 'locked-closed' } }
|
||||
DiscoverStack: { screen: discoverStack, navigationOptions: {
|
||||
drawerIcon: ({ tintColor }) => <Icon name="compass" size={20} style={{ color: tintColor }} />
|
||||
}},
|
||||
TrendingStack: { screen: trendingStack, navigationOptions: {
|
||||
drawerIcon: ({ tintColor }) => <Icon name="fire" size={20} style={{ color: tintColor }} />
|
||||
}},
|
||||
MyLBRYStack: { screen: myLbryStack, navigationOptions: {
|
||||
drawerIcon: ({ tintColor }) => <Icon name="folder" size={20} style={{ color: tintColor }} />
|
||||
}},
|
||||
Rewards: { screen: rewardsStack, navigationOptions: {
|
||||
drawerIcon: ({ tintColor }) => <Icon name="trophy" size={20} style={{ color: tintColor }} />
|
||||
}},
|
||||
WalletStack: { screen: walletStack, navigationOptions: {
|
||||
drawerIcon: ({ tintColor }) => <Icon name="wallet" size={20} style={{ color: tintColor }} />
|
||||
}},
|
||||
Settings: { screen: SettingsPage, navigationOptions: {
|
||||
drawerLockMode: 'locked-closed',
|
||||
drawerIcon: ({ tintColor }) => <Icon name="cog" size={20} style={{ color: tintColor }} />
|
||||
}},
|
||||
About: { screen: AboutPage, navigationOptions: {
|
||||
drawerLockMode: 'locked-closed',
|
||||
drawerIcon: ({ tintColor }) => <Icon name="info" size={20} style={{ color: tintColor }} />
|
||||
}}
|
||||
}, {
|
||||
drawerWidth: 300,
|
||||
headerMode: 'none'
|
||||
headerMode: 'none',
|
||||
contentOptions: {
|
||||
activeTintColor: Colors.LbryGreen
|
||||
}
|
||||
});
|
||||
|
||||
export const AppNavigator = new StackNavigator({
|
||||
|
|
|
@ -7,10 +7,11 @@ import {
|
|||
makeSelectIsUriResolving,
|
||||
} from 'lbry-redux';
|
||||
import { selectShowNsfw } from '../../redux/selectors/settings';
|
||||
import SearchResultItem from './view';
|
||||
import FileListItem from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
claim: makeSelectClaimForUri(props.uri)(state),
|
||||
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||
isDownloaded: !!makeSelectFileInfoForUri(props.uri)(state),
|
||||
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
|
||||
|
@ -21,4 +22,4 @@ const perform = dispatch => ({
|
|||
resolveUri: uri => dispatch(doResolveUri(uri))
|
||||
});
|
||||
|
||||
export default connect(select, perform)(SearchResultItem);
|
||||
export default connect(select, perform)(FileListItem);
|
113
app/src/component/fileListItem/view.js
Normal file
113
app/src/component/fileListItem/view.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
import React from 'react';
|
||||
import { normalizeURI, parseURI } from 'lbry-redux';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Platform,
|
||||
ProgressBarAndroid,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Colors from '../../styles/colors';
|
||||
import FileItemMedia from '../fileItemMedia';
|
||||
import Link from '../../component/link';
|
||||
import NsfwOverlay from '../../component/nsfwOverlay';
|
||||
import fileListStyle from '../../styles/fileList';
|
||||
|
||||
class FileListItem extends React.PureComponent {
|
||||
getStorageForFileInfo = (fileInfo) => {
|
||||
if (!fileInfo.completed) {
|
||||
const written = this.formatBytes(fileInfo.written_bytes);
|
||||
const total = this.formatBytes(fileInfo.total_bytes);
|
||||
return `(${written} / ${total})`;
|
||||
}
|
||||
|
||||
return this.formatBytes(fileInfo.written_bytes);
|
||||
}
|
||||
|
||||
formatBytes = (bytes) => {
|
||||
if (bytes < 1048576) { // < 1MB
|
||||
const value = (bytes / 1024.0).toFixed(2);
|
||||
return `${value} KB`;
|
||||
}
|
||||
|
||||
if (bytes < 1073741824) { // < 1GB
|
||||
const value = (bytes / (1024.0 * 1024.0)).toFixed(2);
|
||||
return `${value} MB`;
|
||||
}
|
||||
|
||||
const value = (bytes / (1024.0 * 1024.0 * 1024.0)).toFixed(2);
|
||||
return `${value} GB`;
|
||||
}
|
||||
|
||||
getDownloadProgress = (fileInfo) => {
|
||||
return Math.ceil((fileInfo.written_bytes / fileInfo.total_bytes) * 100);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
claim,
|
||||
fileInfo,
|
||||
metadata,
|
||||
isResolvingUri,
|
||||
isDownloaded,
|
||||
style,
|
||||
onPress,
|
||||
navigation
|
||||
} = this.props;
|
||||
|
||||
const uri = normalizeURI(this.props.uri);
|
||||
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||
const isResolving = !fileInfo && isResolvingUri;
|
||||
const title = fileInfo ? fileInfo.metadata.title : metadata && metadata.title ? metadata.title : parseURI(uri).contentName;
|
||||
|
||||
let name;
|
||||
let channel;
|
||||
if (claim) {
|
||||
name = claim.name;
|
||||
channel = claim.channel_name;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style}>
|
||||
<TouchableOpacity style={style} onPress={onPress}>
|
||||
<FileItemMedia style={fileListStyle.thumbnail}
|
||||
blurRadius={obscureNsfw ? 15 : 0}
|
||||
resizeMode="cover"
|
||||
title={title}
|
||||
thumbnail={metadata ? metadata.thumbnail : null} />
|
||||
<View style={fileListStyle.detailsContainer}>
|
||||
{isResolving && (
|
||||
<View>
|
||||
<Text style={fileListStyle.uri}>{uri}</Text>
|
||||
<View style={fileListStyle.row}>
|
||||
<ActivityIndicator size={"small"} color={Colors.LbryGreen} />
|
||||
</View>
|
||||
</View>)}
|
||||
|
||||
{!isResolving && <Text style={fileListStyle.title}>{title || name}</Text>}
|
||||
{!isResolving && channel &&
|
||||
<Link style={fileListStyle.publisher} text={channel} onPress={() => {
|
||||
const channelUri = normalizeURI(channel);
|
||||
navigation.navigate({ routeName: 'File', key: channelUri, params: { uri: channelUri }});
|
||||
}} />}
|
||||
|
||||
{fileInfo &&
|
||||
<View style={fileListStyle.downloadInfo}>
|
||||
<Text style={fileListStyle.downloadStorage}>{this.getStorageForFileInfo(fileInfo)}</Text>
|
||||
{!fileInfo.completed &&
|
||||
<View style={fileListStyle.progress}>
|
||||
<View style={[fileListStyle.progressCompleted, { flex: this.getDownloadProgress(fileInfo) } ]} />
|
||||
|
||||
<View style={[fileListStyle.progressRemaining, { flex: (100 - this.getDownloadProgress(fileInfo)) } ]} />
|
||||
</View>}
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
{obscureNsfw && <NsfwOverlay onPress={() => navigation.navigate({ routeName: 'Settings', key: 'settingsPage' })} />}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FileListItem;
|
|
@ -1,64 +0,0 @@
|
|||
import React from 'react';
|
||||
import { normalizeURI, parseURI } from 'lbry-redux';
|
||||
import { ActivityIndicator, Text, TouchableOpacity, View } from 'react-native';
|
||||
import Colors from '../../styles/colors';
|
||||
import FileItemMedia from '../fileItemMedia';
|
||||
import Link from '../../component/link';
|
||||
import NsfwOverlay from '../../component/nsfwOverlay';
|
||||
import searchStyle from '../../styles/search';
|
||||
|
||||
class SearchResultItem extends React.PureComponent {
|
||||
render() {
|
||||
const {
|
||||
claim,
|
||||
metadata,
|
||||
isResolvingUri,
|
||||
showUri,
|
||||
isDownloaded,
|
||||
style,
|
||||
onPress,
|
||||
navigation
|
||||
} = this.props;
|
||||
|
||||
const uri = normalizeURI(this.props.uri);
|
||||
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||
const title = metadata && metadata.title ? metadata.title : parseURI(uri).contentName;
|
||||
|
||||
let name;
|
||||
let channel;
|
||||
if (claim) {
|
||||
name = claim.name;
|
||||
channel = claim.channel_name;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style}>
|
||||
<TouchableOpacity style={style} onPress={onPress}>
|
||||
<FileItemMedia style={searchStyle.thumbnail}
|
||||
blurRadius={obscureNsfw ? 15 : 0}
|
||||
resizeMode="cover"
|
||||
title={title}
|
||||
thumbnail={metadata ? metadata.thumbnail : null} />
|
||||
<View style={searchStyle.detailsContainer}>
|
||||
{isResolvingUri && (
|
||||
<View>
|
||||
<Text style={searchStyle.uri}>{uri}</Text>
|
||||
<View style={searchStyle.row}>
|
||||
<ActivityIndicator size={"small"} color={Colors.LbryGreen} />
|
||||
</View>
|
||||
</View>)}
|
||||
{!isResolvingUri && <Text style={searchStyle.title}>{title || name}</Text>}
|
||||
{!isResolvingUri && channel &&
|
||||
<Link style={searchStyle.publisher} text={channel} onPress={() => {
|
||||
const channelUri = normalizeURI(channel);
|
||||
navigation.navigate({ routeName: 'File', key: channelUri, params: { uri: channelUri }});
|
||||
}} />}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
{obscureNsfw && <NsfwOverlay onPress={() => navigation.navigate({ routeName: 'Settings', key: 'settingsPage' })} />}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SearchResultItem;
|
|
@ -45,12 +45,20 @@ class UriBar extends React.PureComponent {
|
|||
}
|
||||
|
||||
handleItemPress = (item) => {
|
||||
const { navigation, updateSearchQuery } = this.props;
|
||||
const { navigation, onSearchSubmitted, updateSearchQuery } = this.props;
|
||||
const { type, value } = item;
|
||||
|
||||
Keyboard.dismiss();
|
||||
|
||||
if (SEARCH_TYPES.SEARCH === type) {
|
||||
this.setState({ currentValue: value });
|
||||
updateSearchQuery(value);
|
||||
|
||||
if (onSearchSubmitted) {
|
||||
onSearchSubmitted(value);
|
||||
return;
|
||||
}
|
||||
|
||||
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: value }});
|
||||
} else {
|
||||
const uri = normalizeURI(value);
|
||||
|
|
20
app/src/page/downloads/index.js
Normal file
20
app/src/page/downloads/index.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
doFileList,
|
||||
selectFileInfosDownloaded,
|
||||
selectMyClaimsWithoutChannels,
|
||||
selectIsFetchingFileList,
|
||||
} from 'lbry-redux';
|
||||
import DownloadsPage from './view';
|
||||
|
||||
const select = (state) => ({
|
||||
fileInfos: selectFileInfosDownloaded(state),
|
||||
fetching: selectIsFetchingFileList(state),
|
||||
claims: selectMyClaimsWithoutChannels(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
fileList: () => dispatch(doFileList()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(DownloadsPage);
|
77
app/src/page/downloads/view.js
Normal file
77
app/src/page/downloads/view.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
import React from 'react';
|
||||
import { Lbry, buildURI } from 'lbry-redux';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Button,
|
||||
FlatList,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
ScrollView
|
||||
} from 'react-native';
|
||||
import Colors from '../../styles/colors';
|
||||
import PageHeader from '../../component/pageHeader';
|
||||
import FileListItem from '../../component/fileListItem';
|
||||
import FloatingWalletBalance from '../../component/floatingWalletBalance';
|
||||
import UriBar from '../../component/uriBar';
|
||||
import downloadsStyle from '../../styles/downloads';
|
||||
import fileListStyle from '../../styles/fileList';
|
||||
|
||||
class DownloadsPage extends React.PureComponent {
|
||||
static navigationOptions = {
|
||||
title: 'My LBRY'
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fileList();
|
||||
}
|
||||
|
||||
uriFromFileInfo(fileInfo) {
|
||||
const { name: claimName, claim_name: claimNameDownloaded, claim_id: claimId } = fileInfo;
|
||||
const uriParams = {};
|
||||
uriParams.contentName = claimName || claimNameDownloaded;
|
||||
uriParams.claimId = claimId;
|
||||
return buildURI(uriParams);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { fetching, fileInfos, navigation } = this.props;
|
||||
const hasDownloads = fileInfos && Object.values(fileInfos).length > 0;
|
||||
|
||||
return (
|
||||
<View style={downloadsStyle.container}>
|
||||
{!fetching && !hasDownloads && <Text style={downloadsStyle.noDownloadsText}>You have not downloaded anything from LBRY yet.</Text>}
|
||||
{fetching && !hasDownloads && <ActivityIndicator size="large" color={Colors.LbryGreen} style={downloadsStyle.loading} /> }
|
||||
{hasDownloads &&
|
||||
<FlatList
|
||||
style={downloadsStyle.scrollContainer}
|
||||
contentContainerStyle={downloadsStyle.scrollPadding}
|
||||
renderItem={ ({item}) => (
|
||||
<FileListItem
|
||||
style={fileListStyle.item}
|
||||
uri={this.uriFromFileInfo(item)}
|
||||
navigation={navigation}
|
||||
onPress={() => navigation.navigate({
|
||||
routeName: 'File',
|
||||
key: 'filePage',
|
||||
params: { uri: this.uriFromFileInfo(item), autoplay: true }
|
||||
})} />
|
||||
)
|
||||
}
|
||||
data={fileInfos.sort((a, b) => {
|
||||
// TODO: Implement sort based on user selection
|
||||
if (!a.completed && b.completed) return -1;
|
||||
if (a.completed && !b.completed) return 1;
|
||||
if (a.metadata.title === b.metadata.title) return 0;
|
||||
return (a.metadata.title < b.metadata.title) ? -1 : 1;
|
||||
})}
|
||||
keyExtractor={(item, index) => item.outpoint}
|
||||
/>}
|
||||
<FloatingWalletBalance navigation={navigation} />
|
||||
<UriBar navigation={navigation} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DownloadsPage;
|
|
@ -64,6 +64,7 @@ class FilePage extends React.PureComponent {
|
|||
|
||||
const { isResolvingUri, resolveUri, navigation } = this.props;
|
||||
const { uri } = navigation.state.params;
|
||||
|
||||
if (!isResolvingUri) resolveUri(uri);
|
||||
|
||||
this.fetchFileInfo(this.props);
|
||||
|
@ -295,7 +296,7 @@ class FilePage extends React.PureComponent {
|
|||
blackListedOutpoints,
|
||||
navigation
|
||||
} = this.props;
|
||||
const { uri } = navigation.state.params;
|
||||
const { uri, autoplay } = navigation.state.params;
|
||||
|
||||
let innerContent = null;
|
||||
if ((isResolvingUri && !claim) || !claim) {
|
||||
|
@ -402,7 +403,7 @@ class FilePage extends React.PureComponent {
|
|||
ref={(ref) => { this.player = ref; }}
|
||||
uri={uri}
|
||||
style={playerStyle}
|
||||
autoPlay={this.state.autoPlayMedia}
|
||||
autoPlay={autoplay || this.state.autoPlayMedia}
|
||||
onFullscreenToggled={this.handleFullscreenToggle}
|
||||
onLayout={(evt) => {
|
||||
if (!this.state.playerHeight) {
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
} from 'react-native';
|
||||
import Colors from '../../styles/colors';
|
||||
import PageHeader from '../../component/pageHeader';
|
||||
import SearchResultItem from '../../component/searchResultItem';
|
||||
import FileListItem from '../../component/fileListItem';
|
||||
import FloatingWalletBalance from '../../component/floatingWalletBalance';
|
||||
import UriBar from '../../component/uriBar';
|
||||
import searchStyle from '../../styles/search';
|
||||
|
@ -38,7 +38,7 @@ class SearchPage extends React.PureComponent {
|
|||
<Text style={searchStyle.noResultsText}>No results to display.</Text>}
|
||||
<ScrollView style={searchStyle.scrollContainer} contentContainerStyle={searchStyle.scrollPadding}>
|
||||
{!isSearching && uris && uris.length ? (
|
||||
uris.map(uri => <SearchResultItem key={uri}
|
||||
uris.map(uri => <FileListItem key={uri}
|
||||
uri={uri}
|
||||
style={searchStyle.resultItem}
|
||||
navigation={navigation}
|
||||
|
|
|
@ -46,7 +46,7 @@ class SettingsPage extends React.PureComponent {
|
|||
|
||||
<View style={settingsStyle.row}>
|
||||
<View style={settingsStyle.switchText}>
|
||||
<Text style={settingsStyle.label}>Keep the daemon background service running when the app is suspended.</Text>
|
||||
<Text style={settingsStyle.label}>Keep the daemon background service running after closing the app</Text>
|
||||
<Text style={settingsStyle.description}>Enable this option for quicker app launch and to keep the synchronisation with the blockchain up to date.</Text>
|
||||
</View>
|
||||
<View style={settingsStyle.switchContainer}>
|
||||
|
|
34
app/src/styles/downloads.js
Normal file
34
app/src/styles/downloads.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const downloadsStyle = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
itemList: {
|
||||
flex: 1,
|
||||
},
|
||||
scrollContainer: {
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
paddingLeft: 16,
|
||||
paddingRight: 16,
|
||||
marginBottom: 60
|
||||
},
|
||||
scrollPadding: {
|
||||
paddingBottom: 16
|
||||
},
|
||||
noDownloadsText: {
|
||||
textAlign: 'center',
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 14,
|
||||
position: 'absolute'
|
||||
},
|
||||
loading: {
|
||||
position: 'absolute'
|
||||
}
|
||||
});
|
||||
|
||||
export default downloadsStyle;
|
67
app/src/styles/fileList.js
Normal file
67
app/src/styles/fileList.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { Dimensions, StyleSheet } from 'react-native';
|
||||
import Colors from './colors';
|
||||
|
||||
const screenDimension = Dimensions.get('window');
|
||||
const screenWidth = screenDimension.width;
|
||||
const screenHeight = screenDimension.height;
|
||||
const thumbnailHeight = 100;
|
||||
const thumbnailWidth = (screenHeight / screenWidth) * thumbnailHeight;
|
||||
|
||||
const fileListStyle = StyleSheet.create({
|
||||
item: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: 16
|
||||
},
|
||||
detailsContainer: {
|
||||
flex: 1
|
||||
},
|
||||
thumbnail: {
|
||||
width: thumbnailWidth,
|
||||
height: thumbnailHeight,
|
||||
marginRight: 16,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
title: {
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
fontSize: 16
|
||||
},
|
||||
uri: {
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
fontSize: 14,
|
||||
marginBottom: 8
|
||||
},
|
||||
publisher: {
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
fontSize: 14,
|
||||
marginTop: 3,
|
||||
color: Colors.LbryGreen
|
||||
},
|
||||
loading: {
|
||||
position: 'absolute'
|
||||
},
|
||||
downloadInfo: {
|
||||
marginTop: 8
|
||||
},
|
||||
downloadStorage: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 14,
|
||||
color: Colors.ChannelGrey
|
||||
},
|
||||
progress: {
|
||||
marginTop: 4,
|
||||
height: 3,
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
progressCompleted: {
|
||||
backgroundColor: Colors.LbryGreen
|
||||
},
|
||||
progressRemaining: {
|
||||
backgroundColor: Colors.LbryGreen,
|
||||
opacity: 0.2
|
||||
}
|
||||
});
|
||||
|
||||
export default fileListStyle;
|
|
@ -1,11 +1,4 @@
|
|||
import { Dimensions, StyleSheet } from 'react-native';
|
||||
import Colors from './colors';
|
||||
|
||||
const screenDimension = Dimensions.get('window');
|
||||
const screenWidth = screenDimension.width;
|
||||
const screenHeight = screenDimension.height;
|
||||
const thumbnailHeight = 100;
|
||||
const thumbnailWidth = (screenHeight / screenWidth) * thumbnailHeight;
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const searchStyle = StyleSheet.create({
|
||||
container: {
|
||||
|
@ -30,36 +23,12 @@ const searchStyle = StyleSheet.create({
|
|||
justifyContent: 'space-between',
|
||||
marginTop: 16
|
||||
},
|
||||
thumbnail: {
|
||||
width: thumbnailWidth,
|
||||
height: thumbnailHeight,
|
||||
marginRight: 16,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
detailsContainer: {
|
||||
flex: 1
|
||||
},
|
||||
searchInput: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 16
|
||||
},
|
||||
title: {
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
fontSize: 16
|
||||
},
|
||||
uri: {
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
fontSize: 14,
|
||||
marginBottom: 8
|
||||
},
|
||||
publisher: {
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
fontSize: 14,
|
||||
marginTop: 3,
|
||||
color: Colors.LbryGreen
|
||||
},
|
||||
noResultsText: {
|
||||
textAlign: 'center',
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
|
|
|
@ -25,12 +25,14 @@ const settingsStyle = StyleSheet.create({
|
|||
},
|
||||
label: {
|
||||
fontSize: 14,
|
||||
fontFamily: 'Metropolis-Regular'
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
lineHeight: 18
|
||||
},
|
||||
description: {
|
||||
color: '#aaaaaa',
|
||||
fontSize: 12,
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
color: '#aaaaaa'
|
||||
lineHeight: 18
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -157,7 +157,8 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
|
|||
builder.setContentIntent(getLaunchPendingIntent(id))
|
||||
.setContentText(String.format("%.0f%% (%s / %s)", progress, formatBytes(writtenBytes), formatBytes(totalBytes)))
|
||||
.setGroup(GROUP_DOWNLOADS)
|
||||
.setProgress(MAX_PROGRESS, new Double(progress).intValue(), false);
|
||||
.setProgress(MAX_PROGRESS, new Double(progress).intValue(), false)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download);
|
||||
notificationManager.notify(notificationId, builder.build());
|
||||
|
||||
if (progress == MAX_PROGRESS) {
|
||||
|
|
Loading…
Reference in a new issue
I noticed this only works because the dynamic elements are regenerated.
Are you getting warnings from not using
key
attributes?