From 5eba4ab2ec04b7ecf60aba7271f9f99fa3dc83ff Mon Sep 17 00:00:00 2001
From: Akinwale Ariwodola <akinwale@gmail.com>
Date: Mon, 30 Sep 2019 22:54:31 +0100
Subject: [PATCH 1/3] use shared state subscriber for preferences_set and
 preferences_get

---
 package-lock.json |  4 ++--
 package.json      |  2 +-
 src/index.js      | 36 ++++++++++++++++++------------------
 3 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 57c6435..1d0eb6f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5640,8 +5640,8 @@
             }
         },
         "lbry-redux": {
-            "version": "github:lbryio/lbry-redux#7ec72a737bcd336f000c5f5085891643110298c3",
-            "from": "github:lbryio/lbry-redux#7ec72a737bcd336f000c5f5085891643110298c3",
+            "version": "github:lbryio/lbry-redux#23bcde0539a27fb19bf3a7d87683279194e02046",
+            "from": "github:lbryio/lbry-redux#23bcde0539a27fb19bf3a7d87683279194e02046",
             "requires": {
                 "proxy-polyfill": "0.1.6",
                 "reselect": "^3.0.0",
diff --git a/package.json b/package.json
index 40155f1..dba18fb 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
         "base-64": "^0.1.0",
         "@expo/vector-icons": "^8.1.0",
         "gfycat-style-urls": "^1.0.3",
-        "lbry-redux": "lbryio/lbry-redux#7ec72a737bcd336f000c5f5085891643110298c3",
+        "lbry-redux": "lbryio/lbry-redux#23bcde0539a27fb19bf3a7d87683279194e02046",
         "lbryinc": "lbryio/lbryinc#67bb3e215be3f13605c5e3f9f2b0e2fb880724cf",
         "lodash": ">=4.17.11",
         "merge": ">=1.2.1",
diff --git a/src/index.js b/src/index.js
index 4013fe2..252f85c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -13,6 +13,7 @@ import {
   searchReducer,
   tagsReducer,
   walletReducer,
+  sharedStateSubscriber,
 } from 'lbry-redux';
 import {
   Lbryio,
@@ -83,7 +84,7 @@ const compressor = createCompressor();
 const authFilter = createFilter('auth', ['authToken']);
 const contentFilter = createFilter('content', ['positions']);
 const saveClaimsFilter = createFilter('claims', ['claimsByUri']);
-const subscriptionsFilter = createFilter('subscriptions', ['enabledChannelNotifications', 'subscriptions']);
+const subscriptionsFilter = createFilter('subscriptions', ['enabledChannelNotifications', 'subscriptions', 'latest']);
 const settingsFilter = createFilter('settings', ['clientSettings']);
 const tagsFilter = createFilter('tags', ['followedTags']);
 const walletFilter = createFilter('wallet', ['receiveAddress']);
@@ -147,25 +148,24 @@ const persistor = persistStore(store, persistOptions, err => {
 });
 window.persistor = persistor;
 
-let currentPayload;
-store.subscribe(() => {
-  const state = store.getState();
-  const subscriptions = state.subscriptions.subscriptions.map(({ uri }) => uri);
-  const tags = state.tags.followedTags;
-
-  const newPayload = {
-    version: '0.1',
-    shared: {
-      subscriptions,
-      tags,
+const sharedStateCache = {};
+const sharedStateFilters = {
+  tags: { source: 'tags', property: 'followedTags' },
+  subscriptions: {
+    source: 'subscriptions',
+    property: 'subscriptions',
+    transform: function(value) {
+      return value.map(({ uri }) => uri);
     },
-  };
+  },
+};
 
-  if (!isEqual(newPayload, currentPayload)) {
-    currentPayload = newPayload;
-    if (Lbryio.authToken) {
-      Lbryio.call('user_settings', 'set', { settings: JSON.stringify(newPayload) });
-    }
+store.subscribe(() => {
+  try {
+    const state = store.getState();
+    sharedStateSubscriber(state, sharedStateFilters, sharedStateCache);
+  } catch (e) {
+    // handle gracefully?
   }
 });
 

From f42cbb90a063eec91f0ccb2489a67703b3a8be42 Mon Sep 17 00:00:00 2001
From: Akinwale Ariwodola <akinwale@gmail.com>
Date: Wed, 2 Oct 2019 10:29:08 +0100
Subject: [PATCH 2/3] use doPreferenceGet for retrieving saved user state

---
 src/component/AppNavigator.js | 6 +++---
 src/index.js                  | 8 ++++++--
 src/page/splash/view.js       | 7 ++++---
 3 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/src/component/AppNavigator.js b/src/component/AppNavigator.js
index a5fb642..2d68083 100644
--- a/src/component/AppNavigator.js
+++ b/src/component/AppNavigator.js
@@ -29,7 +29,7 @@ import {
 import { connect } from 'react-redux';
 import { AppState, BackHandler, Linking, NativeModules, TextInput, ToastAndroid } from 'react-native';
 import { selectDrawerStack } from 'redux/selectors/drawer';
-import { SETTINGS, doDismissToast, doPopulateSharedUserState, doToast, selectToast } from 'lbry-redux';
+import { SETTINGS, doDismissToast, doPopulateSharedUserState, doPreferenceGet, doToast, selectToast } from 'lbry-redux';
 import {
   Lbryio,
   doGetSync,
@@ -308,8 +308,8 @@ class AppWithNavigationState extends React.Component {
 
   getUserSettings = () => {
     const { dispatch } = this.props;
-    Lbryio.call('user_settings', 'get').then(settings => {
-      dispatch(doPopulateSharedUserState(settings));
+    doPreferenceGet('shared', null, null, preference => {
+      dispatch(doPopulateSharedUserState(preference));
     });
   };
 
diff --git a/src/index.js b/src/index.js
index 252f85c..cea3631 100644
--- a/src/index.js
+++ b/src/index.js
@@ -148,7 +148,11 @@ const persistor = persistStore(store, persistOptions, err => {
 });
 window.persistor = persistor;
 
-const sharedStateCache = {};
+/**
+ * source: the reducer name
+ * property: the property in the reducer-specific state
+ * transform: optional method to modify the value to be stored
+ */
 const sharedStateFilters = {
   tags: { source: 'tags', property: 'followedTags' },
   subscriptions: {
@@ -163,7 +167,7 @@ const sharedStateFilters = {
 store.subscribe(() => {
   try {
     const state = store.getState();
-    sharedStateSubscriber(state, sharedStateFilters, sharedStateCache);
+    sharedStateSubscriber(state, sharedStateFilters, '0.1');
   } catch (e) {
     // handle gracefully?
   }
diff --git a/src/page/splash/view.js b/src/page/splash/view.js
index 2a2a5c6..3eecfdc 100644
--- a/src/page/splash/view.js
+++ b/src/page/splash/view.js
@@ -1,5 +1,5 @@
 import React from 'react';
-import { Lbry } from 'lbry-redux';
+import { Lbry, doPreferenceGet } from 'lbry-redux';
 import { Lbryio } from 'lbryinc';
 import { ActivityIndicator, Linking, NativeModules, Platform, Text, View } from 'react-native';
 import { NavigationActions, StackActions } from 'react-navigation';
@@ -113,8 +113,9 @@ class SplashScreen extends React.PureComponent {
 
   getUserSettings = () => {
     const { populateSharedUserState } = this.props;
-    Lbryio.call('user_settings', 'get').then(settings => {
-      populateSharedUserState(settings);
+
+    doPreferenceGet('shared', null, null, preference => {
+      populateSharedUserState(preference);
     });
   };
 

From 79c02825d187844d759514117c664f9426c641ca Mon Sep 17 00:00:00 2001
From: Akinwale Ariwodola <akinwale@gmail.com>
Date: Fri, 11 Oct 2019 17:12:51 +0100
Subject: [PATCH 3/3] preferences sync and wallet sync updates

---
 package.json                           |  4 ++--
 src/component/AppNavigator.js          | 21 ++++++++++++++++++++-
 src/component/channelSelector/index.js |  2 ++
 src/component/channelSelector/view.js  |  7 +++++--
 src/page/channelCreator/index.js       |  2 ++
 src/page/channelCreator/view.js        | 13 ++++++++++++-
 src/page/splash/view.js                | 11 ++---------
 src/page/wallet/view.js                |  4 +---
 8 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/package.json b/package.json
index dba18fb..3c44f78 100644
--- a/package.json
+++ b/package.json
@@ -12,8 +12,8 @@
         "base-64": "^0.1.0",
         "@expo/vector-icons": "^8.1.0",
         "gfycat-style-urls": "^1.0.3",
-        "lbry-redux": "lbryio/lbry-redux#23bcde0539a27fb19bf3a7d87683279194e02046",
-        "lbryinc": "lbryio/lbryinc#67bb3e215be3f13605c5e3f9f2b0e2fb880724cf",
+        "lbry-redux": "lbryio/lbry-redux#f06e1e64a8587a183bd7333f628fca821fe81fa4",
+        "lbryinc": "lbryio/lbryinc#02d8571cd7fafd00d1a60f133d884eb8c5f1a306",
         "lodash": ">=4.17.11",
         "merge": ">=1.2.1",
         "moment": "^2.22.1",
diff --git a/src/component/AppNavigator.js b/src/component/AppNavigator.js
index 2d68083..462d520 100644
--- a/src/component/AppNavigator.js
+++ b/src/component/AppNavigator.js
@@ -39,6 +39,7 @@ import {
   selectEmailToVerify,
   selectEmailVerifyIsPending,
   selectEmailVerifyErrorMessage,
+  selectHashChanged,
   selectUser,
 } from 'lbryinc';
 import { makeSelectClientSetting } from 'redux/selectors/settings';
@@ -53,6 +54,8 @@ import discoverStyle from 'styles/discover';
 import searchStyle from 'styles/search';
 import SearchRightHeaderIcon from 'component/searchRightHeaderIcon';
 
+const SYNC_GET_INTERVAL = 1000 * 60 * 5; // every 5 minutes
+
 const menuNavigationButton = navigation => (
   <NavigationButton
     name="bars"
@@ -272,6 +275,7 @@ class AppWithNavigationState extends React.Component {
     this.state = {
       emailVerifyDone: false,
       verifyPending: false,
+      syncHashChanged: false,
     };
   }
 
@@ -292,8 +296,17 @@ class AppWithNavigationState extends React.Component {
   }
 
   componentDidMount() {
+    const { dispatch } = this.props;
     this.emailVerifyCheckInterval = setInterval(() => this.checkEmailVerification(), 5000);
     Linking.addEventListener('url', this._handleUrl);
+
+    // call /sync/get with interval
+    setInterval(() => {
+      this.setState({ syncHashChanged: false }); // reset local state
+      NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => {
+        dispatch(doGetSync(walletPassword));
+      });
+    }, SYNC_GET_INTERVAL);
   }
 
   checkEmailVerification = () => {
@@ -320,7 +333,7 @@ class AppWithNavigationState extends React.Component {
   }
 
   componentDidUpdate() {
-    const { dispatch, user } = this.props;
+    const { dispatch, user, hashChanged } = this.props;
     if (this.state.verifyPending && this.emailVerifyCheckInterval > 0 && user && user.has_verified_email) {
       clearInterval(this.emailVerifyCheckInterval);
       AsyncStorage.setItem(Constants.KEY_EMAIL_VERIFY_PENDING, 'false');
@@ -332,6 +345,11 @@ class AppWithNavigationState extends React.Component {
       // get user settings after email verification
       this.getUserSettings();
     }
+
+    if (hashChanged && !this.state.syncHashChanged) {
+      this.setState({ syncHashChanged: true });
+      this.getUserSettings();
+    }
   }
 
   componentWillUpdate(nextProps) {
@@ -438,6 +456,7 @@ class AppWithNavigationState extends React.Component {
 
 const mapStateToProps = state => ({
   backgroundPlayEnabled: makeSelectClientSetting(SETTINGS.BACKGROUND_PLAY_ENABLED)(state),
+  hashChanged: selectHashChanged(state),
   keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state),
   nav: state.nav,
   toast: selectToast(state),
diff --git a/src/component/channelSelector/index.js b/src/component/channelSelector/index.js
index daee8c7..eb9bae9 100644
--- a/src/component/channelSelector/index.js
+++ b/src/component/channelSelector/index.js
@@ -7,6 +7,7 @@ import {
   doCreateChannel,
   doToast,
 } from 'lbry-redux';
+import { doGetSync } from 'lbryinc';
 import ChannelSelector from './view';
 
 const select = state => ({
@@ -19,6 +20,7 @@ const perform = dispatch => ({
   notify: data => dispatch(doToast(data)),
   createChannel: (name, amount) => dispatch(doCreateChannel(name, amount)),
   fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
+  getSync: password => dispatch(doGetSync(password)),
 });
 
 export default connect(
diff --git a/src/component/channelSelector/view.js b/src/component/channelSelector/view.js
index bd5cfbe..a4e6388 100644
--- a/src/component/channelSelector/view.js
+++ b/src/component/channelSelector/view.js
@@ -1,6 +1,6 @@
 import React from 'react';
 import { CLAIM_VALUES, isNameValid } from 'lbry-redux';
-import { ActivityIndicator, Picker, Text, TextInput, TouchableOpacity, View } from 'react-native';
+import { ActivityIndicator, NativeModules, Picker, Text, TextInput, TouchableOpacity, View } from 'react-native';
 import Button from 'component/button';
 import Colors from 'styles/colors';
 import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
@@ -117,7 +117,7 @@ export default class ChannelSelector extends React.PureComponent {
   };
 
   handleCreateChannelClick = () => {
-    const { balance, createChannel, onChannelChange, notify } = this.props;
+    const { balance, createChannel, getSync, onChannelChange, notify } = this.props;
     const { newChannelBid, newChannelName } = this.state;
 
     if (newChannelName.trim().length === 0 || !isNameValid(newChannelName.substr(1), false)) {
@@ -153,6 +153,9 @@ export default class ChannelSelector extends React.PureComponent {
       if (onChannelChange) {
         onChannelChange(channelName);
       }
+
+      // sync wallet
+      NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(password => getSync(password));
     };
 
     const failure = () => {
diff --git a/src/page/channelCreator/index.js b/src/page/channelCreator/index.js
index b58c369..085cf2a 100644
--- a/src/page/channelCreator/index.js
+++ b/src/page/channelCreator/index.js
@@ -12,6 +12,7 @@ import {
   doUpdateChannel,
   doToast,
 } from 'lbry-redux';
+import { doGetSync } from 'lbryinc';
 import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
 import { doUpdateChannelFormState, doClearChannelFormState } from 'redux/actions/form';
 import { selectDrawerStack } from 'redux/selectors/drawer';
@@ -37,6 +38,7 @@ const perform = dispatch => ({
   clearChannelFormState: () => dispatch(doClearChannelFormState()),
   createChannel: (name, amount, optionalParams) => dispatch(doCreateChannel(name, amount, optionalParams)),
   fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
+  getSync: password => dispatch(doGetSync(password)),
   updateChannel: params => dispatch(doUpdateChannel(params)),
   updateChannelFormState: data => dispatch(doUpdateChannelFormState(data)),
   pushDrawerStack: (routeName, params) => dispatch(doPushDrawerStack(routeName, params)),
diff --git a/src/page/channelCreator/view.js b/src/page/channelCreator/view.js
index 17a90d7..506a7aa 100644
--- a/src/page/channelCreator/view.js
+++ b/src/page/channelCreator/view.js
@@ -376,7 +376,15 @@ export default class ChannelCreator extends React.PureComponent {
   };
 
   handleCreateChannelClick = () => {
-    const { balance, clearChannelFormState, createChannel, onChannelChange, notify, updateChannel } = this.props;
+    const {
+      balance,
+      clearChannelFormState,
+      createChannel,
+      onChannelChange,
+      getSync,
+      notify,
+      updateChannel,
+    } = this.props;
     const {
       claimId,
       coverImageUrl,
@@ -439,6 +447,9 @@ export default class ChannelCreator extends React.PureComponent {
       clearChannelFormState();
       notify({ message: 'The channel was successfully created.' });
       this.showChannelList();
+
+      // sync wallet
+      NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(password => getSync(password));
     };
 
     const failure = () => {
diff --git a/src/page/splash/view.js b/src/page/splash/view.js
index 3eecfdc..b5ce480 100644
--- a/src/page/splash/view.js
+++ b/src/page/splash/view.js
@@ -16,8 +16,6 @@ import splashStyle from 'styles/splash';
 
 const BLOCK_HEIGHT_INTERVAL = 1000 * 60 * 2.5; // every 2.5 minutes
 
-const SETTINGS_GET_INTERVAL = 1000 * 60 * 5; // every 5 minutes
-
 const testingNetwork = 'Testing network';
 const waitingForResolution = 'Waiting for name resolution';
 
@@ -98,9 +96,7 @@ class SplashScreen extends React.PureComponent {
         // user is authenticated, navigate to the main view
         if (user.has_verified_email) {
           NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => {
-            if (walletPassword) {
-              getSync(walletPassword);
-            }
+            getSync(walletPassword);
             this.navigateToMain();
           });
           return;
@@ -140,14 +136,11 @@ class SplashScreen extends React.PureComponent {
 
       // get user settings interval
       this.getUserSettings();
-      setInterval(() => this.getUserSettings(), SETTINGS_GET_INTERVAL);
 
       if (user && user.id && user.has_verified_email) {
         // user already authenticated
         NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => {
-          if (walletPassword) {
-            getSync(walletPassword);
-          }
+          getSync(walletPassword);
           this.navigateToMain();
         });
       } else {
diff --git a/src/page/wallet/view.js b/src/page/wallet/view.js
index df6c5f5..491ef88 100644
--- a/src/page/wallet/view.js
+++ b/src/page/wallet/view.js
@@ -47,9 +47,7 @@ class WalletPage extends React.PureComponent {
     const { deviceWalletSynced, getSync, user } = this.props;
     if (deviceWalletSynced && user && user.has_verified_email) {
       NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => {
-        if (walletPassword) {
-          getSync(walletPassword);
-        }
+        getSync(walletPassword);
       });
     }
   };