diff --git a/app/package-lock.json b/app/package-lock.json index 8b048b37..9588c367 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -4060,8 +4060,8 @@ } }, "lbry-redux": { - "version": "github:lbryio/lbry-redux#18fec9f1f0f5ad366ebe12279e5dd0d67a37b2d5", - "from": "github:lbryio/lbry-redux#mobile-channel-pagination", + "version": "github:lbryio/lbry-redux#42c185e922a7c6091b0e1580bacbfd8e02f45a91", + "from": "github:lbryio/lbry-redux#031sdk-resolve", "requires": { "proxy-polyfill": "0.1.6", "reselect": "^3.0.0", @@ -4076,8 +4076,8 @@ } }, "lbryinc": { - "version": "github:lbryio/lbryinc#4d24cef00106294d3e848b6c2b261e01382fce17", - "from": "github:lbryio/lbryinc#subscriptions", + "version": "github:lbryio/lbryinc#fff1a8979f9aca6ce7e8917396270704cc497f82", + "from": "github:lbryio/lbryinc", "requires": { "bluebird": "^3.5.1", "lbry-redux": "github:lbryio/lbry-redux#84b7d396934d57a37802aadbef71db91230a9404", diff --git a/app/package.json b/app/package.json index 54d7a117..eaa02be9 100644 --- a/app/package.json +++ b/app/package.json @@ -8,8 +8,8 @@ "dependencies": { "base-64": "^0.1.0", "@expo/vector-icons": "^8.1.0", - "lbry-redux": "lbryio/lbry-redux#mobile-channel-pagination", - "lbryinc": "lbryio/lbryinc#subscriptions", + "lbry-redux": "lbryio/lbry-redux", + "lbryinc": "lbryio/lbryinc", "moment": "^2.22.1", "react": "16.2.0", "react-native": "0.55.3", diff --git a/app/src/constants.js b/app/src/constants.js index e2ff9156..2ab9b0e3 100644 --- a/app/src/constants.js +++ b/app/src/constants.js @@ -5,6 +5,8 @@ const Constants = { SETTING_ALPHA_UNDERSTANDS_RISKS: "alphaUnderstandRisks", SETTING_SUBSCRIPTIONS_VIEW_MODE: "subscriptionsViewMode", + SETTING_RATING_REMINDER_LAST_SHOWN: "ratingReminderLastShown", + SETTING_RATING_REMINDER_DISABLED: "ratingReminderDisabled", ACTION_DELETE_COMPLETED_BLOBS: "DELETE_COMPLETED_BLOBS", ACTION_FIRST_RUN_PAGE_CHANGED: "FIRST_RUN_PAGE_CHANGED", @@ -27,7 +29,10 @@ const Constants = { DRAWER_ROUTE_ABOUT: "About", SUBSCRIPTIONS_VIEW_ALL: 'view_all', - SUBSCRIPTIONS_VIEW_LATEST_FIRST: 'view_latest_first' + SUBSCRIPTIONS_VIEW_LATEST_FIRST: 'view_latest_first', + + PLAY_STORE_URL: 'https://play.google.com/store/apps/details?id=io.lbry.browser', + RATING_REMINDER_INTERVAL: 604800, // 7 days (7 * 24 * 3600s) }; export default Constants; diff --git a/app/src/page/discover/index.js b/app/src/page/discover/index.js index 4cedbdae..9fbd53a6 100644 --- a/app/src/page/discover/index.js +++ b/app/src/page/discover/index.js @@ -13,6 +13,9 @@ import { selectSubscriptionClaims, selectUnreadSubscriptions, } from 'lbryinc'; +import { doSetClientSetting } from 'redux/actions/settings'; +import { makeSelectClientSetting } from 'redux/selectors/settings'; +import Constants from 'constants'; import DiscoverPage from './view'; const select = state => ({ @@ -21,6 +24,8 @@ const select = state => ({ enabledChannelNotifications: selectEnabledChannelNotifications(state), featuredUris: selectFeaturedUris(state), fetchingFeaturedUris: selectFetchingFeaturedUris(state), + ratingReminderDisabled: makeSelectClientSetting(Constants.SETTING_RATING_REMINDER_DISABLED)(state), + ratingReminderLastShown: makeSelectClientSetting(Constants.SETTING_RATING_REMINDER_LAST_SHOWN)(state), unreadSubscriptions: selectUnreadSubscriptions(state), }); @@ -29,6 +34,7 @@ const perform = dispatch => ({ fetchRewardedContent: () => dispatch(doFetchRewardedContent()), fetchSubscriptions: () => dispatch(doFetchMySubscriptions()), removeUnreadSubscriptions: () => dispatch(doRemoveUnreadSubscriptions()), + setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)) }); -export default connect(select, perform)(DiscoverPage); \ No newline at end of file +export default connect(select, perform)(DiscoverPage); diff --git a/app/src/page/discover/view.js b/app/src/page/discover/view.js index 30691873..fc59e43c 100644 --- a/app/src/page/discover/view.js +++ b/app/src/page/discover/view.js @@ -1,8 +1,10 @@ import React from 'react'; import NavigationActions from 'react-navigation'; import { + Alert, ActivityIndicator, AsyncStorage, + Linking, NativeModules, SectionList, Text, @@ -10,6 +12,7 @@ import { } from 'react-native'; import { normalizeURI, parseURI } from 'lbry-redux'; import moment from 'moment'; +import Constants from 'constants'; import Colors from 'styles/colors'; import discoverStyle from 'styles/discover'; import FloatingWalletBalance from 'component/floatingWalletBalance'; @@ -50,6 +53,8 @@ class DiscoverPage extends React.PureComponent { fetchFeaturedUris(); fetchRewardedContent(); fetchSubscriptions(); + + this.showRatingReminder(); } subscriptionForUri = (uri, channelName) => { @@ -103,6 +108,46 @@ class DiscoverPage extends React.PureComponent { } } + showRatingReminder = () => { + const { ratingReminderDisabled, ratingReminderLastShown, setClientSetting } = this.props; + + const now = moment().unix(); + if ('true' !== ratingReminderDisabled && ratingReminderLastShown) { + const lastShownParts = ratingReminderLastShown.split('|'); + if (lastShownParts.length === 2) { + const lastShownTime = parseInt(lastShownParts[0], 10); + const lastShownCount = parseInt(lastShownParts[1], 10); + if (!isNaN(lastShownTime) && !isNaN(lastShownCount)) { + if (now > (lastShownTime + (Constants.RATING_REMINDER_INTERVAL * lastShownCount))) { + Alert.alert( + 'Enjoying LBRY?', + 'Are you enjoying your experience with the LBRY app? You can leave a review for us on the Play Store.', + [ + { text: 'Never ask again', onPress: () => setClientSetting(Constants.SETTING_RATING_REMINDER_DISABLED, 'true')}, + { text: 'Maybe later', onPress: () => this.updateRatingReminderShown(lastShownCount)}, + { text: 'Rate app', onPress: () => { + setClientSetting(Constants.SETTING_RATING_REMINDER_DISABLED, 'true'); + Linking.openURL(Constants.PLAY_STORE_URL); + }} + ], + { cancelable: false } + ); + } + } + } + } + if (!ratingReminderLastShown) { + // first time, so set a value for the next interval multiplier + this.updateRatingReminderShown(0); + } + } + + updateRatingReminderShown = (lastShownCount) => { + const { setClientSetting } = this.props; + const settingString = (moment().unix() + '|' + (lastShownCount + 1)); + setClientSetting(Constants.SETTING_RATING_REMINDER_LAST_SHOWN, settingString); + } + render() { const { featuredUris, fetchingFeaturedUris, navigation } = this.props; const hasContent = typeof featuredUris === 'object' && Object.keys(featuredUris).length, diff --git a/app/src/page/splash/view.js b/app/src/page/splash/view.js index 3fa6259d..acdfbe2f 100644 --- a/app/src/page/splash/view.js +++ b/app/src/page/splash/view.js @@ -125,7 +125,7 @@ class SplashScreen extends React.PureComponent { const { deleteCompleteBlobs, fetchSubscriptions } = this.props; const startupStatus = status.startup_status; // At the minimum, wallet should be started and blocks_behind equal to 0 before calling resolve - const hasStarted = startupStatus.file_manager && startupStatus.wallet && status.wallet.blocks_behind <= 0; + const hasStarted = startupStatus.stream_manager && startupStatus.wallet && status.wallet.blocks_behind <= 0; if (hasStarted) { deleteCompleteBlobs(); @@ -142,7 +142,7 @@ class SplashScreen extends React.PureComponent { }); // fetch subscriptions, so that we can check for new content after resolve - Lbry.resolve({ uri: 'lbry://one' }).then(() => { + Lbry.resolve({ urls: 'lbry://one' }).then(() => { // Leave the splash screen const { authenticate, diff --git a/buildozer.spec.sample b/buildozer.spec.sample index fdb34771..293d2adb 100644 --- a/buildozer.spec.sample +++ b/buildozer.spec.sample @@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py # (list) Application requirements # comma seperated e.g. requirements = sqlite3,kivy -requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.30.4#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.4.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve +requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.32.0#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.4.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve # (str) Custom source folders for requirements # Sets custom source for any requirements with recipes diff --git a/buildozer.spec.travis b/buildozer.spec.travis index fdb34771..293d2adb 100644 --- a/buildozer.spec.travis +++ b/buildozer.spec.travis @@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py # (list) Application requirements # comma seperated e.g. requirements = sqlite3,kivy -requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.30.4#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.4.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve +requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.32.0#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.4.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve # (str) Custom source folders for requirements # Sets custom source for any requirements with recipes diff --git a/buildozer.spec.vagrant b/buildozer.spec.vagrant index fdb34771..293d2adb 100644 --- a/buildozer.spec.vagrant +++ b/buildozer.spec.vagrant @@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py # (list) Application requirements # comma seperated e.g. requirements = sqlite3,kivy -requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.30.4#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.4.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve +requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.32.0#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.4.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve # (str) Custom source folders for requirements # Sets custom source for any requirements with recipes diff --git a/src/main/python/lbrynetservice.py b/src/main/python/lbrynetservice.py index 96bcf377..d321033a 100644 --- a/src/main/python/lbrynetservice.py +++ b/src/main/python/lbrynetservice.py @@ -1,19 +1,26 @@ +import asyncio import keyring import logging +import pathlib import platform +import sys from jnius import autoclass from keyring.backend import KeyringBackend -from lbrynet import build_type -from lbrynet.extras.cli import conf, log_support, check_connection, Daemon, reactor +from lbrynet import __version__ as lbrynet_version, build_type +from lbrynet.conf import Config +from lbrynet.extras.daemon.loggly_handler import get_loggly_handler from lbrynet.extras.daemon.Components import DHT_COMPONENT, HASH_ANNOUNCER_COMPONENT, PEER_PROTOCOL_SERVER_COMPONENT -from lbrynet.extras.daemon.Components import REFLECTOR_COMPONENT +from lbrynet.extras.daemon.Daemon import Daemon +from lbrynet.extras.daemon.loggly_handler import get_loggly_handler +from lbrynet.utils import check_connection +log = logging.getLogger(__name__) +log.setLevel(logging.DEBUG) lbrynet_android_utils = autoclass('io.lbry.browser.Utils') service = autoclass('io.lbry.browser.LbrynetService').serviceInstance platform.platform = lambda: 'Android %s (API %s)' % (lbrynet_android_utils.getAndroidRelease(), lbrynet_android_utils.getAndroidSdk()) build_type.BUILD = 'dev' if lbrynet_android_utils.isDebug() else 'release' -log = logging.getLogger(__name__) # Keyring backend class LbryAndroidKeyring(KeyringBackend): @@ -34,36 +41,68 @@ class LbryAndroidKeyring(KeyringBackend): context = service.getApplicationContext() lbrynet_android_utils.deletePassword(servicename, username, context, self._keystore) +def ensure_directory_exists(path: str): + if not os.path.isdir(path): + pathlib.Path(path).mkdir(parents=True, exist_ok=True) + +def configure_logging(conf): + default_formatter = logging.Formatter("%(asctime)s %(levelname)-8s %(name)s:%(lineno)d: %(message)s") + + file_handler = logging.handlers.RotatingFileHandler( + conf.log_file_path, maxBytes=2097152, backupCount=5 + ) + file_handler.setFormatter(default_formatter) + logging.getLogger('lbrynet').addHandler(file_handler) + logging.getLogger('torba').addHandler(file_handler) + + handler = logging.StreamHandler() + handler.setFormatter(default_formatter) + + log.addHandler(handler) + logging.getLogger('lbrynet').addHandler(handler) + logging.getLogger('torba').addHandler(handler) + + logging.getLogger('aioupnp').setLevel(logging.WARNING) + logging.getLogger('aiohttp').setLevel(logging.CRITICAL) + logging.getLogger('lbrynet').setLevel(logging.DEBUG) + logging.getLogger('torba').setLevel(logging.INFO) + + loggly_handler = get_loggly_handler() + loggly_handler.setLevel(logging.ERROR) + logging.getLogger('lbrynet').addHandler(loggly_handler) def start(): keyring.set_keyring(LbryAndroidKeyring()) - private_storage_dir = lbrynet_android_utils.getAppInternalStorageDir(service.getApplicationContext()) - conf.initialize_settings( + conf = Config( data_dir=f'{private_storage_dir}/lbrynet', wallet_dir=f'{private_storage_dir}/lbryum', - download_dir=f'{lbrynet_android_utils.getInternalStorageDir(service.getApplicationContext())}/Download' + download_dir=f'{lbrynet_android_utils.getInternalStorageDir(service.getApplicationContext())}/Download', + components_to_skip=[DHT_COMPONENT, HASH_ANNOUNCER_COMPONENT, PEER_PROTOCOL_SERVER_COMPONENT], + use_upnp=False ) - conf.settings.update({ - 'components_to_skip': [ - DHT_COMPONENT, HASH_ANNOUNCER_COMPONENT, PEER_PROTOCOL_SERVER_COMPONENT, - REFLECTOR_COMPONENT - ], - 'use_upnp': False, - # 'use_https': True, # TODO: does this work on android? - # 'use_auth_http': True - }) - log_support.configure_logging(conf.settings.get_log_filename(), True, []) - log_support.configure_loggly_handler() + for directory in (conf.data_dir, conf.download_dir, conf.wallet_dir): + ensure_directory_exists(directory) - log.info('Final Settings: %s', conf.settings.get_current_settings_dict()) - log.info('Starting lbrynet-daemon'); + configure_logging(conf) + + log.info('Starting lbry sdk {}'.format(lbrynet_version)); + + loop = asyncio.get_event_loop() + loop.set_debug(True) if check_connection(): - daemon = Daemon() - daemon.start_listening() - reactor.run() + daemon = Daemon(conf) + try: + loop.run_until_complete(daemon.start()) + loop.run_until_complete(daemon.stop_event.wait()) + except (GracefulExit): + pass + finally: + loop.run_until_complete(daemon.stop()) + if hasattr(loop, 'shutdown_asyncgens'): + loop.run_until_complete(loop.shutdown_asyncgens()) else: print("Not connected to internet, unable to start")