fetch and display rewards, add styles for the reward card
This commit is contained in:
commit
c8b6c1c21c
66 changed files with 2784 additions and 1741 deletions
.gitignore.travis.ymlVagrantfile
app
package-lock.jsonpackage.json
buildozer.spec.samplebuildozer.spec.travisbuildozer.spec.vagrantsrc
component
AppNavigator.js
constants.jsindex.jsbutton
fileDownloadButton
mediaPlayer
pageHeader
rewardCard
uriBar/internal
walletAddress
walletSend
page
about
discover
file
firstRun
rewards
settings
splash
trending
redux
styles
p4a/pythonforandroid/bootstraps/lbry/build/templates
recipes
libgmp
netifaces
pycrypto
sqlite3
twisted
src/main
assets/fonts
java/io/lbry/browser
python
res
drawable-hdpi
drawable-mdpi
drawable-xhdpi
drawable-xxhdpi
drawable-xxxhdpi
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -6,4 +6,6 @@ build.log
|
|||
recipes/**/*.pyc
|
||||
src/main/assets/index.android.bundle
|
||||
src/main/assets/index.android.bundle.meta
|
||||
*.log
|
||||
.vagrant
|
||||
|
||||
|
|
|
@ -28,13 +28,13 @@ install:
|
|||
- mkdir -p cd ~/.buildozer/android/platform/
|
||||
- wget -q 'https://dl.google.com/android/repository/android-ndk-r13b-linux-x86_64.zip' -P ~/.buildozer/android/platform/
|
||||
- wget -q 'https://dl.google.com/android/android-sdk_r23-linux.tgz' -P ~/.buildozer/android/platform/
|
||||
- wget -q 'https://dl.google.com/android/repository/platform-26_r02.zip' -P ~/.buildozer/android/platform/
|
||||
- wget -q 'https://dl.google.com/android/repository/platform-27_r01.zip' -P ~/.buildozer/android/platform/
|
||||
- wget -q 'https://dl.google.com/android/repository/build-tools_r26.0.1-linux.zip' -P ~/.buildozer/android/platform/
|
||||
- unzip -qq ~/.buildozer/android/platform/android-ndk-r13b-linux-x86_64.zip -d ~/.buildozer/android/platform/
|
||||
- tar -xf ~/.buildozer/android/platform/android-sdk_r23-linux.tgz -C ~/.buildozer/android/platform/
|
||||
- mv ~/.buildozer/android/platform/android-sdk-linux ~/.buildozer/android/platform/android-sdk-23
|
||||
- unzip -qq ~/.buildozer/android/platform/platform-26_r02.zip -d ~/.buildozer/android/platform/android-sdk-23/platforms
|
||||
- mv ~/.buildozer/android/platform/android-sdk-23/platforms/android-8.0.0 ~/.buildozer/android/platform/android-sdk-23/platforms/android-26
|
||||
- unzip -qq ~/.buildozer/android/platform/platform-27_r01.zip -d ~/.buildozer/android/platform/android-sdk-23/platforms
|
||||
- mv ~/.buildozer/android/platform/android-sdk-23/platforms/android-8.1.0 ~/.buildozer/android/platform/android-sdk-23/platforms/android-27
|
||||
- mkdir -p ~/.buildozer/android/platform/android-sdk-23/build-tools
|
||||
- unzip -qq ~/.buildozer/android/platform/build-tools_r26.0.1-linux.zip -d ~/.buildozer/android/platform/android-sdk-23/build-tools
|
||||
- mv ~/.buildozer/android/platform/android-sdk-23/build-tools/android-8.0.0 ~/.buildozer/android/platform/android-sdk-23/build-tools/26.0.1
|
||||
|
|
97
Vagrantfile
vendored
Normal file
97
Vagrantfile
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
echoed=false
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "ubuntu/bionic64"
|
||||
#config.disksize.size = "20GB"
|
||||
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
host = RbConfig::CONFIG['host_os']
|
||||
|
||||
# Give VM 1/4 system memory & access to all cpu cores on the host
|
||||
if host =~ /darwin/
|
||||
cpus = `sysctl -n hw.ncpu`.to_i
|
||||
# sysctl returns Bytes and we need to convert to MB
|
||||
mem = `sysctl -n hw.memsize`.to_i / 1024 / 1024 / 4
|
||||
elsif host =~ /linux/
|
||||
cpus = `nproc`.to_i
|
||||
# meminfo shows KB and we need to convert to MB
|
||||
mem = `grep 'MemTotal' /proc/meminfo | sed -e 's/MemTotal://' -e 's/ kB//'`.to_i / 1024 / 4
|
||||
else
|
||||
cpus = `wmic cpu get NumberOfCores`.split("\n")[2].to_i
|
||||
mem = `wmic OS get TotalVisibleMemorySize`.split("\n")[2].to_i / 1024 /4
|
||||
end
|
||||
|
||||
mem = mem / 1024 / 4
|
||||
mem = [mem, 2048].max # Minimum 2048
|
||||
|
||||
if echoed === false
|
||||
echoed=true
|
||||
puts("Memory", mem)
|
||||
puts("CPUs", cpus)
|
||||
end
|
||||
|
||||
#v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/home_vagrant_lbry-android", "1"]
|
||||
#v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/vagrant", "1"]
|
||||
v.customize ["modifyvm", :id, "--memory", mem]
|
||||
v.customize ["modifyvm", :id, "--cpus", cpus]
|
||||
end
|
||||
|
||||
config.vm.synced_folder "./", "/home/vagrant/lbry-android"
|
||||
|
||||
|
||||
config.vm.provision "shell", inline: <<-SHELL
|
||||
dpkg --add-architecture i386
|
||||
apt-get update
|
||||
apt-get install -y libssl-dev
|
||||
apt-get install -y autoconf libffi-dev pkg-config libtool build-essential ccache git libncurses5:i386 libstdc++6:i386 libgtk2.0-0:i386 libpangox-1.0-0:i386 libpangoxft-1.0-0:i386 libidn11:i386 python2.7 python2.7-dev openjdk-8-jdk unzip zlib1g-dev zlib1g:i386 m4 libc6-dev-i386 python-pip
|
||||
pip install -f --upgrade setuptools pyopenssl
|
||||
git clone https://github.com/lbryio/buildozer.git
|
||||
cd buildozer
|
||||
python2.7 setup.py install
|
||||
cd ../
|
||||
rm -rf ./buildozer
|
||||
|
||||
# Install additonal buildozer dependencies
|
||||
sudo apt-get install cython
|
||||
|
||||
# Install node
|
||||
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
export HOME=/home/vagrant
|
||||
|
||||
cp $HOME/lbry-android/buildozer.spec.vagrant $HOME/lbry-android/buildozer.spec
|
||||
|
||||
mkdir -p cd $HOME/.buildozer/android/platform/
|
||||
wget -q 'https://dl.google.com/android/repository/android-ndk-r13b-linux-x86_64.zip' -P $HOME/.buildozer/android/platform/
|
||||
wget -q 'https://dl.google.com/android/android-sdk_r23-linux.tgz' -P $HOME/.buildozer/android/platform/
|
||||
wget -q 'https://dl.google.com/android/repository/platform-27_r01.zip' -P $HOME/.buildozer/android/platform/
|
||||
wget -q 'https://dl.google.com/android/repository/build-tools_r26.0.1-linux.zip' -P $HOME/.buildozer/android/platform/
|
||||
unzip -qq $HOME/.buildozer/android/platform/android-ndk-r13b-linux-x86_64.zip -d $HOME/.buildozer/android/platform/
|
||||
rm $HOME/.buildozer/android/platform/android-ndk-r13b-linux-x86_64.zip
|
||||
tar -xf $HOME/.buildozer/android/platform/android-sdk_r23-linux.tgz -C $HOME/.buildozer/android/platform/
|
||||
rm $HOME/.buildozer/android/platform/android-sdk_r23-linux.tgz
|
||||
mv $HOME/.buildozer/android/platform/android-sdk-linux $HOME/.buildozer/android/platform/android-sdk-23
|
||||
unzip -qq $HOME/.buildozer/android/platform/android-23_r02.zip -d $HOME/.buildozer/android/platform/android-sdk-23/platforms
|
||||
rm $HOME/.buildozer/android/platform/platform-27_r01.zip
|
||||
mv $HOME/.buildozer/android/platform/android-sdk-23/platforms/android-8.1.0 $HOME/.buildozer/android/platform/android-sdk-23/platforms/android-27
|
||||
mkdir -p $HOME/.buildozer/android/platform/android-sdk-23/build-tools
|
||||
unzip -qq $HOME/.buildozer/android/platform/build-tools_r26.0.1-linux.zip -d $HOME/.buildozer/android/platform/android-sdk-23/build-tools
|
||||
rm $HOME/.buildozer/android/platform/build-tools_r26.0.1-linux.zip
|
||||
mv $HOME/.buildozer/android/platform/android-sdk-23/build-tools/android-8.0.0 $HOME/.buildozer/android/platform/android-sdk-23/build-tools/26.0.1
|
||||
mkdir -p $HOME/.buildozer/android/platform/android-sdk-23/licenses
|
||||
|
||||
rm -rf $HOME/.buildozer/android/platform/android-sdk-23/tools
|
||||
# https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
|
||||
wget -q https://dl.google.com/android/repository/tools_r25.2.5-linux.zip
|
||||
unzip -o -q ./tools_r25.2.5-linux.zip -d $HOME/.buildozer/android/platform/android-sdk-23/
|
||||
rm sdk-tools-linux-3859397.zip
|
||||
|
||||
echo $'\nd56f5187479451eabf01fb78af6dfcb131a6481e' > $HOME/.buildozer/android/platform/android-sdk-23/licenses/android-sdk-license
|
||||
|
||||
sudo chown -r vagrant $HOME
|
||||
|
||||
echo "Installing React Native via NPM..."
|
||||
sudo npm install -g react-native-cli
|
||||
SHELL
|
||||
end
|
2675
app/package-lock.json
generated
2675
app/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -6,20 +6,23 @@
|
|||
"start": "node node_modules/react-native/local-cli/cli.js start"
|
||||
},
|
||||
"dependencies": {
|
||||
"base-64": "^0.1.0",
|
||||
"lbry-redux": "lbryio/lbry-redux",
|
||||
"lbryinc": "lbryio/lbryinc",
|
||||
"lbryinc": "lbryio/lbryinc#authentication-flow",
|
||||
"moment": "^2.22.1",
|
||||
"react": "16.2.0",
|
||||
"react-native": "0.55.3",
|
||||
"react-native-fetch-blob": "^0.10.8",
|
||||
"react-native-image-zoom-viewer": "^2.2.5",
|
||||
"react-native-vector-icons": "^4.5.0",
|
||||
"react-native-video": "2.0.0",
|
||||
"react-native-vector-icons": "^5.0.0",
|
||||
"react-native-video": "lbryio/react-native-video#exoplayer-lbry-android",
|
||||
"react-navigation": "^1.5.12",
|
||||
"react-navigation-redux-helpers": "^1.0.1",
|
||||
"react-redux": "^5.0.3",
|
||||
"redux": "^3.6.0",
|
||||
"redux-logger": "3.0.6",
|
||||
"redux-persist": "^4.8.0",
|
||||
"redux-persist-filesystem-storage": "^1.2.0",
|
||||
"redux-persist-transform-compress": "^4.2.0",
|
||||
"redux-persist-transform-filter": "0.0.10",
|
||||
"redux-thunk": "^2.2.0"
|
||||
|
|
|
@ -28,19 +28,29 @@ import {
|
|||
TextInput,
|
||||
ToastAndroid
|
||||
} from 'react-native';
|
||||
import { SETTINGS, doHideNotification, selectNotification } from 'lbry-redux';
|
||||
import { SETTINGS, doHideNotification, doNotify, selectNotification } from 'lbry-redux';
|
||||
import {
|
||||
doUserEmailVerify,
|
||||
doUserEmailVerifyFailure,
|
||||
selectEmailToVerify,
|
||||
selectEmailVerifyIsPending,
|
||||
selectEmailVerifyErrorMessage,
|
||||
selectUser
|
||||
} from 'lbryinc';
|
||||
import { makeSelectClientSetting } from '../redux/selectors/settings';
|
||||
import Feather from 'react-native-vector-icons/Feather';
|
||||
import { decode as atob } from 'base-64';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import Constants from '../constants';
|
||||
import discoverStyle from '../styles/discover';
|
||||
import searchStyle from '../styles/search';
|
||||
import SearchRightHeaderIcon from "../component/searchRightHeaderIcon";
|
||||
import SearchRightHeaderIcon from '../component/searchRightHeaderIcon';
|
||||
|
||||
const discoverStack = StackNavigator({
|
||||
Discover: {
|
||||
screen: DiscoverPage,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
title: 'Discover',
|
||||
headerLeft: <Feather name="menu" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
||||
headerLeft: <Icon name="bars" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
||||
})
|
||||
},
|
||||
File: {
|
||||
|
@ -65,7 +75,7 @@ const trendingStack = StackNavigator({
|
|||
screen: TrendingPage,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
title: 'Trending',
|
||||
headerLeft: <Feather name="menu" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
||||
headerLeft: <Icon name="bars" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
@ -75,7 +85,7 @@ const walletStack = StackNavigator({
|
|||
screen: WalletPage,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
title: 'Wallet',
|
||||
headerLeft: <Feather name="menu" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
||||
headerLeft: <Icon name="bars" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
||||
})
|
||||
},
|
||||
TransactionHistory: {
|
||||
|
@ -94,7 +104,7 @@ const rewardsStack = StackNavigator({
|
|||
screen: RewardsPage,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
title: 'Rewards',
|
||||
headerLeft: <Feather name="menu" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
||||
headerLeft: <Icon name="bars" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
@ -134,18 +144,27 @@ export const AppNavigator = new StackNavigator({
|
|||
class AppWithNavigationState extends React.Component {
|
||||
static supportedDisplayTypes = ['toast'];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
emailVerifyDone: false
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
AppState.addEventListener('change', this._handleAppStateChange);
|
||||
BackHandler.addEventListener('hardwareBackPress', function() {
|
||||
const { dispatch, nav } = this.props;
|
||||
// There should be a better way to check this
|
||||
if (nav.routes.length > 0) {
|
||||
const subRoutes = nav.routes[0].routes[0].routes;
|
||||
const lastRoute = subRoutes[subRoutes.length - 1];
|
||||
if (nav.routes[0].routes[0].index > 0 &&
|
||||
['About', 'Settings'].indexOf(lastRoute.key) > -1) {
|
||||
dispatch(NavigationActions.back());
|
||||
return true;
|
||||
if (nav.routes[0].routes && nav.routes[0].routes.length > 0) {
|
||||
const subRoutes = nav.routes[0].routes[0].routes;
|
||||
const lastRoute = subRoutes[subRoutes.length - 1];
|
||||
if (nav.routes[0].routes[0].index > 0 &&
|
||||
['About', 'Settings'].indexOf(lastRoute.key) > -1) {
|
||||
dispatch(NavigationActions.back());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (nav.routes[0].routeName === 'Main') {
|
||||
if (nav.routes[0].routes[0].routes[0].index > 0) {
|
||||
|
@ -170,7 +189,14 @@ class AppWithNavigationState extends React.Component {
|
|||
|
||||
componentWillUpdate(nextProps) {
|
||||
const { dispatch } = this.props;
|
||||
const { notification } = nextProps;
|
||||
const {
|
||||
notification,
|
||||
emailToVerify,
|
||||
emailVerifyPending,
|
||||
emailVerifyErrorMessage,
|
||||
user
|
||||
} = nextProps;
|
||||
|
||||
if (notification) {
|
||||
const { displayType, message } = notification;
|
||||
let currentDisplayType;
|
||||
|
@ -192,6 +218,20 @@ class AppWithNavigationState extends React.Component {
|
|||
|
||||
dispatch(doHideNotification());
|
||||
}
|
||||
|
||||
if (user &&
|
||||
!emailVerifyPending &&
|
||||
!this.state.emailVerifyDone &&
|
||||
(emailToVerify || emailVerifyErrorMessage)) {
|
||||
AsyncStorage.getItem(Constants.KEY_SHOULD_VERIFY_EMAIL).then(shouldVerify => {
|
||||
if ('true' === shouldVerify) {
|
||||
this.setState({ emailVerifyDone: true });
|
||||
const message = emailVerifyErrorMessage ?
|
||||
String(emailVerifyErrorMessage) : 'Your email address was successfully verified.';
|
||||
dispatch(doNotify({ message, displayType: ['toast'] }));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_handleAppStateChange = (nextAppState) => {
|
||||
|
@ -210,12 +250,38 @@ class AppWithNavigationState extends React.Component {
|
|||
_handleUrl = (evt) => {
|
||||
const { dispatch } = this.props;
|
||||
if (evt.url) {
|
||||
const navigateAction = NavigationActions.navigate({
|
||||
routeName: 'File',
|
||||
key: evt.url,
|
||||
params: { uri: evt.url }
|
||||
});
|
||||
dispatch(navigateAction);
|
||||
if (evt.url.startsWith('lbry://?verify=')) {
|
||||
this.setState({ emailVerifyDone: false });
|
||||
let verification = {};
|
||||
try {
|
||||
verification = JSON.parse(atob(evt.url.substring(15)));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
if (verification.token && verification.recaptcha) {
|
||||
AsyncStorage.setItem(Constants.KEY_SHOULD_VERIFY_EMAIL, 'true');
|
||||
try {
|
||||
dispatch(doUserEmailVerify(verification.token, verification.recaptcha));
|
||||
} catch (error) {
|
||||
const message = 'Invalid Verification Token';
|
||||
dispatch(doUserEmailVerifyFailure(message));
|
||||
dispatch(doNotify({ message, displayType: ['toast'] }));
|
||||
}
|
||||
} else {
|
||||
dispatch(doNotify({
|
||||
message: 'Invalid Verification URI',
|
||||
displayType: ['toast'],
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
const navigateAction = NavigationActions.navigate({
|
||||
routeName: 'File',
|
||||
key: evt.url,
|
||||
params: { uri: evt.url }
|
||||
});
|
||||
dispatch(navigateAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,10 +300,14 @@ class AppWithNavigationState extends React.Component {
|
|||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state),
|
||||
nav: state.nav,
|
||||
notification: selectNotification(state),
|
||||
keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state),
|
||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state)
|
||||
emailToVerify: selectEmailToVerify(state),
|
||||
emailVerifyPending: selectEmailVerifyIsPending(state),
|
||||
emailVerifyErrorMessage: selectEmailVerifyErrorMessage(state),
|
||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),
|
||||
user: selectUser(state),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(AppWithNavigationState);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React from 'react';
|
||||
import { Text, TouchableOpacity } from 'react-native';
|
||||
import buttonStyle from '../../styles/button';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
import Colors from '../../styles/colors';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
|
||||
export default class Button extends React.PureComponent {
|
||||
render() {
|
||||
|
@ -10,9 +11,10 @@ export default class Button extends React.PureComponent {
|
|||
style,
|
||||
text,
|
||||
icon,
|
||||
theme,
|
||||
onPress
|
||||
} = this.props;
|
||||
|
||||
|
||||
let styles = [buttonStyle.button, buttonStyle.row];
|
||||
if (style) {
|
||||
if (style.length) {
|
||||
|
@ -21,19 +23,26 @@ export default class Button extends React.PureComponent {
|
|||
styles.push(style);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (disabled) {
|
||||
styles.push(buttonStyle.disabled);
|
||||
}
|
||||
|
||||
|
||||
const textStyles = [buttonStyle.text];
|
||||
if (icon && icon.trim().length > 0) {
|
||||
textStyles.push(buttonStyle.textWithIcon);
|
||||
}
|
||||
|
||||
|
||||
if (theme === 'light') {
|
||||
textStyles.push(buttonStyle.textDark);
|
||||
} else {
|
||||
// Dark background, default
|
||||
textStyles.push(buttonStyle.textLight);
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableOpacity disabled={disabled} style={styles} onPress={onPress}>
|
||||
{icon && <Icon name={icon} size={18} color='#ffffff' class={buttonStyle.icon} /> }
|
||||
{icon && <Icon name={icon} size={18} color={'light' === theme ? Colors.DarkGrey : Colors.White} />}
|
||||
{text && (text.trim().length > 0) && <Text style={textStyles}>{text}</Text>}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import { NativeModules, Text, View, TouchableOpacity } from 'react-native';
|
||||
import Button from '../button';
|
||||
import fileDownloadButtonStyle from '../../styles/fileDownloadButton';
|
||||
|
||||
class FileDownloadButton extends React.PureComponent {
|
||||
|
@ -64,17 +65,17 @@ class FileDownloadButton extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
return (
|
||||
<TouchableOpacity style={[style, fileDownloadButtonStyle.container]} onPress={() => {
|
||||
if (NativeModules.Mixpanel) {
|
||||
NativeModules.Mixpanel.track('Purchase Uri', { Uri: uri });
|
||||
}
|
||||
purchaseUri(uri);
|
||||
if (isPlayable && onPlay) {
|
||||
this.props.onPlay();
|
||||
}
|
||||
}}>
|
||||
<Text style={fileDownloadButtonStyle.text}>{isPlayable ? 'Play' : 'Download'}</Text>
|
||||
</TouchableOpacity>
|
||||
<Button icon={isPlayable ? 'play' : null}
|
||||
text={isPlayable ? 'Play' : 'Download'}
|
||||
style={[style, fileDownloadButtonStyle.container]} onPress={() => {
|
||||
if (NativeModules.Mixpanel) {
|
||||
NativeModules.Mixpanel.track('Purchase Uri', { Uri: uri });
|
||||
}
|
||||
purchaseUri(uri);
|
||||
if (isPlayable && onPlay) {
|
||||
this.props.onPlay();
|
||||
}
|
||||
}} />
|
||||
);
|
||||
} else if (fileInfo && fileInfo.download_path) {
|
||||
return (
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
TouchableOpacity
|
||||
} from 'react-native';
|
||||
import Video from 'react-native-video';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import FileItemMedia from '../fileItemMedia';
|
||||
import mediaPlayerStyle from '../../styles/mediaPlayer';
|
||||
|
||||
|
@ -152,11 +152,10 @@ class MediaPlayer extends React.PureComponent {
|
|||
}
|
||||
|
||||
checkSeekerPosition(val = 0) {
|
||||
const offset = this.getTrackingOffset();
|
||||
if (val < offset) {
|
||||
val = offset;
|
||||
} else if (val >= (offset + this.seekerWidth)) {
|
||||
return offset + this.seekerWidth;
|
||||
if (val < 0) {
|
||||
val = 0;
|
||||
} else if (val >= this.seekerWidth) {
|
||||
return this.seekerWidth;
|
||||
}
|
||||
|
||||
return val;
|
||||
|
@ -211,9 +210,6 @@ class MediaPlayer extends React.PureComponent {
|
|||
}
|
||||
|
||||
calculateSeekerPosition() {
|
||||
if (this.state.fullscreenMode) {
|
||||
return this.getTrackingOffset() + (this.seekerWidth * this.getCurrentTimePercentage());
|
||||
}
|
||||
return this.seekerWidth * this.getCurrentTimePercentage();
|
||||
}
|
||||
|
||||
|
@ -229,7 +225,7 @@ class MediaPlayer extends React.PureComponent {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
this.setSeekerPosition(this.calculateSeekerPosition());
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -265,10 +261,16 @@ class MediaPlayer extends React.PureComponent {
|
|||
return null;
|
||||
}
|
||||
|
||||
getEncodedDownloadPath = (fileInfo) => {
|
||||
const { file_name: fileName } = fileInfo;
|
||||
const encodedFileName = encodeURIComponent(fileName).replace(/!/g, '%21');
|
||||
return fileInfo.download_path.replace(new RegExp(fileName, 'g'), encodedFileName);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { backgroundPlayEnabled, fileInfo, thumbnail, onLayout, style } = this.props;
|
||||
const flexCompleted = this.getCurrentTimePercentage() * 100;
|
||||
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
|
||||
const completedWidth = this.getCurrentTimePercentage() * this.seekerWidth;
|
||||
const remainingWidth = this.seekerWidth - completedWidth;
|
||||
let styles = [this.state.fullscreenMode ? mediaPlayerStyle.fullscreenContainer : mediaPlayerStyle.container];
|
||||
if (style) {
|
||||
if (style.length) {
|
||||
|
@ -283,7 +285,7 @@ class MediaPlayer extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<View style={styles} onLayout={onLayout}>
|
||||
<Video source={{ uri: 'file:///' + fileInfo.download_path }}
|
||||
<Video source={{ uri: 'file:///' + this.getEncodedDownloadPath(fileInfo) }}
|
||||
ref={(ref: Video) => { this.video = ref }}
|
||||
resizeMode={this.state.resizeMode}
|
||||
playInBackground={backgroundPlayEnabled}
|
||||
|
@ -306,14 +308,18 @@ class MediaPlayer extends React.PureComponent {
|
|||
this.seekerWidth = evt.nativeEvent.layout.width;
|
||||
}}>
|
||||
<View style={mediaPlayerStyle.progress}>
|
||||
<View style={[mediaPlayerStyle.innerProgressCompleted, { flex: flexCompleted }]} />
|
||||
<View style={[mediaPlayerStyle.innerProgressRemaining, { flex: flexRemaining }]} />
|
||||
<View style={[mediaPlayerStyle.innerProgressCompleted, { width: completedWidth }]} />
|
||||
<View style={[mediaPlayerStyle.innerProgressRemaining, { width: remainingWidth }]} />
|
||||
</View>
|
||||
</View>}
|
||||
|
||||
{this.state.areControlsVisible &&
|
||||
<View style={[mediaPlayerStyle.seekerHandle, { left: this.state.seekerPosition }]} { ...this.seekResponder.panHandlers }>
|
||||
<View style={this.state.seeking ? mediaPlayerStyle.bigSeekerCircle : mediaPlayerStyle.seekerCircle} />
|
||||
<View style={{ left: this.getTrackingOffset(), width: this.seekerWidth }}>
|
||||
<View style={[mediaPlayerStyle.seekerHandle,
|
||||
(this.state.fullscreenMode ? mediaPlayerStyle.seekerHandleFs : mediaPlayerStyle.seekerHandleContained),
|
||||
{ left: this.state.seekerPosition }]} { ...this.seekResponder.panHandlers }>
|
||||
<View style={this.state.seeking ? mediaPlayerStyle.bigSeekerCircle : mediaPlayerStyle.seekerCircle} />
|
||||
</View>
|
||||
</View>}
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Feather from 'react-native-vector-icons/Feather';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import pageHeaderStyle from '../../styles/pageHeader';
|
||||
|
||||
const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56;
|
||||
|
@ -21,7 +21,7 @@ class PageHeader extends React.PureComponent {
|
|||
pageHeaderStyle.container,
|
||||
{ height: APPBAR_HEIGHT }
|
||||
];
|
||||
|
||||
|
||||
return (
|
||||
<View style={containerStyles}>
|
||||
<View style={pageHeaderStyle.flexOne}>
|
||||
|
@ -35,7 +35,7 @@ class PageHeader extends React.PureComponent {
|
|||
</AnimatedText>
|
||||
</View>
|
||||
<TouchableOpacity style={pageHeaderStyle.left}>
|
||||
<Feather name="arrow-left" size={24} onPress={onBackPressed} style={pageHeaderStyle.backIcon} />
|
||||
<Icon name="arrow-left" size={24} onPress={onBackPressed} style={pageHeaderStyle.backIcon} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import rewardStyle from '../../styles/reward';
|
||||
|
||||
type Props = {
|
||||
|
@ -17,20 +18,25 @@ type Props = {
|
|||
|
||||
class RewardCard extends React.PureComponent<Props> {
|
||||
render() {
|
||||
const { reward } = props;
|
||||
const { canClaim, onClaimPress, reward } = this.props;
|
||||
const claimed = !!reward.transaction_id;
|
||||
|
||||
return (
|
||||
<View style={[rewardStyle.card, rewardStyle.row]}>
|
||||
<View style={rewardStyle.leftCol}>
|
||||
|
||||
<TouchableOpacity onPress={() => { if (!claimed && onClaimPress) { onClaimPress(); } }}>
|
||||
<Icon name={claimed ? "check-circle" : "circle"}
|
||||
style={claimed ? rewardStyle.claimed : (canClaim ? rewardStyle.unclaimed : rewardStyle.disabled)}
|
||||
size={20} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View style={rewardStyle.midCol}>
|
||||
<View style={rewardStyle.rewardTitle}>{reward.reward_title}</View>
|
||||
<View style={rewardStyle.rewardDescription}>{reward.reward_description}</View>
|
||||
<Text style={rewardStyle.rewardTitle}>{reward.reward_title}</Text>
|
||||
<Text style={rewardStyle.rewardDescription}>{reward.reward_description}</Text>
|
||||
</View>
|
||||
<View style={rewardStyle.rightCol}>
|
||||
<View style={rewardStyle.rewardAmount}>{reward.reward_amount}</View>
|
||||
<Text style={rewardStyle.rewardAmount}>{reward.reward_amount}</Text>
|
||||
<Text style={rewardStyle.rewardCurrency}>LBC</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -2,37 +2,37 @@
|
|||
import React from 'react';
|
||||
import { SEARCH_TYPES, normalizeURI } from 'lbry-redux';
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
import Feather from 'react-native-vector-icons/Feather';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import uriBarStyle from '../../../styles/uriBar';
|
||||
|
||||
class UriBarItem extends React.PureComponent {
|
||||
render() {
|
||||
const { item, onPress } = this.props;
|
||||
const { shorthand, type, value } = item;
|
||||
|
||||
|
||||
let icon;
|
||||
switch (type) {
|
||||
case SEARCH_TYPES.CHANNEL:
|
||||
icon = <Feather name="at-sign" size={18} />
|
||||
icon = <Icon name="at" size={18} />
|
||||
break;
|
||||
|
||||
|
||||
case SEARCH_TYPES.SEARCH:
|
||||
icon = <Feather name="search" size={18} />
|
||||
icon = <Icon name="search" size={18} />
|
||||
break;
|
||||
|
||||
|
||||
case SEARCH_TYPES.FILE:
|
||||
default:
|
||||
icon = <Feather name="file" size={18} />
|
||||
icon = <Icon name="file" size={18} />
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<TouchableOpacity style={uriBarStyle.item} onPress={onPress}>
|
||||
{icon}
|
||||
<Text style={uriBarStyle.itemText} numberOfLines={1}>{shorthand || value} - {type === 'search' ? 'Search' : value}</Text>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default UriBarItem;
|
||||
|
|
|
@ -24,14 +24,14 @@ class WalletAddress extends React.PureComponent<Props> {
|
|||
|
||||
render() {
|
||||
const { receiveAddress, getNewAddress, gettingNewAddress } = this.props;
|
||||
|
||||
|
||||
return (
|
||||
<View style={walletStyle.card}>
|
||||
<Text style={walletStyle.title}>Receive Credits</Text>
|
||||
<Text style={[walletStyle.text, walletStyle.bottomMarginMedium]}>Use this wallet address to receive credits sent by another user (or yourself).</Text>
|
||||
<Address address={receiveAddress} style={walletStyle.bottomMarginSmall} />
|
||||
<Button style={[walletStyle.button, walletStyle.bottomMarginLarge]}
|
||||
icon={'refresh'}
|
||||
icon={'sync'}
|
||||
text={'Get New Address'}
|
||||
onPress={getNewAddress}
|
||||
disabled={gettingNewAddress}
|
||||
|
|
|
@ -17,21 +17,21 @@ type Props = {
|
|||
|
||||
class WalletSend extends React.PureComponent<Props> {
|
||||
amountInput = null;
|
||||
|
||||
|
||||
state = {
|
||||
amount: null,
|
||||
address: null,
|
||||
addressChanged: false,
|
||||
addressValid: false
|
||||
};
|
||||
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
const { draftTransaction, transactionError } = nextProps;
|
||||
if (transactionError && transactionError.trim().length > 0) {
|
||||
this.setState({ address: draftTransaction.address, amount: draftTransaction.amount });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
handleSend = () => {
|
||||
const { balance, sendToAddress, notify } = this.props;
|
||||
const { address, amount } = this.state;
|
||||
|
@ -42,7 +42,7 @@ class WalletSend extends React.PureComponent<Props> {
|
|||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (amount > balance) {
|
||||
notify({
|
||||
message: 'Insufficient credits',
|
||||
|
@ -50,13 +50,13 @@ class WalletSend extends React.PureComponent<Props> {
|
|||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (amount && address) {
|
||||
sendToAddress(address, parseFloat(amount));
|
||||
this.setState({ address: null, amount: null });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
handleAddressInputBlur = () => {
|
||||
if (this.state.addressChanged && !this.state.addressValid) {
|
||||
const { notify } = this.props;
|
||||
|
@ -66,7 +66,7 @@ class WalletSend extends React.PureComponent<Props> {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
handleAddressInputSubmit = () => {
|
||||
if (this.amountInput) {
|
||||
this.amountInput.focus();
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
const Constants = {
|
||||
SETTING_ALPHA_UNDERSTANDS_RISKS: "ALPHA_UNDERSTANDS_RISKS"
|
||||
KEY_FIRST_RUN_EMAIL: "firstRunEmail",
|
||||
KEY_SHOULD_VERIFY_EMAIL: "shouldVerifyEmail",
|
||||
|
||||
SETTING_ALPHA_UNDERSTANDS_RISKS: "alphaUnderstandRisks",
|
||||
|
||||
ACTION_FIRST_RUN_PAGE_CHANGED: "FIRST_RUN_PAGE_CHANGED",
|
||||
};
|
||||
|
||||
export default Constants;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Provider, connect } from 'react-redux';
|
||||
import DiscoverPage from './page/discover';
|
||||
import {
|
||||
AppRegistry,
|
||||
AppState,
|
||||
|
@ -9,16 +8,6 @@ import {
|
|||
View,
|
||||
NativeModules
|
||||
} from 'react-native';
|
||||
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
|
||||
import {
|
||||
StackNavigator, addNavigationHelpers
|
||||
} from 'react-navigation';
|
||||
import { AppNavigator } from './component/AppNavigator';
|
||||
import AppWithNavigationState from './component/AppNavigator';
|
||||
import { persistStore, autoRehydrate } from 'redux-persist';
|
||||
import createCompressor from 'redux-persist-transform-compress';
|
||||
import createFilter from 'redux-persist-transform-filter';
|
||||
import thunk from 'redux-thunk';
|
||||
import {
|
||||
Lbry,
|
||||
claimsReducer,
|
||||
|
@ -28,9 +17,20 @@ import {
|
|||
searchReducer,
|
||||
walletReducer
|
||||
} from 'lbry-redux';
|
||||
import settingsReducer from './redux/reducers/settings';
|
||||
import moment from 'moment';
|
||||
import { authReducer, rewardsReducer, userReducer } from 'lbryinc';
|
||||
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
|
||||
import { createLogger } from 'redux-logger';
|
||||
import { StackNavigator, addNavigationHelpers } from 'react-navigation';
|
||||
import { AppNavigator } from './component/AppNavigator';
|
||||
import { persistStore, autoRehydrate } from 'redux-persist';
|
||||
import { reactNavigationMiddleware } from './utils/redux';
|
||||
import AppWithNavigationState from './component/AppNavigator';
|
||||
import FilesystemStorage from 'redux-persist-filesystem-storage';
|
||||
import createCompressor from 'redux-persist-transform-compress';
|
||||
import createFilter from 'redux-persist-transform-filter';
|
||||
import moment from 'moment';
|
||||
import settingsReducer from './redux/reducers/settings';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
function isFunction(object) {
|
||||
return typeof object === 'function';
|
||||
|
@ -69,17 +69,21 @@ const navigatorReducer = (state = initialNavState, action) => {
|
|||
};
|
||||
|
||||
const reducers = combineReducers({
|
||||
auth: authReducer,
|
||||
claims: claimsReducer,
|
||||
costInfo: costInfoReducer,
|
||||
fileInfo: fileInfoReducer,
|
||||
notifications: notificationsReducer,
|
||||
search: searchReducer,
|
||||
wallet: walletReducer,
|
||||
nav: navigatorReducer,
|
||||
settings: settingsReducer
|
||||
notifications: notificationsReducer,
|
||||
rewards: rewardsReducer,
|
||||
settings: settingsReducer,
|
||||
search: searchReducer,
|
||||
user: userReducer,
|
||||
wallet: walletReducer
|
||||
});
|
||||
|
||||
const bulkThunk = createBulkThunkMiddleware();
|
||||
const logger = createLogger({ collapsed: true });
|
||||
const middleware = [thunk, bulkThunk, reactNavigationMiddleware];
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
|
@ -93,20 +97,22 @@ const store = createStore(
|
|||
applyMiddleware(...middleware)
|
||||
)
|
||||
);
|
||||
window.store = store;
|
||||
|
||||
const compressor = createCompressor();
|
||||
const authFilter = createFilter('auth', ['authToken']);
|
||||
const saveClaimsFilter = createFilter('claims', ['byId', 'claimsByUri']);
|
||||
const subscriptionsFilter = createFilter('subscriptions', ['subscriptions']);
|
||||
const settingsFilter = createFilter('settings', ['clientSettings']);
|
||||
const walletFilter = createFilter('wallet', ['receiveAddress']);
|
||||
|
||||
const persistOptions = {
|
||||
whitelist: ['claims', 'subscriptions', 'settings', 'wallet'],
|
||||
whitelist: ['auth', 'claims', 'subscriptions', 'settings', 'wallet'],
|
||||
// Order is important. Needs to be compressed last or other transforms can't
|
||||
// read the data
|
||||
transforms: [saveClaimsFilter, subscriptionsFilter, settingsFilter, walletFilter, compressor],
|
||||
transforms: [authFilter, saveClaimsFilter, subscriptionsFilter, settingsFilter, walletFilter, compressor],
|
||||
debounce: 10000,
|
||||
storage: AsyncStorage
|
||||
storage: FilesystemStorage
|
||||
};
|
||||
|
||||
persistStore(store, persistOptions, err => {
|
||||
|
|
|
@ -11,11 +11,11 @@ class AboutPage extends React.PureComponent {
|
|||
lbryId: null,
|
||||
versionInfo: null
|
||||
};
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
if (NativeModules.VersionInfo) {
|
||||
NativeModules.VersionInfo.getAppVersion().then(version => {
|
||||
this.setState({appVersion: version});
|
||||
this.setState({appVersion: version});
|
||||
});
|
||||
}
|
||||
Lbry.version().then(info => {
|
||||
|
@ -23,19 +23,19 @@ class AboutPage extends React.PureComponent {
|
|||
versionInfo: info,
|
||||
});
|
||||
});
|
||||
Lbry.status({ session_status: true }).then(info => {
|
||||
Lbry.status().then(info => {
|
||||
this.setState({
|
||||
lbryId: info.lbry_id,
|
||||
lbryId: info.installation_id,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const loading = 'Loading...';
|
||||
const ver = this.state.versionInfo ? this.state.versionInfo : null;
|
||||
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={aboutStyle.container}>
|
||||
<PageHeader title={"About LBRY"}
|
||||
onBackPressed={() => { this.props.navigation.goBack(); }} />
|
||||
<ScrollView style={aboutStyle.scrollContainer}>
|
||||
|
@ -50,27 +50,38 @@ class AboutPage extends React.PureComponent {
|
|||
<Link style={aboutStyle.link} href="https://lbry.io/faq/what-is-lbry" text="What is LBRY?" />
|
||||
<Link style={aboutStyle.link} href="https://lbry.io/faq" text="Frequently Asked Questions" />
|
||||
</View>
|
||||
<Text style={aboutStyle.socialTitle}>Get Social</Text>
|
||||
<Text style={aboutStyle.paragraph}>
|
||||
You can interact with the LBRY team and members of the community on Discord, Facebook, Instagram, Twitter or Reddit.
|
||||
</Text>
|
||||
<View style={aboutStyle.links}>
|
||||
<Link style={aboutStyle.link} href="https://discordapp.com/invite/Z3bERWA" text="Discord" />
|
||||
<Link style={aboutStyle.link} href="https://www.facebook.com/LBRYio" text="Facebook" />
|
||||
<Link style={aboutStyle.link} href="https://www.instagram.com/LBRYio/" text="Instagram" />
|
||||
<Link style={aboutStyle.link} href="https://twitter.com/LBRYio" text="Twitter" />
|
||||
<Link style={aboutStyle.link} href="https://reddit.com/r/lbry" text="Reddit" />
|
||||
</View>
|
||||
<Text style={aboutStyle.releaseInfoTitle}>Release information</Text>
|
||||
<View style={aboutStyle.row}>
|
||||
<View style={aboutStyle.col}><Text style={aboutStyle.text}>App version</Text></View>
|
||||
<View style={aboutStyle.col}><Text selectable={true} style={aboutStyle.valueText}>{this.state.appVersion}</Text></View>
|
||||
</View>
|
||||
|
||||
|
||||
<View style={aboutStyle.row}>
|
||||
<View style={aboutStyle.col}><Text style={aboutStyle.text}>Daemon (lbrynet)</Text></View>
|
||||
<View style={aboutStyle.col}><Text selectable={true} style={aboutStyle.valueText}>{ver ? ver.lbrynet_version : loading }</Text></View>
|
||||
</View>
|
||||
|
||||
|
||||
<View style={aboutStyle.row}>
|
||||
<View style={aboutStyle.col}><Text style={aboutStyle.text}>Wallet (lbryum)</Text></View>
|
||||
<View style={aboutStyle.col}><Text selectable={true} style={aboutStyle.valueText}>{ver ? ver.lbryum_version : loading }</Text></View>
|
||||
</View>
|
||||
|
||||
|
||||
<View style={aboutStyle.row}>
|
||||
<View style={aboutStyle.col}><Text style={aboutStyle.text}>Platform</Text></View>
|
||||
<View style={aboutStyle.col}><Text selectable={true} style={aboutStyle.valueText}>{ver ? ver.platform : loading }</Text></View>
|
||||
</View>
|
||||
|
||||
|
||||
<View style={aboutStyle.row}>
|
||||
<View style={aboutStyle.col}>
|
||||
<Text style={aboutStyle.text}>Installation ID</Text>
|
||||
|
|
|
@ -14,7 +14,6 @@ import FileItem from '../../component/fileItem';
|
|||
import discoverStyle from '../../styles/discover';
|
||||
import Colors from '../../styles/colors';
|
||||
import UriBar from '../../component/uriBar';
|
||||
import Feather from 'react-native-vector-icons/Feather';
|
||||
|
||||
class DiscoverPage extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
|
|
|
@ -3,7 +3,6 @@ import { Lbry, normalizeURI } from 'lbry-redux';
|
|||
import {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
Button,
|
||||
Dimensions,
|
||||
NativeModules,
|
||||
ScrollView,
|
||||
|
@ -16,6 +15,7 @@ import {
|
|||
WebView
|
||||
} from 'react-native';
|
||||
import ImageViewer from 'react-native-image-zoom-viewer';
|
||||
import Button from '../../component/button';
|
||||
import Colors from '../../styles/colors';
|
||||
import ChannelPage from '../channel';
|
||||
import FileDownloadButton from '../../component/fileDownloadButton';
|
||||
|
@ -99,9 +99,17 @@ class FilePage extends React.PureComponent {
|
|||
if (mode) {
|
||||
// fullscreen, so change orientation to landscape mode
|
||||
NativeModules.ScreenOrientation.lockOrientationLandscape();
|
||||
if (NativeModules.UtilityModule) {
|
||||
// hide the navigation bar (on devices that use have soft navigation bar)
|
||||
NativeModules.UtilityModule.hideNavigationBar();
|
||||
}
|
||||
} else {
|
||||
// Switch back to portrait mode when the media is not fullscreen
|
||||
NativeModules.ScreenOrientation.lockOrientationPortrait();
|
||||
if (NativeModules.UtilityModule) {
|
||||
// hide the navigation bar (on devices that use have soft navigation bar)
|
||||
NativeModules.UtilityModule.showNavigationBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,9 +146,11 @@ class FilePage extends React.PureComponent {
|
|||
StatusBar.setHidden(false);
|
||||
if (NativeModules.ScreenOrientation) {
|
||||
NativeModules.ScreenOrientation.unlockOrientation();
|
||||
}
|
||||
}
|
||||
if (NativeModules.UtilityModule) {
|
||||
NativeModules.UtilityModule.keepAwakeOff();
|
||||
const utility = NativeModules.UtilityModule;
|
||||
utility.keepAwakeOff();
|
||||
utility.showNavigationBar();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,9 +332,16 @@ class FilePage extends React.PureComponent {
|
|||
|
||||
{ showActions &&
|
||||
<View style={filePageStyle.actions}>
|
||||
{completed && <Button color="red" title="Delete" onPress={this.onDeletePressed} />}
|
||||
{completed && <Button style={filePageStyle.actionButton}
|
||||
theme={"light"}
|
||||
icon={"trash"}
|
||||
text={"Delete"}
|
||||
onPress={this.onDeletePressed} />}
|
||||
{!completed && fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes &&
|
||||
<Button color="red" title="Stop Download" onPress={this.onStopDownloadPressed} />
|
||||
<Button style={filePageStyle.actionButton}
|
||||
theme={"light"}
|
||||
text={"Stop Download"}
|
||||
onPress={this.onStopDownloadPressed} />
|
||||
}
|
||||
</View>}
|
||||
<ScrollView style={showActions ? filePageStyle.scrollContainerActions : filePageStyle.scrollContainer}>
|
||||
|
@ -336,6 +353,9 @@ class FilePage extends React.PureComponent {
|
|||
const channelUri = normalizeURI(channelName);
|
||||
navigation.navigate({ routeName: 'File', key: channelUri, params: { uri: channelUri }});
|
||||
}} />}
|
||||
|
||||
{description && description.length > 0 && <View style={filePageStyle.divider} />}
|
||||
|
||||
{description && <Text style={filePageStyle.description} selectable={true}>{this.linkify(description)}</Text>}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
|
|
@ -1,6 +1,28 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doNotify } from 'lbry-redux';
|
||||
import {
|
||||
doGenerateAuthToken,
|
||||
doUserEmailNew,
|
||||
selectAuthToken,
|
||||
selectEmailNewErrorMessage,
|
||||
selectEmailNewIsPending,
|
||||
selectEmailToVerify,
|
||||
selectIsAuthenticating
|
||||
} from 'lbryinc';
|
||||
import FirstRun from './view';
|
||||
|
||||
const perform = dispatch => ({});
|
||||
const select = (state) => ({
|
||||
authenticating: selectIsAuthenticating(state),
|
||||
authToken: selectAuthToken(state),
|
||||
emailToVerify: selectEmailToVerify(state),
|
||||
emailNewErrorMessage: selectEmailNewErrorMessage(state),
|
||||
emailNewPending: selectEmailNewIsPending(state),
|
||||
});
|
||||
|
||||
export default connect(null, perform)(FirstRun);
|
||||
const perform = dispatch => ({
|
||||
addUserEmail: email => dispatch(doUserEmailNew(email)),
|
||||
generateAuthToken: installationId => dispatch(doGenerateAuthToken(installationId)),
|
||||
notify: data => dispatch(doNotify(data))
|
||||
});
|
||||
|
||||
export default connect(select, perform)(FirstRun);
|
113
app/src/page/firstRun/internal/email-collect-page.js
Normal file
113
app/src/page/firstRun/internal/email-collect-page.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
AsyncStorage,
|
||||
Linking,
|
||||
Text,
|
||||
TextInput,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Colors from '../../../styles/colors';
|
||||
import Constants from '../../../constants';
|
||||
import firstRunStyle from '../../../styles/firstRun';
|
||||
|
||||
class EmailCollectPage extends React.PureComponent {
|
||||
static MAX_STATUS_TRIES = 15;
|
||||
|
||||
state = {
|
||||
email: null,
|
||||
authenticationStarted: false,
|
||||
authenticationFailed: false,
|
||||
statusTries: 0
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { authenticating, authToken } = this.props;
|
||||
|
||||
if (this.state.authenticationStarted && !authenticating && authToken === null) {
|
||||
this.setState({ authenticationFailed: true, authenticationStarted: false });
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// call user/new
|
||||
const { generateAuthToken, authenticating, authToken } = this.props;
|
||||
if (!authToken && !authenticating) {
|
||||
this.startAuthenticating();
|
||||
}
|
||||
|
||||
AsyncStorage.getItem('firstRunEmail').then(email => {
|
||||
if (email) {
|
||||
this.setState({ email });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
startAuthenticating = () => {
|
||||
const { generateAuthToken } = this.props;
|
||||
this.setState({ authenticationStarted: true, authenticationFailed: false });
|
||||
Lbry.status().then(info => {
|
||||
generateAuthToken(info.installation_id)
|
||||
}).catch(error => {
|
||||
if (this.state.statusTries >= EmailCollectPage.MAX_STATUS_TRIES) {
|
||||
this.setState({ authenticationFailed: true });
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.startAuthenticating();
|
||||
this.setState({ statusTries: this.state.statusTries + 1 });
|
||||
}, 1000); // Retry every second for a maximum of MAX_STATUS_TRIES tries (15 seconds)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleChangeText = (text) => {
|
||||
// save the value to the state email
|
||||
this.setState({ email: text });
|
||||
AsyncStorage.setItem(Constants.KEY_FIRST_RUN_EMAIL, text);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { authenticating, authToken, onEmailViewLayout, emailToVerify } = this.props;
|
||||
|
||||
let content;
|
||||
if (this.state.authenticationFailed) {
|
||||
// Ask the user to try again
|
||||
content = (
|
||||
<View>
|
||||
<Text style={firstRunStyle.paragraph}>The LBRY servers were unreachable at this time. Please check your Internet connection and then restart the app to try again.</Text>
|
||||
</View>
|
||||
);
|
||||
} else if (!authToken || authenticating) {
|
||||
content = (
|
||||
<View>
|
||||
<ActivityIndicator size="large" color={Colors.White} style={firstRunStyle.waiting} />
|
||||
<Text style={firstRunStyle.paragraph}>Please wait while we get some things ready...</Text>
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<View onLayout={onEmailViewLayout}>
|
||||
<Text style={firstRunStyle.title}>Rewards.</Text>
|
||||
<Text style={firstRunStyle.paragraph}>You can earn LBRY Credits (LBC) rewards by completing various tasks in the app.</Text>
|
||||
<Text style={firstRunStyle.paragraph}>Please provide a valid email address below to be able to claim your rewards.</Text>
|
||||
<TextInput style={firstRunStyle.emailInput}
|
||||
placeholder="you@example.com"
|
||||
underlineColorAndroid="transparent"
|
||||
value={this.state.email}
|
||||
onChangeText={text => this.handleChangeText(text)}
|
||||
/>
|
||||
<Text style={firstRunStyle.infoParagraph}>This information is disclosed only to LBRY, Inc. and not to the LBRY network. It is only required to earn LBRY rewards and may be used to sync usage data across devices.</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={firstRunStyle.container}>
|
||||
{content}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default EmailCollectPage;
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
AsyncStorage,
|
||||
Linking,
|
||||
NativeModules,
|
||||
Text,
|
||||
|
@ -9,20 +11,24 @@ import {
|
|||
} from 'react-native';
|
||||
import { NavigationActions } from 'react-navigation';
|
||||
import Colors from '../../styles/colors';
|
||||
import Constants from '../../constants';
|
||||
import WelcomePage from './internal/welcome-page';
|
||||
import EmailCollectPage from './internal/email-collect-page';
|
||||
import firstRunStyle from '../../styles/firstRun';
|
||||
|
||||
class FirstRunScreen extends React.PureComponent {
|
||||
static pages = ['welcome'];
|
||||
static pages = [
|
||||
'welcome',
|
||||
'email-collect'
|
||||
];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
currentPage: null,
|
||||
launchUrl: null,
|
||||
isFirstRun: false
|
||||
}
|
||||
}
|
||||
state = {
|
||||
currentPage: null,
|
||||
emailSubmitted: false,
|
||||
isFirstRun: false,
|
||||
launchUrl: null,
|
||||
showBottomContainer: true
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
Linking.getInitialURL().then((url) => {
|
||||
|
@ -47,6 +53,21 @@ class FirstRunScreen extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { emailNewErrorMessage, emailNewPending } = nextProps;
|
||||
const { notify } = this.props;
|
||||
|
||||
if (this.state.emailSubmitted && !emailNewPending) {
|
||||
this.setState({ emailSubmitted: false });
|
||||
if (emailNewErrorMessage) {
|
||||
notify ({ message: String(emailNewErrorMessage), displayType: ['toast']});
|
||||
} else {
|
||||
// Request successful. Navigate to discover.
|
||||
this.closeFinalPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
launchSplashScreen() {
|
||||
const { navigation } = this.props;
|
||||
const resetAction = NavigationActions.reset({
|
||||
|
@ -60,36 +81,92 @@ class FirstRunScreen extends React.PureComponent {
|
|||
|
||||
handleContinuePressed = () => {
|
||||
const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage);
|
||||
if (pageIndex === (FirstRunScreen.pages.length - 1)) {
|
||||
// Final page. Let the app know that first run experience is completed.
|
||||
if (NativeModules.FirstRun) {
|
||||
NativeModules.FirstRun.firstRunCompleted();
|
||||
}
|
||||
|
||||
// Navigate to the splash screen
|
||||
this.launchSplashScreen();
|
||||
if (this.state.currentPage !== 'email-collect' &&
|
||||
pageIndex === (FirstRunScreen.pages.length - 1)) {
|
||||
this.closeFinalPage();
|
||||
} else {
|
||||
// TODO: Page transition animation?
|
||||
this.state.currentPage = FirstRunScreen.pages[pageIndex + 1];
|
||||
// TODO: Actions and page verification for specific pages
|
||||
if (this.state.currentPage === 'email-collect') {
|
||||
// handle email collect
|
||||
this.handleEmailCollectPageContinue();
|
||||
} else {
|
||||
this.showNextPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleEmailCollectPageContinue() {
|
||||
const { notify, addUserEmail } = this.props;
|
||||
// validate the email
|
||||
AsyncStorage.getItem(Constants.KEY_FIRST_RUN_EMAIL).then(email => {
|
||||
if (!email || email.trim().length === 0 || email.indexOf('@') === -1) {
|
||||
return notify({
|
||||
message: 'Please provide a valid email address to continue.',
|
||||
displayType: ['toast'],
|
||||
});
|
||||
}
|
||||
|
||||
addUserEmail(email);
|
||||
this.setState({ emailSubmitted: true });
|
||||
});
|
||||
}
|
||||
|
||||
showNextPage() {
|
||||
const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage);
|
||||
const nextPage = FirstRunScreen.pages[pageIndex + 1];
|
||||
this.setState({ currentPage: nextPage });
|
||||
if (nextPage === 'email-collect') {
|
||||
// do not show the buttons (because we're waiting to get things ready)
|
||||
this.setState({ showBottomContainer: false });
|
||||
}
|
||||
}
|
||||
|
||||
closeFinalPage() {
|
||||
// Final page. Let the app know that first run experience is completed.
|
||||
if (NativeModules.FirstRun) {
|
||||
NativeModules.FirstRun.firstRunCompleted();
|
||||
}
|
||||
|
||||
// Navigate to the splash screen
|
||||
this.launchSplashScreen();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
authenticating,
|
||||
authToken,
|
||||
generateAuthToken,
|
||||
emailNewErrorMessage,
|
||||
emailNewPending,
|
||||
emailToVerify
|
||||
} = this.props;
|
||||
|
||||
let page = null;
|
||||
if (this.state.currentPage === 'welcome') {
|
||||
// show welcome page
|
||||
page = (<WelcomePage />);
|
||||
} else if (this.state.currentPage === 'email-collect') {
|
||||
page = (<EmailCollectPage authenticating={authenticating}
|
||||
authToken={authToken}
|
||||
generateAuthToken={generateAuthToken}
|
||||
onEmailViewLayout={() => this.setState({ showBottomContainer: true })} />);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={firstRunStyle.screenContainer}>
|
||||
{page}
|
||||
{this.state.currentPage &&
|
||||
<TouchableOpacity style={firstRunStyle.button} onPress={this.handleContinuePressed}>
|
||||
<Text style={firstRunStyle.buttonText}>Continue</Text>
|
||||
</TouchableOpacity>}
|
||||
{this.state.currentPage && this.state.showBottomContainer &&
|
||||
<View style={firstRunStyle.bottomContainer}>
|
||||
{emailNewPending &&
|
||||
<ActivityIndicator size="small" color={Colors.White} style={firstRunStyle.pageWaiting} />}
|
||||
|
||||
{!emailNewPending &&
|
||||
<TouchableOpacity style={firstRunStyle.button} onPress={this.handleContinuePressed}>
|
||||
<Text style={firstRunStyle.buttonText}>Continue</Text>
|
||||
</TouchableOpacity>}
|
||||
</View>}
|
||||
</View>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import { NativeModules, Text, View, ScrollView } from 'react-native';
|
||||
import { ActivityIndicator, NativeModules, ScrollView, Text, View } from 'react-native';
|
||||
import Colors from '../../styles/colors';
|
||||
import Link from '../../component/link';
|
||||
import PageHeader from '../../component/pageHeader';
|
||||
|
@ -17,7 +17,7 @@ class RewardsPage extends React.PureComponent {
|
|||
if (user && !user.is_reward_approved) {
|
||||
if (!user.primary_email || !user.has_verified_email || !user.is_identity_verified) {
|
||||
return (
|
||||
<View style={rewardStyle.card}>
|
||||
<View style={[rewardStyle.card, rewardStyle.verification]}>
|
||||
<Text style={rewardStyle.title}>Humans Only</Text>
|
||||
<Text style={rewardStyle.text}>Rewards are for human beings only. You'll have to prove you're one of us before you can claim any rewards.</Text>
|
||||
</View>
|
||||
|
@ -25,12 +25,11 @@ class RewardsPage extends React.PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={[rewardStyle.card, rewardStyle.verification]}>
|
||||
<Text style={rewardStyle.text}>This account must undergo review.</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
console.log(user);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -38,9 +37,6 @@ class RewardsPage extends React.PureComponent {
|
|||
renderUnclaimedRewards() {
|
||||
const { fetching, rewards, user, claimed } = this.props;
|
||||
|
||||
console.log(fetching);
|
||||
console.log(rewards);
|
||||
|
||||
if (fetching) {
|
||||
return (
|
||||
<View style={rewardStyle.busyContainer}>
|
||||
|
@ -68,19 +64,19 @@ class RewardsPage extends React.PureComponent {
|
|||
const isNotEligible =
|
||||
!user || !user.primary_email || !user.has_verified_email || !user.is_reward_approved;
|
||||
return (
|
||||
<View>
|
||||
<ScrollView contentContainerStyle={rewardStyle.scrollContentContainer}>
|
||||
{rewards.map(reward => <RewardCard key={reward.reward_type} reward={reward} />)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<ScrollView style={rewardStyle.scrollContainer}>
|
||||
{this.renderVerification()}
|
||||
<View style={rewardStyle.container}>
|
||||
{this.renderVerification()}
|
||||
<View style={rewardStyle.rewardsContainer}>
|
||||
{this.renderUnclaimedRewards()}
|
||||
</ScrollView>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { SETTINGS } from 'lbry-redux';
|
||||
import { Text, View, ScrollView, Switch } from 'react-native';
|
||||
import { Text, View, ScrollView, Switch, NativeModules } from 'react-native';
|
||||
import PageHeader from '../../component/pageHeader';
|
||||
import settingsStyle from '../../styles/settings';
|
||||
|
||||
|
@ -8,7 +8,7 @@ class SettingsPage extends React.PureComponent {
|
|||
static navigationOptions = {
|
||||
title: 'Settings'
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const {
|
||||
backgroundPlayEnabled,
|
||||
|
@ -16,10 +16,10 @@ class SettingsPage extends React.PureComponent {
|
|||
showNsfw,
|
||||
setClientSetting
|
||||
} = this.props;
|
||||
|
||||
// If no true / false value set, default to true
|
||||
const actualKeepDaemonRunning = (keepDaemonRunning === undefined || keepDaemonRunning === null) ? true : keepDaemonRunning;
|
||||
|
||||
|
||||
// default to true if the setting is null or undefined
|
||||
const actualKeepDaemonRunning = (keepDaemonRunning === null || keepDaemonRunning === undefined) ? true : keepDaemonRunning;
|
||||
|
||||
return (
|
||||
<View>
|
||||
<PageHeader title={"Settings"}
|
||||
|
@ -34,7 +34,7 @@ class SettingsPage extends React.PureComponent {
|
|||
<Switch value={backgroundPlayEnabled} onValueChange={(value) => setClientSetting(SETTINGS.BACKGROUND_PLAY_ENABLED, value)} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
||||
<View style={settingsStyle.row}>
|
||||
<View style={settingsStyle.switchText}>
|
||||
<Text style={settingsStyle.label}>Show NSFW content</Text>
|
||||
|
@ -43,14 +43,19 @@ class SettingsPage extends React.PureComponent {
|
|||
<Switch value={showNsfw} onValueChange={(value) => setClientSetting(SETTINGS.SHOW_NSFW, value)} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
||||
<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.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}>
|
||||
<Switch value={actualKeepDaemonRunning} onValueChange={(value) => setClientSetting(SETTINGS.KEEP_DAEMON_RUNNING, value)} />
|
||||
<Switch value={actualKeepDaemonRunning} onValueChange={(value) => {
|
||||
setClientSetting(SETTINGS.KEEP_DAEMON_RUNNING, value);
|
||||
if (NativeModules.DaemonServiceControl) {
|
||||
NativeModules.DaemonServiceControl.setKeepDaemonRunning(value);
|
||||
}
|
||||
}} />
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doBalanceSubscribe } from 'lbry-redux';
|
||||
import { doBalanceSubscribe, doNotify } from 'lbry-redux';
|
||||
import { doAuthenticate, doUserEmailVerify, doUserEmailVerifyFailure, selectUser } from 'lbryinc';
|
||||
import SplashScreen from './view';
|
||||
|
||||
const perform = dispatch => ({
|
||||
balanceSubscribe: () => dispatch(doBalanceSubscribe())
|
||||
const select = state => ({
|
||||
user: selectUser(state),
|
||||
});
|
||||
|
||||
export default connect(null, perform)(SplashScreen);
|
||||
const perform = dispatch => ({
|
||||
authenticate: (appVersion, deviceId) => dispatch(doAuthenticate(appVersion, deviceId)),
|
||||
balanceSubscribe: () => dispatch(doBalanceSubscribe()),
|
||||
notify: data => dispatch(doNotify(data)),
|
||||
verifyUserEmail: (token, recaptcha) => dispatch(doUserEmailVerify(token, recaptcha)),
|
||||
verifyUserEmailFailure: error => dispatch(doUserEmailVerifyFailure(error)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(SplashScreen);
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import { Lbry } from 'lbry-redux';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
AsyncStorage,
|
||||
Linking,
|
||||
NativeModules,
|
||||
Platform,
|
||||
|
@ -10,8 +11,10 @@ import {
|
|||
View
|
||||
} from 'react-native';
|
||||
import { NavigationActions } from 'react-navigation';
|
||||
import { decode as atob } from 'base-64';
|
||||
import PropTypes from 'prop-types';
|
||||
import Colors from '../../styles/colors';
|
||||
import Constants from '../../constants';
|
||||
import splashStyle from '../../styles/splash';
|
||||
|
||||
class SplashScreen extends React.PureComponent {
|
||||
|
@ -21,14 +24,15 @@ class SplashScreen extends React.PureComponent {
|
|||
|
||||
componentWillMount() {
|
||||
this.setState({
|
||||
daemonReady: false,
|
||||
details: 'Starting daemon',
|
||||
message: 'Connecting',
|
||||
isRunning: false,
|
||||
isLagging: false,
|
||||
launchUrl: null,
|
||||
didDownloadHeaders: false,
|
||||
isDownloadingHeaders: false,
|
||||
headersDownloadProgress: 0
|
||||
headersDownloadProgress: 0,
|
||||
shouldAuthenticate: false
|
||||
});
|
||||
|
||||
if (NativeModules.DaemonServiceControl) {
|
||||
|
@ -53,14 +57,61 @@ class SplashScreen extends React.PureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
const { navigation, verifyUserEmail, verifyUserEmailFailure } = this.props;
|
||||
const { user } = nextProps;
|
||||
if (this.state.daemonReady && this.state.shouldAuthenticate && user && user.id) {
|
||||
// user is authenticated, navigate to the main view
|
||||
const resetAction = NavigationActions.reset({
|
||||
index: 0,
|
||||
actions: [
|
||||
NavigationActions.navigate({ routeName: 'Main'})
|
||||
]
|
||||
});
|
||||
navigation.dispatch(resetAction);
|
||||
|
||||
const launchUrl = navigation.state.params.launchUrl || this.state.launchUrl;
|
||||
if (launchUrl) {
|
||||
if (launchUrl.startsWith('lbry://?verify=')) {
|
||||
let verification = {};
|
||||
try {
|
||||
verification = JSON.parse(atob(launchUrl.substring(15)));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
if (verification.token && verification.recaptcha) {
|
||||
AsyncStorage.setItem(Constants.KEY_SHOULD_VERIFY_EMAIL, 'true');
|
||||
try {
|
||||
verifyUserEmail(verification.token, verification.recaptcha);
|
||||
} catch (error) {
|
||||
const message = 'Invalid Verification Token';
|
||||
verifyUserEmailFailure(message);
|
||||
notify({ message, displayType: ['toast'] });
|
||||
}
|
||||
} else {
|
||||
notify({
|
||||
message: 'Invalid Verification URI',
|
||||
displayType: ['toast'],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
navigation.navigate({ routeName: 'File', key: launchUrl, params: { uri: launchUrl } });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_updateStatusCallback(status) {
|
||||
const startupStatus = status.startup_status;
|
||||
if (startupStatus.code == 'started') {
|
||||
// At the minimum, wallet should be started and blocks_behind equal to 0 before calling resolve
|
||||
const hasStarted = startupStatus.wallet && status.wallet.blocks_behind <= 0;
|
||||
if (hasStarted) {
|
||||
// Wait until we are able to resolve a name before declaring
|
||||
// that we are done.
|
||||
// TODO: This is a hack, and the logic should live in the daemon
|
||||
// to give us a better sense of when we are actually started
|
||||
this.setState({
|
||||
daemonReady: true,
|
||||
message: 'Testing Network',
|
||||
details: 'Waiting for name resolution',
|
||||
isLagging: false,
|
||||
|
@ -69,56 +120,61 @@ class SplashScreen extends React.PureComponent {
|
|||
|
||||
Lbry.resolve({ uri: 'lbry://one' }).then(() => {
|
||||
// Leave the splash screen
|
||||
const { balanceSubscribe, navigation } = this.props;
|
||||
const {
|
||||
authenticate,
|
||||
balanceSubscribe,
|
||||
navigation,
|
||||
notify
|
||||
} = this.props;
|
||||
|
||||
balanceSubscribe();
|
||||
|
||||
const resetAction = NavigationActions.reset({
|
||||
index: 0,
|
||||
actions: [
|
||||
NavigationActions.navigate({ routeName: 'Main'})
|
||||
]
|
||||
NativeModules.VersionInfo.getAppVersion().then(appVersion => {
|
||||
if (NativeModules.UtilityModule) {
|
||||
// authenticate with the device ID if the method is available
|
||||
NativeModules.UtilityModule.getDeviceId().then(deviceId => {
|
||||
authenticate(`android-${appVersion}`, deviceId);
|
||||
});
|
||||
} else {
|
||||
authenticate(appVersion);
|
||||
}
|
||||
this.setState({ shouldAuthenticate: true });
|
||||
});
|
||||
navigation.dispatch(resetAction);
|
||||
|
||||
const launchUrl = navigation.state.params.launchUrl || this.state.launchUrl;
|
||||
if (launchUrl) {
|
||||
navigation.navigate({ routeName: 'File', key: launchUrl, params: { uri: launchUrl } });
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const blockchainStatus = status.blockchain_status;
|
||||
if (blockchainStatus) {
|
||||
const blockchainHeaders = status.blockchain_headers;
|
||||
const walletStatus = status.wallet;
|
||||
|
||||
if (blockchainHeaders) {
|
||||
this.setState({
|
||||
isDownloadingHeaders: blockchainStatus.is_downloading_headers,
|
||||
headersDownloadProgress: blockchainStatus.headers_download_progress
|
||||
isDownloadingHeaders: blockchainHeaders.downloading_headers,
|
||||
headersDownloadProgress: blockchainHeaders.download_progress
|
||||
});
|
||||
} else {
|
||||
// set downloading flag to false if blockchain_headers isn't in the status response
|
||||
this.setState({
|
||||
isDownloadingHeaders: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (blockchainStatus && (blockchainStatus.is_downloading_headers ||
|
||||
(this.state.didDownloadHeaders && 'loading_wallet' === startupStatus.code))) {
|
||||
if (!this.state.didDownloadHeaders) {
|
||||
this.setState({ didDownloadHeaders: true });
|
||||
}
|
||||
if (blockchainHeaders && blockchainHeaders.downloading_headers) {
|
||||
const downloadProgress = blockchainHeaders.download_progress ? blockchainHeaders.download_progress : 0;
|
||||
this.setState({
|
||||
message: 'Blockchain Sync',
|
||||
details: `Catching up with the blockchain (${blockchainStatus.headers_download_progress}%)`,
|
||||
isLagging: startupStatus.is_lagging
|
||||
details: `Catching up with the blockchain (${downloadProgress}%)`,
|
||||
});
|
||||
} else if (blockchainStatus && blockchainStatus.blocks_behind > 0) {
|
||||
const behind = blockchainStatus.blocks_behind;
|
||||
} else if (walletStatus && walletStatus.blocks_behind > 0) {
|
||||
const behind = walletStatus.blocks_behind;
|
||||
const behindText = behind + ' block' + (behind == 1 ? '' : 's') + ' behind';
|
||||
this.setState({
|
||||
message: 'Blockchain Sync',
|
||||
details: behindText,
|
||||
isLagging: startupStatus.is_lagging,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
message: 'Network Loading',
|
||||
details: startupStatus.message + (startupStatus.is_lagging ? '' : '...'),
|
||||
isLagging: startupStatus.is_lagging,
|
||||
details: 'Initializing LBRY service...'
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
|
|
|
@ -14,7 +14,6 @@ import FileItem from '../../component/fileItem';
|
|||
import discoverStyle from '../../styles/discover';
|
||||
import Colors from '../../styles/colors';
|
||||
import UriBar from '../../component/uriBar';
|
||||
import Feather from 'react-native-vector-icons/Feather';
|
||||
|
||||
class TrendingPage extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
|
|
|
@ -9,3 +9,4 @@ export function doSetClientSetting(key, value) {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ reducers[ACTIONS.CLIENT_SETTING_CHANGED] = (state, action) => {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
export default function reducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
if (handler) return handler(state, action);
|
||||
|
|
|
@ -2,9 +2,11 @@ import { StyleSheet } from 'react-native';
|
|||
import Colors from './colors';
|
||||
|
||||
const aboutStyle = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1
|
||||
},
|
||||
scrollContainer: {
|
||||
paddingTop: 16,
|
||||
paddingBottom: 16
|
||||
flex: 1
|
||||
},
|
||||
row: {
|
||||
marginBottom: 1,
|
||||
|
@ -18,6 +20,7 @@ const aboutStyle = StyleSheet.create({
|
|||
color: Colors.LbryGreen,
|
||||
fontSize: 24,
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
marginTop: 16,
|
||||
marginLeft: 12,
|
||||
marginRight: 12,
|
||||
marginBottom: 8
|
||||
|
@ -33,7 +36,7 @@ const aboutStyle = StyleSheet.create({
|
|||
links: {
|
||||
marginLeft: 12,
|
||||
marginRight: 12,
|
||||
marginBottom: 12
|
||||
marginBottom: 18
|
||||
},
|
||||
link: {
|
||||
color: Colors.LbryGreen,
|
||||
|
@ -44,6 +47,13 @@ const aboutStyle = StyleSheet.create({
|
|||
col: {
|
||||
alignSelf: 'stretch'
|
||||
},
|
||||
socialTitle: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
marginLeft: 12,
|
||||
marginRight: 12,
|
||||
marginBottom: 8,
|
||||
fontSize: 20
|
||||
},
|
||||
releaseInfoTitle: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
marginLeft: 12,
|
||||
|
|
|
@ -1,24 +1,34 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
import Colors from './colors';
|
||||
|
||||
const buttonStyle = StyleSheet.create({
|
||||
button: {
|
||||
borderRadius: 24,
|
||||
padding: 8,
|
||||
paddingLeft: 12,
|
||||
paddingRight: 12
|
||||
paddingRight: 12,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
disabled: {
|
||||
backgroundColor: '#999999'
|
||||
},
|
||||
row: {
|
||||
alignSelf: 'flex-start',
|
||||
flexDirection: 'row',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
icon: {
|
||||
color: '#ffffff',
|
||||
iconLight: {
|
||||
color: Colors.White,
|
||||
},
|
||||
iconDark: {
|
||||
color: Colors.DarkGrey,
|
||||
},
|
||||
textLight: {
|
||||
color: Colors.White,
|
||||
},
|
||||
textDark: {
|
||||
color: Colors.DarkGrey
|
||||
},
|
||||
text: {
|
||||
color: '#ffffff',
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 14
|
||||
},
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
const Colors = {
|
||||
Black: '#000000',
|
||||
ChannelGrey: '#9b9b9b',
|
||||
DarkGrey: '#555555',
|
||||
DescriptionGrey: '#999999',
|
||||
LbryGreen: '#40b89a',
|
||||
LightGrey: '#cccccc',
|
||||
LighterGrey: '#e5e5e5',
|
||||
Orange: '#ffbb00',
|
||||
Red: '#ff0000',
|
||||
VeryLightGrey: '#f1f1f1',
|
||||
|
|
|
@ -12,6 +12,13 @@ const filePageStyle = StyleSheet.create({
|
|||
pageContainer: {
|
||||
flex: 1
|
||||
},
|
||||
divider: {
|
||||
backgroundColor: Colors.LighterGrey,
|
||||
width: '100%',
|
||||
height: 1,
|
||||
marginTop: 4,
|
||||
marginBottom: 20
|
||||
},
|
||||
innerPageContainer: {
|
||||
flex: 1,
|
||||
marginBottom: 60
|
||||
|
@ -23,7 +30,7 @@ const filePageStyle = StyleSheet.create({
|
|||
mediaContainer: {
|
||||
alignItems: 'center',
|
||||
width: screenWidth,
|
||||
height: 220
|
||||
height: 240
|
||||
},
|
||||
emptyClaimText: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
|
@ -68,7 +75,7 @@ const filePageStyle = StyleSheet.create({
|
|||
},
|
||||
thumbnail: {
|
||||
width: screenWidth,
|
||||
height: 204,
|
||||
height: 224,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
|
@ -85,7 +92,7 @@ const filePageStyle = StyleSheet.create({
|
|||
},
|
||||
containedPlayer: {
|
||||
width: '100%',
|
||||
height: 220,
|
||||
height: 240,
|
||||
},
|
||||
containedPlayerLandscape: {
|
||||
width: '100%',
|
||||
|
@ -139,9 +146,9 @@ const filePageStyle = StyleSheet.create({
|
|||
marginTop: -14,
|
||||
width: '50%',
|
||||
},
|
||||
deleteButton: {
|
||||
backgroundColor: Colors.Red,
|
||||
width: 80
|
||||
actionButton: {
|
||||
backgroundColor: Colors.White,
|
||||
width: 160
|
||||
},
|
||||
loading: {
|
||||
position: 'absolute',
|
||||
|
|
|
@ -28,16 +28,56 @@ const firstRunStyle = StyleSheet.create({
|
|||
marginBottom: 20,
|
||||
color: Colors.White
|
||||
},
|
||||
button: {
|
||||
infoParagraph: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 14,
|
||||
lineHeight: 20,
|
||||
marginLeft: 32,
|
||||
marginRight: 32,
|
||||
marginBottom: 20,
|
||||
color: Colors.White
|
||||
},
|
||||
emailInput: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 24,
|
||||
lineHeight: 24,
|
||||
marginLeft: 32,
|
||||
marginRight: 32,
|
||||
marginBottom: 20,
|
||||
textAlign: 'center'
|
||||
},
|
||||
leftButton: {
|
||||
flex: 1,
|
||||
alignSelf: 'flex-start',
|
||||
marginLeft: 32,
|
||||
marginRight: 32
|
||||
},
|
||||
bottomContainer: {
|
||||
flex: 1
|
||||
},
|
||||
actionButton: {
|
||||
backgroundColor: Colors.White,
|
||||
alignSelf: 'center',
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 12,
|
||||
paddingLeft: 16,
|
||||
paddingRight: 16
|
||||
},
|
||||
button: {
|
||||
alignSelf: 'flex-end',
|
||||
marginLeft: 32,
|
||||
marginRight: 32
|
||||
},
|
||||
buttonText: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 28,
|
||||
fontSize: 24,
|
||||
color: Colors.White
|
||||
},
|
||||
waiting: {
|
||||
marginBottom: 24
|
||||
},
|
||||
pageWaiting: {
|
||||
alignSelf: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ const mediaPlayerStyle = StyleSheet.create({
|
|||
progress: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
width: '100%',
|
||||
height: 3
|
||||
},
|
||||
innerProgressCompleted: {
|
||||
|
@ -101,8 +100,13 @@ const mediaPlayerStyle = StyleSheet.create({
|
|||
position: 'absolute',
|
||||
height: 36,
|
||||
width: 48,
|
||||
bottom: 0,
|
||||
marginLeft: -16
|
||||
marginLeft: -18
|
||||
},
|
||||
seekerHandleContained: {
|
||||
bottom: -17
|
||||
},
|
||||
seekerHandleFs: {
|
||||
bottom: 0
|
||||
},
|
||||
bigSeekerCircle: {
|
||||
borderRadius: 24,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
import Colors from './colors';
|
||||
|
||||
const walletStyle = StyleSheet.create({
|
||||
scrollContainer: {
|
||||
const rewardStyle = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1
|
||||
},
|
||||
row: {
|
||||
|
@ -16,9 +16,11 @@ const walletStyle = StyleSheet.create({
|
|||
alignItems: 'center',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
infoText: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 18
|
||||
rewardsContainer: {
|
||||
flex: 1
|
||||
},
|
||||
scrollContentContainer: {
|
||||
paddingBottom: 16
|
||||
},
|
||||
card: {
|
||||
backgroundColor: Colors.White,
|
||||
|
@ -27,10 +29,21 @@ const walletStyle = StyleSheet.create({
|
|||
marginRight: 16,
|
||||
padding: 16
|
||||
},
|
||||
text: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 16,
|
||||
lineHeight: 24
|
||||
},
|
||||
infoText: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 18,
|
||||
marginLeft: 12
|
||||
},
|
||||
title: {
|
||||
fontFamily: 'Metropolis-Bold',
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
fontSize: 20,
|
||||
marginBottom: 24
|
||||
marginBottom: 8,
|
||||
color: Colors.LbryGreen
|
||||
},
|
||||
bottomMarginSmall: {
|
||||
marginBottom: 8
|
||||
|
@ -40,7 +53,43 @@ const walletStyle = StyleSheet.create({
|
|||
},
|
||||
bottomMarginLarge: {
|
||||
marginBottom: 24
|
||||
},
|
||||
leftCol: {
|
||||
width: '10%'
|
||||
},
|
||||
midCol: {
|
||||
width: '65%'
|
||||
},
|
||||
rightCol: {
|
||||
width: '15%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
rewardAmount: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 24,
|
||||
textAlign: 'center'
|
||||
},
|
||||
rewardCurrency: {
|
||||
fontFamily: 'Metropolis-Regular'
|
||||
},
|
||||
rewardTitle: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 16,
|
||||
color: Colors.LbryGreen,
|
||||
marginBottom: 4,
|
||||
},
|
||||
rewardDescription: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 14,
|
||||
lineHeight: 18
|
||||
},
|
||||
claimed: {
|
||||
color: Colors.LbryGreen,
|
||||
},
|
||||
disabled: {
|
||||
color: Colors.LightGrey
|
||||
}
|
||||
});
|
||||
|
||||
export default walletStyle;
|
||||
export default rewardStyle;
|
||||
|
|
|
@ -10,7 +10,8 @@ const searchStyle = StyleSheet.create({
|
|||
flex: 1,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: 16,
|
||||
paddingLeft: 16,
|
||||
paddingRight: 16,
|
||||
marginBottom: 60
|
||||
},
|
||||
scrollPadding: {
|
||||
|
@ -20,14 +21,14 @@ const searchStyle = StyleSheet.create({
|
|||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 16
|
||||
marginTop: 16
|
||||
},
|
||||
thumbnail: {
|
||||
width: '100%',
|
||||
height: 80
|
||||
},
|
||||
thumbnailContainer: {
|
||||
width: '25%'
|
||||
width: '25%'
|
||||
},
|
||||
detailsContainer: {
|
||||
width: '70%'
|
||||
|
|
|
@ -20,7 +20,8 @@ const walletStyle = StyleSheet.create({
|
|||
width: '85%'
|
||||
},
|
||||
button: {
|
||||
backgroundColor: Colors.LbryGreen
|
||||
backgroundColor: Colors.LbryGreen,
|
||||
alignSelf: 'flex-start'
|
||||
},
|
||||
historyList: {
|
||||
backgroundColor: '#ffffff'
|
||||
|
|
|
@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
|
|||
|
||||
# (list) Application requirements
|
||||
# comma seperated e.g. requirements = sqlite3,kivy
|
||||
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.15#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
|
||||
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, netifaces, txupnp==0.0.1a10, git+https://github.com/lbryio/lbryschema.git@v0.0.16#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
|
||||
|
||||
# (str) Custom source folders for requirements
|
||||
# Sets custom source for any requirements with recipes
|
||||
|
@ -89,7 +89,7 @@ fullscreen = 0
|
|||
android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,READ_PHONE_STATE,WRITE_EXTERNAL_STORAGE
|
||||
|
||||
# (int) Android API to use
|
||||
android.api = 26
|
||||
android.api = 27
|
||||
|
||||
# (int) Minimum API required
|
||||
android.minapi = 21
|
||||
|
@ -148,7 +148,7 @@ android.react_src = ./app
|
|||
|
||||
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
|
||||
# bootstrap)
|
||||
android.gradle_dependencies = com.android.support:appcompat-v7:23.4.0, com.facebook.react:react-native:0.55.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+
|
||||
android.gradle_dependencies = com.android.support:appcompat-v7:27.1.1, com.facebook.react:react-native:0.55.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0
|
||||
|
||||
# (str) python-for-android branch to use, defaults to master
|
||||
#p4a.branch = stable
|
||||
|
|
|
@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
|
|||
|
||||
# (list) Application requirements
|
||||
# comma seperated e.g. requirements = sqlite3,kivy
|
||||
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.15#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
|
||||
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, netifaces, txupnp==0.0.1a10, git+https://github.com/lbryio/lbryschema.git@v0.0.16#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
|
||||
|
||||
# (str) Custom source folders for requirements
|
||||
# Sets custom source for any requirements with recipes
|
||||
|
@ -89,7 +89,7 @@ fullscreen = 0
|
|||
android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,READ_PHONE_STATE,WRITE_EXTERNAL_STORAGE
|
||||
|
||||
# (int) Android API to use
|
||||
android.api = 26
|
||||
android.api = 27
|
||||
|
||||
# (int) Minimum API required
|
||||
android.minapi = 21
|
||||
|
@ -148,7 +148,7 @@ android.react_src = ./app
|
|||
|
||||
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
|
||||
# bootstrap)
|
||||
android.gradle_dependencies = com.android.support:appcompat-v7:23.4.0, com.facebook.react:react-native:0.55.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+
|
||||
android.gradle_dependencies = com.android.support:appcompat-v7:27.1.1, com.facebook.react:react-native:0.55.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0
|
||||
|
||||
# (str) python-for-android branch to use, defaults to master
|
||||
#p4a.branch = stable
|
||||
|
|
276
buildozer.spec.vagrant
Normal file
276
buildozer.spec.vagrant
Normal file
|
@ -0,0 +1,276 @@
|
|||
[app]
|
||||
|
||||
# (str) Title of your application
|
||||
title = LBRY Browser
|
||||
|
||||
# (str) Package name
|
||||
package.name = browser
|
||||
|
||||
# (str) Package domain (needed for android/ios packaging)
|
||||
package.domain = io.lbry
|
||||
|
||||
# (str) Source code where the main.py live
|
||||
source.dir = ./src/main/python
|
||||
|
||||
# (list) Source files to include (let empty to include all the files)
|
||||
source.include_exts = py,png,jpg,kv,atlas
|
||||
|
||||
# (list) List of inclusions using pattern matching
|
||||
#source.include_patterns = assets/*, res/*
|
||||
|
||||
# (list) Source files to exclude (let empty to not exclude anything)
|
||||
#source.exclude_exts = spec
|
||||
|
||||
# (list) List of directory to exclude (let empty to not exclude anything)
|
||||
#source.exclude_dirs = tests, bin
|
||||
|
||||
# (list) List of exclusions using pattern matching
|
||||
#source.exclude_patterns = license,images/*/*.jpg
|
||||
|
||||
# (str) Application versioning (method 1)
|
||||
#version = 0.1
|
||||
|
||||
# (str) Application versioning (method 2)
|
||||
version.regex = __version__ = ['"](.*)['"]
|
||||
version.filename = %(source.dir)s/main.py
|
||||
|
||||
# (list) Application requirements
|
||||
# comma seperated e.g. requirements = sqlite3,kivy
|
||||
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, netifaces, txupnp==0.0.1a10, git+https://github.com/lbryio/lbryschema.git@v0.0.16#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
|
||||
|
||||
# (str) Custom source folders for requirements
|
||||
# Sets custom source for any requirements with recipes
|
||||
# requirements.source.kivy = ../../kivy
|
||||
|
||||
# (list) Garden requirements
|
||||
#garden_requirements =
|
||||
|
||||
# (str) Presplash of the application
|
||||
#presplash.filename = %(source.dir)s/data/presplash.png
|
||||
|
||||
# (str) Icon of the application
|
||||
#icon.filename = %(source.dir)s/data/icon.png
|
||||
|
||||
# (str) Supported orientation (one of landscape, portrait or all)
|
||||
orientation = portrait
|
||||
|
||||
# (list) List of service to declare
|
||||
#services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY
|
||||
services = lbrynetservice:./lbrynetservice.py
|
||||
|
||||
#
|
||||
# OSX Specific
|
||||
#
|
||||
|
||||
#
|
||||
# author = © Copyright Info
|
||||
|
||||
# change the major version of python used by the app
|
||||
osx.python_version = 3
|
||||
|
||||
# Kivy version to use
|
||||
osx.kivy_version = 1.9.1
|
||||
|
||||
#
|
||||
# Android specific
|
||||
#
|
||||
|
||||
# (bool) Indicate if the application should be fullscreen or not
|
||||
fullscreen = 0
|
||||
|
||||
# (string) Presplash background color (for new android toolchain)
|
||||
# Supported formats are: #RRGGBB #AARRGGBB or one of the following names:
|
||||
# red, blue, green, black, white, gray, cyan, magenta, yellow, lightgray,
|
||||
# darkgray, grey, lightgrey, darkgrey, aqua, fuchsia, lime, maroon, navy,
|
||||
# olive, purple, silver, teal.
|
||||
#android.presplash_color = #FFFFFF
|
||||
|
||||
# (list) Permissions
|
||||
android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,READ_PHONE_STATE,WRITE_EXTERNAL_STORAGE
|
||||
|
||||
# (int) Android API to use
|
||||
android.api = 27
|
||||
|
||||
# (int) Minimum API required
|
||||
android.minapi = 21
|
||||
|
||||
# (int) Android SDK version to use
|
||||
android.sdk = 23
|
||||
|
||||
# (str) Android NDK version to use
|
||||
android.ndk = 13b
|
||||
|
||||
# (bool) Use --private data storage (True) or --dir public storage (False)
|
||||
#android.private_storage = True
|
||||
|
||||
# (str) Android NDK directory (if empty, it will be automatically downloaded.)
|
||||
#android.ndk_path = ~/Dev/SDKs/android-ndk-r13b
|
||||
|
||||
# (str) Android SDK directory (if empty, it will be automatically downloaded.)
|
||||
#android.sdk_path = ~/Dev/SDKs/android
|
||||
|
||||
# (str) ANT directory (if empty, it will be automatically downloaded.)
|
||||
#android.ant_path =
|
||||
|
||||
# (bool) If True, then skip trying to update the Android sdk
|
||||
# This can be useful to avoid excess Internet downloads or save time
|
||||
# when an update is due and you just want to test/build your package
|
||||
#android.skip_update = False
|
||||
|
||||
# (str) Android entry point, default is ok for Kivy-based app
|
||||
#android.entrypoint = org.renpy.android.PythonActivity
|
||||
|
||||
# (list) Pattern to whitelist for the whole project
|
||||
#android.whitelist =
|
||||
|
||||
# (str) Path to a custom whitelist file
|
||||
#android.whitelist_src =
|
||||
|
||||
# (str) Path to a custom blacklist file
|
||||
#android.blacklist_src =
|
||||
|
||||
# (list) List of Java .jar files to add to the libs so that pyjnius can access
|
||||
# their classes. Don't add jars that you do not need, since extra jars can slow
|
||||
# down the build process. Allows wildcards matching, for example:
|
||||
# OUYA-ODK/libs/*.jar
|
||||
#android.add_jars = foo.jar,bar.jar,path/to/more/*.jar
|
||||
|
||||
# (list) List of Java files to add to the android project (can be java or a
|
||||
# directory containing the files)
|
||||
android.add_src = ./src/main
|
||||
|
||||
# (str) Path to the React files and node_modules for building
|
||||
android.react_src = ./app
|
||||
|
||||
# (list) Android AAR archives to add (currently works only with sdl2_gradle
|
||||
# bootstrap)
|
||||
#android.add_aars =
|
||||
|
||||
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
|
||||
# bootstrap)
|
||||
android.gradle_dependencies = com.android.support:appcompat-v7:27.1.1, com.facebook.react:react-native:0.55.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0
|
||||
|
||||
# (str) python-for-android branch to use, defaults to master
|
||||
#p4a.branch = stable
|
||||
|
||||
# (str) OUYA Console category. Should be one of GAME or APP
|
||||
# If you leave this blank, OUYA support will not be enabled
|
||||
#android.ouya.category = GAME
|
||||
|
||||
# (str) Filename of OUYA Console icon. It must be a 732x412 png image.
|
||||
#android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png
|
||||
|
||||
# (str) XML file to include as an intent filters in <activity> tag
|
||||
#android.manifest.intent_filters =
|
||||
|
||||
# (list) Android additionnal libraries to copy into libs/armeabi
|
||||
#android.add_libs_armeabi = libs/android-v7/*
|
||||
#android.add_libs_armeabi_v7a = libs/android-v7/*
|
||||
#android.add_libs_x86 = libs/android-x86/*.so
|
||||
#android.add_libs_mips = libs/android-mips/*.so
|
||||
|
||||
# (bool) Indicate whether the screen should stay on
|
||||
# Don't forget to add the WAKE_LOCK permission if you set this to True
|
||||
#android.wakelock = False
|
||||
|
||||
# (list) Android application meta-data to set (key=value format)
|
||||
#android.meta_data =
|
||||
|
||||
# (list) Android library project to add (will be added in the
|
||||
# project.properties automatically.)
|
||||
#android.library_references =
|
||||
|
||||
# (str) Android logcat filters to use
|
||||
#android.logcat_filters = *:S python:D
|
||||
|
||||
# (bool) Copy library instead of making a libpymodules.so
|
||||
#android.copy_libs = 1
|
||||
|
||||
# (str) The Android arch to build for, choices: armeabi-v7a, arm64-v8a, x86
|
||||
android.arch = armeabi-v7a
|
||||
|
||||
#
|
||||
# Python for android (p4a) specific
|
||||
#
|
||||
|
||||
# (str) python-for-android git clone directory (if empty, it will be automatically cloned from github)
|
||||
p4a.source_dir = ./p4a
|
||||
|
||||
# (str) The directory in which python-for-android should look for your own build recipes (if any)
|
||||
p4a.local_recipes = ./recipes
|
||||
|
||||
# (str) Filename to the hook for p4a
|
||||
#p4a.hook =
|
||||
|
||||
# (str) Bootstrap to use for android builds
|
||||
p4a.bootstrap = lbry
|
||||
|
||||
|
||||
#
|
||||
# iOS specific
|
||||
#
|
||||
|
||||
# (str) Path to a custom kivy-ios folder
|
||||
#ios.kivy_ios_dir = ../kivy-ios
|
||||
|
||||
# (str) Name of the certificate to use for signing the debug version
|
||||
# Get a list of available identities: buildozer ios list_identities
|
||||
#ios.codesign.debug = "iPhone Developer: <lastname> <firstname> (<hexstring>)"
|
||||
|
||||
# (str) Name of the certificate to use for signing the release version
|
||||
#ios.codesign.release = %(ios.codesign.debug)s
|
||||
|
||||
|
||||
[buildozer]
|
||||
|
||||
# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output))
|
||||
log_level = 2
|
||||
|
||||
# (int) Display warning if buildozer is run as root (0 = False, 1 = True)
|
||||
warn_on_root = 1
|
||||
|
||||
# (str) Path to build artifact storage, absolute or relative to spec file
|
||||
# build_dir = ./.buildozer
|
||||
|
||||
# (str) Path to build output (i.e. .apk, .ipa) storage
|
||||
# bin_dir = ./bin
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# List as sections
|
||||
#
|
||||
# You can define all the "list" as [section:key].
|
||||
# Each line will be considered as a option to the list.
|
||||
# Let's take [app] / source.exclude_patterns.
|
||||
# Instead of doing:
|
||||
#
|
||||
#[app]
|
||||
#source.exclude_patterns = license,data/audio/*.wav,data/images/original/*
|
||||
#
|
||||
# This can be translated into:
|
||||
#
|
||||
#[app:source.exclude_patterns]
|
||||
#license
|
||||
#data/audio/*.wav
|
||||
#data/images/original/*
|
||||
#
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Profiles
|
||||
#
|
||||
# You can extend section / key with a profile
|
||||
# For example, you want to deploy a demo version of your application without
|
||||
# HD content. You could first change the title to add "(demo)" in the name
|
||||
# and extend the excluded directories to remove the HD content.
|
||||
#
|
||||
#[app@demo]
|
||||
#title = My Application (demo)
|
||||
#
|
||||
#[app:source.exclude_patterns@demo]
|
||||
#images/hd/*
|
||||
#
|
||||
# Then, invoke the command line with the "demo" profile:
|
||||
#
|
||||
#buildozer --profile demo android debug
|
||||
|
||||
builddir = /home/vagrant
|
|
@ -1,7 +1,7 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.3.1'
|
||||
|
@ -11,6 +11,9 @@ buildscript {
|
|||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
maven {
|
||||
url 'https://maven.google.com'
|
||||
}
|
||||
maven {
|
||||
// All of React Native (JS, Android binaries) is installed from npm
|
||||
url "$rootDir/react/node_modules/react-native/android"
|
||||
|
@ -26,14 +29,14 @@ apply plugin: 'com.android.application'
|
|||
android {
|
||||
compileSdkVersion {{ android_api }}
|
||||
buildToolsVersion '{{ build_tools_version }}'
|
||||
defaultConfig {
|
||||
defaultConfig {
|
||||
minSdkVersion {{ args.min_sdk_version }}
|
||||
targetSdkVersion {{ android_api }}
|
||||
versionCode {{ args.numeric_version }}
|
||||
versionName '{{ args.version }}'
|
||||
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,14 +63,25 @@ android {
|
|||
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDir 'libs'
|
||||
jniLibs.srcDir 'libs'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
afterEvaluate {project ->
|
||||
if (project.hasProperty("android")) {
|
||||
android {
|
||||
compileSdkVersion {{ android_api }}
|
||||
buildToolsVersion '{{ build_tools_version }}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':react-native-video')
|
||||
compile project(':react-native-fetch-blob')
|
||||
{%- for aar in aars %}
|
||||
compile(name: '{{ aar }}', ext: 'aar')
|
||||
{%- endfor -%}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
rootProject.name = 'browser'
|
||||
include ':react-native-video'
|
||||
project(':react-native-video').projectDir = new File(rootProject.projectDir, './react/node_modules/react-native-video/android')
|
||||
project(':react-native-video').projectDir = new File(rootProject.projectDir, './react/node_modules/react-native-video/android-exoplayer')
|
||||
include ':react-native-fetch-blob'
|
||||
project(':react-native-fetch-blob').projectDir = new File(rootProject.projectDir, './react/node_modules/react-native-fetch-blob/android')
|
||||
|
|
|
@ -7,7 +7,7 @@ import sh
|
|||
|
||||
class LibGMPRecipe(Recipe):
|
||||
version = '6.1.2'
|
||||
url = 'https://ftp.gnu.org/pub/gnu/gmp/gmp-{version}.tar.bz2'
|
||||
url = 'http://www.mirrorservice.org/pub/gnu/gmp/gmp-{version}.tar.bz2'
|
||||
#url = 'https://gmplib.org/download/gmp/gmp-{version}.tar.bz2'
|
||||
|
||||
def should_build(self, arch):
|
||||
|
|
25
recipes/netifaces/__init__.py
Normal file
25
recipes/netifaces/__init__.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
import glob
|
||||
from pythonforandroid.toolchain import CompiledComponentsPythonRecipe, Recipe
|
||||
from os.path import join
|
||||
import sh
|
||||
|
||||
|
||||
class NetifacesRecipe(CompiledComponentsPythonRecipe):
|
||||
version = '0.10.7'
|
||||
url = 'https://files.pythonhosted.org/packages/81/39/4e9a026265ba944ddf1fea176dbb29e0fe50c43717ba4fcf3646d099fe38/netifaces-{version}.tar.gz'
|
||||
depends = ['python2', 'setuptools']
|
||||
call_hostpython_via_targetpython = False
|
||||
|
||||
def get_recipe_env(self, arch):
|
||||
env = super(NetifacesRecipe, self).get_recipe_env(arch)
|
||||
|
||||
target_python = Recipe.get_recipe('python2', self.ctx).get_build_dir(arch.arch)
|
||||
env['PYTHON_ROOT'] = join(target_python, 'python-install')
|
||||
env['LDSHARED'] = env['CC'] + ' -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions'
|
||||
env['CFLAGS'] += ' -I' + env['PYTHON_ROOT'] + '/include/python2.7'
|
||||
env['LDFLAGS'] += ' -L' + env['PYTHON_ROOT'] + '/lib' + ' -lpython2.7'
|
||||
|
||||
return env
|
||||
|
||||
recipe = NetifacesRecipe()
|
|
@ -1,59 +0,0 @@
|
|||
|
||||
from pythonforandroid.toolchain import (
|
||||
CompiledComponentsPythonRecipe,
|
||||
Recipe,
|
||||
current_directory,
|
||||
info,
|
||||
shprint,
|
||||
)
|
||||
from os.path import join
|
||||
import sh
|
||||
|
||||
|
||||
class PyCryptoRecipe(CompiledComponentsPythonRecipe):
|
||||
version = '2.6.1'
|
||||
url = 'https://pypi.python.org/packages/source/p/pycrypto/pycrypto-{version}.tar.gz'
|
||||
depends = ['libgmp', 'openssl', 'python2']
|
||||
site_packages_name = 'Crypto'
|
||||
call_hostpython_via_targetpython = False
|
||||
|
||||
patches = ['add_length.patch', 'fix-fastmath-include-dirs.patch']
|
||||
|
||||
def get_recipe_env(self, arch=None):
|
||||
env = super(PyCryptoRecipe, self).get_recipe_env(arch)
|
||||
openssl_build_dir = Recipe.get_recipe('openssl', self.ctx).get_build_dir(arch.arch)
|
||||
target_python = Recipe.get_recipe('python2', self.ctx).get_build_dir(arch.arch)
|
||||
# include libgmp build dir for gmp.h
|
||||
libgmp_build_dir = Recipe.get_recipe('libgmp', self.ctx).get_build_dir(arch.arch)
|
||||
|
||||
# set to prevent including hostpython headers to avoid
|
||||
# LONG_BIT definition appears wrong for platform error when compiling for Androidd
|
||||
env['PYTHONXCPREFIX'] = target_python
|
||||
env['LDSHARED'] = env['CC'] + ' -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions'
|
||||
env['PYTHON_ROOT'] = join(target_python, 'python-install')
|
||||
env['CFLAGS'] += ' -I' + env['PYTHON_ROOT'] + '/include/python2.7'
|
||||
env['LDFLAGS'] += ' -L' + env['PYTHON_ROOT'] + '/lib' + ' -lpython2.7'
|
||||
|
||||
env['CFLAGS'] += ' -I%s' % (join(libgmp_build_dir, 'include'))
|
||||
env['CC'] = '%s -I%s' % (env['CC'], join(openssl_build_dir, 'include'))
|
||||
env['LDFLAGS'] = env['LDFLAGS'] + ' -L{}'.format(
|
||||
self.ctx.get_libs_dir(arch.arch) +
|
||||
'-L{}'.format(self.ctx.libs_dir)) + ' -L{}'.format(
|
||||
openssl_build_dir)
|
||||
env['EXTRA_CFLAGS'] = '--host linux-armv'
|
||||
env['ac_cv_func_malloc_0_nonnull'] = 'yes'
|
||||
|
||||
return env
|
||||
|
||||
def build_compiled_components(self, arch):
|
||||
info('Configuring compiled components in {}'.format(self.name))
|
||||
|
||||
env = self.get_recipe_env(arch)
|
||||
with current_directory(self.get_build_dir(arch.arch)):
|
||||
configure = sh.Command('./configure')
|
||||
shprint(configure, '--host=arm-eabi',
|
||||
'--prefix={}'.format(self.ctx.get_python_install_dir()),
|
||||
'--enable-shared', _env=env)
|
||||
super(PyCryptoRecipe, self).build_compiled_components(arch)
|
||||
|
||||
recipe = PyCryptoRecipe()
|
|
@ -1,11 +0,0 @@
|
|||
--- pycrypto-2.6.1/src/hash_SHA2_template.c.orig 2013-10-14 14:38:10.000000000 -0700
|
||||
+++ pycrypto-2.6.1/src/hash_SHA2_template.c 2014-05-19 10:15:51.000000000 -0700
|
||||
@@ -87,7 +87,7 @@
|
||||
* return 1 on success
|
||||
* return 0 if the length overflows
|
||||
*/
|
||||
-int add_length(hash_state *hs, sha2_word_t inc) {
|
||||
+static int add_length(hash_state *hs, sha2_word_t inc) {
|
||||
sha2_word_t overflow_detector;
|
||||
overflow_detector = hs->length_lower;
|
||||
hs->length_lower += inc;
|
|
@ -1,11 +0,0 @@
|
|||
--- a/setup.py 2013-10-14 22:38:10.000000000 +0100
|
||||
+++ b/setup.py 2017-12-20 16:05:16.726389781 +0100
|
||||
@@ -370,7 +370,7 @@
|
||||
'ext_modules': plat_ext + [
|
||||
# _fastmath (uses GNU mp library)
|
||||
Extension("Crypto.PublicKey._fastmath",
|
||||
- include_dirs=['src/','/usr/include/'],
|
||||
+ include_dirs=['src/'],
|
||||
libraries=['gmp'],
|
||||
sources=["src/_fastmath.c"]),
|
||||
|
11
recipes/sqlite3/Android.mk
Normal file
11
recipes/sqlite3/Android.mk
Normal file
|
@ -0,0 +1,11 @@
|
|||
LOCAL_PATH := $(call my-dir)/..
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := sqlite3.c
|
||||
|
||||
LOCAL_MODULE := sqlite3
|
||||
|
||||
LOCAL_CFLAGS := -DSQLITE_ENABLE_FTS4
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
32
recipes/sqlite3/__init__.py
Normal file
32
recipes/sqlite3/__init__.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
from pythonforandroid.toolchain import NDKRecipe, shprint, shutil, current_directory
|
||||
from os.path import join, exists
|
||||
import sh
|
||||
|
||||
class Sqlite3Recipe(NDKRecipe):
|
||||
version = '3.24.0'
|
||||
# Don't forget to change the URL when changing the version
|
||||
url = 'https://www.sqlite.org/2018/sqlite-amalgamation-3240000.zip'
|
||||
generated_libraries = ['sqlite3']
|
||||
|
||||
def should_build(self, arch):
|
||||
return not self.has_libs(arch, 'libsqlite3.so')
|
||||
|
||||
def prebuild_arch(self, arch):
|
||||
super(Sqlite3Recipe, self).prebuild_arch(arch)
|
||||
# Copy the Android make file
|
||||
sh.mkdir('-p', join(self.get_build_dir(arch.arch), 'jni'))
|
||||
shutil.copyfile(join(self.get_recipe_dir(), 'Android.mk'),
|
||||
join(self.get_build_dir(arch.arch), 'jni/Android.mk'))
|
||||
|
||||
def build_arch(self, arch, *extra_args):
|
||||
super(Sqlite3Recipe, self).build_arch(arch)
|
||||
# Copy the shared library
|
||||
shutil.copyfile(join(self.get_build_dir(arch.arch), 'libs', arch.arch, 'libsqlite3.so'),
|
||||
join(self.ctx.get_libs_dir(arch.arch), 'libsqlite3.so'))
|
||||
|
||||
def get_recipe_env(self, arch):
|
||||
env = super(Sqlite3Recipe, self).get_recipe_env(arch)
|
||||
env['NDK_PROJECT_PATH'] = self.get_build_dir(arch.arch)
|
||||
return env
|
||||
|
||||
recipe = Sqlite3Recipe()
|
|
@ -12,7 +12,7 @@ import sh
|
|||
|
||||
|
||||
class TwistedRecipe(CythonRecipe):
|
||||
version = '16.0.0'
|
||||
version = '16.6.0'
|
||||
url = 'https://github.com/twisted/twisted/archive/twisted-{version}.tar.gz'
|
||||
|
||||
depends = ['setuptools', 'zope_interface']
|
||||
|
|
BIN
src/main/assets/fonts/FontAwesome5_Brands.ttf
Normal file
BIN
src/main/assets/fonts/FontAwesome5_Brands.ttf
Normal file
Binary file not shown.
BIN
src/main/assets/fonts/FontAwesome5_Regular.ttf
Normal file
BIN
src/main/assets/fonts/FontAwesome5_Regular.ttf
Normal file
Binary file not shown.
BIN
src/main/assets/fonts/FontAwesome5_Solid.ttf
Normal file
BIN
src/main/assets/fonts/FontAwesome5_Solid.ttf
Normal file
Binary file not shown.
|
@ -2,12 +2,16 @@ package io.lbry.browser;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -29,16 +33,48 @@ import org.renpy.android.ResourceManager;
|
|||
* @version 0.1
|
||||
*/
|
||||
public class LbrynetService extends PythonService {
|
||||
|
||||
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "io.lbry.browser.DAEMON_NOTIFICATION_CHANNEL";
|
||||
|
||||
public static String TAG = "LbrynetService";
|
||||
|
||||
public static LbrynetService serviceInstance;
|
||||
|
||||
@Override
|
||||
public boolean canDisplayNotification() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void doStartForeground(Bundle extras) {
|
||||
String serviceTitle = extras.getString("serviceTitle");
|
||||
String serviceDescription = "The LBRY service is running in the background.";
|
||||
|
||||
Context context = getApplicationContext();
|
||||
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID, "LBRY Browser", NotificationManager.IMPORTANCE_LOW);
|
||||
channel.setDescription("LBRY service notification channel");
|
||||
channel.setShowBadge(false);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
Intent contextIntent = new Intent(context, MainActivity.class);
|
||||
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
|
||||
Notification notification = builder.setContentTitle(serviceTitle)
|
||||
.setContentText(serviceDescription)
|
||||
.setContentIntent(pIntent)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setSmallIcon(R.drawable.ic_lbry)
|
||||
.setOngoing(true)
|
||||
.build();
|
||||
startForeground(1, notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int startType() {
|
||||
return START_STICKY;
|
||||
|
@ -66,7 +102,7 @@ public class LbrynetService extends PythonService {
|
|||
super.onDestroy();
|
||||
serviceInstance = null;
|
||||
}
|
||||
|
||||
|
||||
public String getAppRoot() {
|
||||
String app_root = getApplicationContext().getFilesDir().getAbsolutePath() + "/app";
|
||||
return app_root;
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
|||
import com.facebook.react.ReactRootView;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import com.RNFetchBlob.RNFetchBlobPackage;
|
||||
|
||||
import io.lbry.browser.reactpackages.LbryReactPackage;
|
||||
import io.lbry.browser.reactmodules.DownloadManagerModule;
|
||||
|
@ -51,6 +52,8 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
|
||||
public static final String DEVICE_ID_KEY = "deviceId";
|
||||
|
||||
public static final String SETTING_KEEP_DAEMON_RUNNING = "keepDaemonRunning";
|
||||
|
||||
/**
|
||||
* Flag which indicates whether or not the service is running. Will be updated in the
|
||||
* onResume method.
|
||||
|
@ -86,6 +89,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
.setJSMainModulePath("index")
|
||||
.addPackage(new MainReactPackage())
|
||||
.addPackage(new ReactVideoPackage())
|
||||
.addPackage(new RNFetchBlobPackage())
|
||||
.addPackage(new LbryReactPackage())
|
||||
.setUseDeveloperSupport(true)
|
||||
.setInitialLifecycleState(LifecycleState.RESUMED)
|
||||
|
@ -168,19 +172,10 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
Toast.makeText(context, "Rewards cannot be claimed because we could not identify your device.", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-384");
|
||||
md.update(id.getBytes("UTF-8"));
|
||||
String hash = new BigInteger(1, md.digest()).toString(16);
|
||||
|
||||
SharedPreferences sp = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putString(DEVICE_ID_KEY, hash);
|
||||
editor.commit();
|
||||
} catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
|
||||
// SHA-384 not found, UTF-8 encoding not supported
|
||||
Toast.makeText(context, "Rewards cannot be claimed because we could not identify your device.", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
SharedPreferences sp = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putString(DEVICE_ID_KEY, id);
|
||||
editor.commit();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
@ -218,7 +213,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
protected void onDestroy() {
|
||||
// check service running setting and end it here
|
||||
SharedPreferences sp = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
boolean shouldKeepDaemonRunning = sp.getBoolean("keepDaemonRunning", true);
|
||||
boolean shouldKeepDaemonRunning = sp.getBoolean(SETTING_KEEP_DAEMON_RUNNING, true);
|
||||
if (!shouldKeepDaemonRunning) {
|
||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||
if (serviceRunning) {
|
||||
|
|
|
@ -3,12 +3,14 @@ package io.lbry.browser.reactmodules;
|
|||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
import io.lbry.browser.LbrynetService;
|
||||
import io.lbry.browser.MainActivity;
|
||||
import io.lbry.browser.ServiceHelper;
|
||||
|
||||
public class DaemonServiceControlModule extends ReactContextBaseJavaModule {
|
||||
|
@ -33,4 +35,14 @@ public class DaemonServiceControlModule extends ReactContextBaseJavaModule {
|
|||
public void stopService() {
|
||||
ServiceHelper.stop(context, LbrynetService.class);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setKeepDaemonRunning(boolean value) {
|
||||
if (context != null) {
|
||||
SharedPreferences sp = context.getSharedPreferences(MainActivity.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putBoolean(MainActivity.SETTING_KEEP_DAEMON_RUNNING, value);
|
||||
editor.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.NotificationManagerCompat;
|
||||
|
||||
|
@ -28,18 +31,22 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
|
|||
|
||||
private HashMap<String, Integer> downloadIdNotificationIdMap = new HashMap<String, Integer>();
|
||||
|
||||
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#");
|
||||
|
||||
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#");
|
||||
|
||||
private static final int MAX_PROGRESS = 100;
|
||||
|
||||
private static final String GROUP_DOWNLOADS = "io.lbry.browser.GROUP_DOWNLOADS";
|
||||
|
||||
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "io.lbry.browser.DOWNLOADS_NOTIFICATION_CHANNEL";
|
||||
|
||||
private static boolean channelCreated = false;
|
||||
|
||||
public static final String NOTIFICATION_ID_KEY = "io.lbry.browser.notificationId";
|
||||
|
||||
|
||||
public static final int GROUP_ID = 0;
|
||||
|
||||
|
||||
public static boolean groupCreated = false;
|
||||
|
||||
|
||||
public DownloadManagerModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
|
@ -53,28 +60,40 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
|
|||
public String getName() {
|
||||
return "LbryDownloadManager";
|
||||
}
|
||||
|
||||
|
||||
private void createNotificationChannel() {
|
||||
// Only applies to Android 8.0 Oreo (API Level 26) or higher
|
||||
if (!channelCreated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID, "LBRY Downloads", NotificationManager.IMPORTANCE_LOW);
|
||||
channel.setDescription("LBRY file downloads");
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
private void createNotificationGroup() {
|
||||
if (!groupCreated) {
|
||||
Intent intent = new Intent(context, NotificationDeletedReceiver.class);
|
||||
intent.putExtra(NOTIFICATION_ID_KEY, GROUP_ID);
|
||||
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, GROUP_ID, intent, 0);
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
|
||||
builder.setContentTitle("Active downloads")
|
||||
.setContentText("Active downloads")
|
||||
.setSmallIcon(R.drawable.ic_file_download_black_24dp)
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
|
||||
builder.setContentTitle("Active LBRY downloads")
|
||||
.setContentText("Active LBRY downloads")
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setGroup(GROUP_DOWNLOADS)
|
||||
.setGroupSummary(true)
|
||||
.setDeleteIntent(pendingIntent);
|
||||
notificationManager.notify(GROUP_ID, builder.build());
|
||||
|
||||
|
||||
groupCreated = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private PendingIntent getLaunchPendingIntent(String uri) {
|
||||
Intent launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
|
||||
launchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
|
@ -84,17 +103,18 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
|
|||
|
||||
@ReactMethod
|
||||
public void startDownload(String id, String fileName) {
|
||||
createNotificationChannel();
|
||||
createNotificationGroup();
|
||||
|
||||
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
|
||||
// The file URI is used as the unique ID
|
||||
builder.setContentIntent(getLaunchPendingIntent(id))
|
||||
.setContentTitle(String.format("Downloading %s...", fileName))
|
||||
.setGroup(GROUP_DOWNLOADS)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setProgress(MAX_PROGRESS, 0, false)
|
||||
.setSmallIcon(R.drawable.ic_file_download_black_24dp);
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download);
|
||||
|
||||
int notificationId = generateNotificationId();
|
||||
downloadIdNotificationIdMap.put(id, notificationId);
|
||||
|
@ -114,6 +134,7 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
|
|||
return;
|
||||
}
|
||||
|
||||
createNotificationChannel();
|
||||
createNotificationGroup();
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
NotificationCompat.Builder builder = builders.get(notificationId);
|
||||
|
@ -128,12 +149,12 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
|
|||
.setContentText(String.format("%s", formatBytes(totalBytes)))
|
||||
.setProgress(0, 0, false);
|
||||
notificationManager.notify(notificationId, builder.build());
|
||||
|
||||
|
||||
downloadIdNotificationIdMap.remove(id);
|
||||
builders.remove(notificationId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ReactMethod
|
||||
public void stopDownload(String id, String filename) {
|
||||
if (!downloadIdNotificationIdMap.containsKey(id)) {
|
||||
|
@ -144,14 +165,14 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
|
|||
if (!builders.containsKey(notificationId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
NotificationCompat.Builder builder = builders.get(notificationId);
|
||||
notificationManager.cancel(notificationId);
|
||||
|
||||
|
||||
downloadIdNotificationIdMap.remove(id);
|
||||
builders.remove(notificationId);
|
||||
|
||||
|
||||
if (builders.values().size() == 0) {
|
||||
notificationManager.cancel(GROUP_ID);
|
||||
groupCreated = false;
|
||||
|
|
|
@ -2,12 +2,17 @@ package io.lbry.browser.reactmodules;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
import io.lbry.browser.MainActivity;
|
||||
|
||||
public class UtilityModule extends ReactContextBaseJavaModule {
|
||||
private Context context;
|
||||
|
||||
|
@ -23,29 +28,58 @@ public class UtilityModule extends ReactContextBaseJavaModule {
|
|||
|
||||
@ReactMethod
|
||||
public void keepAwakeOn() {
|
||||
final Activity activity = getCurrentActivity();
|
||||
final Activity activity = getCurrentActivity();
|
||||
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void keepAwakeOff() {
|
||||
final Activity activity = getCurrentActivity();
|
||||
final Activity activity = getCurrentActivity();
|
||||
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hideNavigationBar() {
|
||||
if (context != null && context instanceof Activity) {
|
||||
Activity activity = (Activity) context;
|
||||
View decorView = activity.getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void showNavigationBar() {
|
||||
if (context != null && context instanceof Activity) {
|
||||
Activity activity = (Activity) context;
|
||||
View decorView = activity.getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getDeviceId(final Promise promise) {
|
||||
SharedPreferences sp = context.getSharedPreferences(MainActivity.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
String deviceId = sp.getString(MainActivity.DEVICE_ID_KEY, null);
|
||||
promise.resolve(deviceId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,8 +81,8 @@ from jsonrpc.proxy import JSONRPCProxy
|
|||
from lbrynet import analytics
|
||||
from lbrynet import conf
|
||||
from lbrynet.core import utils, system_info
|
||||
from lbrynet.daemon.auth.client import LBRYAPIClient
|
||||
from lbrynet.daemon.DaemonServer import DaemonServer
|
||||
from lbrynet.daemon.Components import PEER_PROTOCOL_SERVER_COMPONENT, REFLECTOR_COMPONENT
|
||||
from lbrynet.daemon.Daemon import Daemon
|
||||
|
||||
# https certificate verification
|
||||
# TODO: this is bad. Need to find a way to properly verify https requests
|
||||
|
@ -124,33 +124,22 @@ def start():
|
|||
|
||||
lbrynet_log = conf.settings.get_log_filename()
|
||||
log_support.configure_logging(lbrynet_log, True, [])
|
||||
log.debug('Final Settings: %s', conf.settings.get_current_settings_dict())
|
||||
|
||||
# TODO: specify components, initialise auth
|
||||
conf.settings.update({
|
||||
'components_to_skip': [PEER_PROTOCOL_SERVER_COMPONENT, REFLECTOR_COMPONENT],
|
||||
'concurrent_announcers': 0
|
||||
})
|
||||
|
||||
log.info('Final Settings: %s', conf.settings.get_current_settings_dict())
|
||||
log.info("Starting lbrynet-daemon")
|
||||
|
||||
if test_internet_connection():
|
||||
analytics_manager = analytics.Manager.new_instance()
|
||||
start_server_and_listen(False, analytics_manager)
|
||||
daemon = Daemon()
|
||||
daemon.start_listening()
|
||||
reactor.run()
|
||||
else:
|
||||
log.info("Not connected to internet, unable to start")
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def start_server_and_listen(use_auth, analytics_manager, max_tries=5):
|
||||
"""The primary entry point for launching the daemon.
|
||||
Args:
|
||||
use_auth: set to true to enable http authentication
|
||||
analytics_manager: to send analytics
|
||||
"""
|
||||
analytics_manager.send_server_startup()
|
||||
daemon_server = DaemonServer(analytics_manager)
|
||||
try:
|
||||
yield daemon_server.start(use_auth)
|
||||
analytics_manager.send_server_startup_success()
|
||||
except Exception as e:
|
||||
log.exception('Failed to startup')
|
||||
yield daemon_server.stop()
|
||||
analytics_manager.send_server_startup_error(str(e))
|
||||
reactor.fireSystemEvent("shutdown")
|
||||
log.info("Not connected to the Internet. Unable to start.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
__version__ = "0.1.8"
|
||||
__version__ = "0.1.10"
|
||||
|
||||
class ServiceApp(App):
|
||||
def build(self):
|
||||
|
|
BIN
src/main/res/drawable-hdpi/ic_lbry.png
Normal file
BIN
src/main/res/drawable-hdpi/ic_lbry.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 134 KiB |
BIN
src/main/res/drawable-mdpi/ic_lbry.png
Normal file
BIN
src/main/res/drawable-mdpi/ic_lbry.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 134 KiB |
BIN
src/main/res/drawable-xhdpi/ic_lbry.png
Normal file
BIN
src/main/res/drawable-xhdpi/ic_lbry.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 134 KiB |
BIN
src/main/res/drawable-xxhdpi/ic_lbry.png
Normal file
BIN
src/main/res/drawable-xxhdpi/ic_lbry.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 134 KiB |
BIN
src/main/res/drawable-xxxhdpi/ic_lbry.png
Normal file
BIN
src/main/res/drawable-xxxhdpi/ic_lbry.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 134 KiB |
Loading…
Add table
Reference in a new issue