text: import * as ACTIONS from 'constants/action_types';
  import { Lbryio } from 'lbryinc';
  import { SETTINGS, Lbry, doWalletEncrypt, doWalletDecrypt } from 'lbry-redux';
  import { selectGetSyncIsPending, selectSetSyncIsPending, selectSyncIsLocked } from 'redux/selectors/sync';
  import { makeSelectClientSetting } from 'redux/selectors/settings';
  import { getSavedPassword } from 'util/saved-passwords';
  import { doAnalyticsTagSync, doHandleSyncComplete } from 'redux/actions/app';
  import { selectUserVerifiedEmail } from 'redux/selectors/user';
  
  let syncTimer = null;
  const SYNC_INTERVAL = 1000 * 60 * 5; // 5 minutes
  const NO_WALLET_ERROR = 'no wallet found for this user';
  const BAD_PASSWORD_ERROR_NAME = 'InvalidPasswordError';
  
  export function doSetDefaultAccount(success, failure) {
    return dispatch => {
      dispatch({
        type: ACTIONS.SET_DEFAULT_ACCOUNT,
      });
  
      Lbry.account_list()
        .then(accountList => {
          const { lbc_mainnet: accounts } = accountList;
          let defaultId;
          for (let i = 0; i < accounts.length; ++i) {
            if (accounts[i].satoshis > 0) {
              defaultId = accounts[i].id;
              break;
            }
          }
  
          // In a case where there's no balance on either account
          // assume the second (which is created after sync) as default
          if (!defaultId && accounts.length > 1) {
            defaultId = accounts[1].id;
          }
  
          // Set the default account
          if (defaultId) {
            Lbry.account_set({ account_id: defaultId, default: true })
              .then(() => {
                if (success) {
                  success();
                }
              })
              .catch(err => {
                if (failure) {
                  failure(err);
                }
              });
          } else if (failure) {
            // no default account to set
            failure('Could not set a default account'); // fail
          }
        })
        .catch(err => {
          if (failure) {
            failure(err);
          }
        });
    };
  }
  
  export function doSetSync(oldHash, newHash, data) {
    return dispatch => {
      dispatch({
        type: ACTIONS.SET_SYNC_STARTED,
      });
  
      return Lbryio.call('sync', 'set', { old_hash: oldHash, new_hash: newHash, data }, 'post')
        .then(response => {
          if (!response.hash) {
            throw Error('No hash returned for sync/set.');
          }
  
          return dispatch({
            type: ACTIONS.SET_SYNC_COMPLETED,
            data: { syncHash: response.hash },
          });
        })
        .catch(error => {
          dispatch({
            type: ACTIONS.SET_SYNC_FAILED,
            data: { error },
          });
        });
    };
  }
  
  export const doGetSyncDesktop = (cb?, password) => (dispatch, getState) => {
    const state = getState();
    const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
    const getSyncPending = selectGetSyncIsPending(state);
    const setSyncPending = selectSetSyncIsPending(state);
    const syncLocked = selectSyncIsLocked(state);
  
    return getSavedPassword().then(savedPassword => {
      const passwordArgument = password || password === '' ? password : savedPassword === null ? '' : savedPassword;
  
      if (syncEnabled && !getSyncPending && !setSyncPending && !syncLocked) {
        return dispatch(doGetSync(passwordArgument, cb));
      }
    });
  };
  
  export function doSyncSubscribe() {
    return (dispatch, getState) => {
      if (syncTimer) clearInterval(syncTimer);
      const state = getState();
      const hasVerifiedEmail = selectUserVerifiedEmail(state);
      const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
      const syncLocked = selectSyncIsLocked(state);
      if (hasVerifiedEmail && syncEnabled && !syncLocked) {
        dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
        dispatch(doAnalyticsTagSync());
        syncTimer = setInterval(() => {
          const state = getState();
          const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
          if (syncEnabled) {
            dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
            dispatch(doAnalyticsTagSync());
          }
        }, SYNC_INTERVAL);
      }
    };
  }
  
  export function doSyncUnsubscribe() {
    return dispatch => {
      if (syncTimer) {
        clearInterval(syncTimer);
      }
    };
  }
  
  export function doGetSync(passedPassword, callback) {
    const password = passedPassword === null || passedPassword === undefined ? '' : passedPassword;
  
    function handleCallback(error, hasNewData) {
      if (callback) {
        if (typeof callback !== 'function') {
          throw new Error('Second argument passed to "doGetSync" must be a function');
        }
  
        callback(error, hasNewData);
      }
    }
  
    return dispatch => {
      dispatch({
        type: ACTIONS.GET_SYNC_STARTED,
      });
  
      const data = {};
  
      Lbry.wallet_status()
        .then(status => {
          if (status.is_locked) {
            return Lbry.wallet_unlock({ password });
          }
  
          // Wallet is already unlocked
          return true;
        })
        .then(isUnlocked => {
          if (isUnlocked) {
            return Lbry.sync_hash();
          }
          data.unlockFailed = true;
          throw new Error();
        })
        .then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
        .then(response => {
          const syncHash = response.hash;
          data.syncHash = syncHash;
          data.syncData = response.data;
          data.changed = response.changed;
          data.hasSyncedWallet = true;
  
          if (response.changed) {
            return Lbry.sync_apply({ password, data: response.data, blocking: true });
          }
        })
        .then(response => {
          if (!response) {
            dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
            handleCallback(null, data.changed);
            return;
          }
  
          const { hash: walletHash, data: walletData } = response;
  
          if (walletHash !== data.syncHash) {
            // different local hash, need to synchronise
            dispatch(doSetSync(data.syncHash, walletHash, walletData));
          }
  
          dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
          handleCallback(null, data.changed);
        })
        .catch(syncAttemptError => {
          const badPasswordError =
            syncAttemptError && syncAttemptError.data && syncAttemptError.data.name === BAD_PASSWORD_ERROR_NAME;
  
          if (data.unlockFailed) {
            dispatch({ type: ACTIONS.GET_SYNC_FAILED, data: { error: syncAttemptError } });
  
            if (badPasswordError) {
              dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
            }
  
            handleCallback(syncAttemptError);
          } else if (data.hasSyncedWallet) {
            const error = (syncAttemptError && syncAttemptError.message) || 'Error getting synced wallet';
            dispatch({
              type: ACTIONS.GET_SYNC_FAILED,
              data: {
                error,
              },
            });
  
            if (badPasswordError) {
              dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
            }
  
            handleCallback(error);
          } else {
            const noWalletError = syncAttemptError && syncAttemptError.message === NO_WALLET_ERROR;
  
            dispatch({
              type: ACTIONS.GET_SYNC_COMPLETED,
              data: {
                hasSyncedWallet: false,
                syncHash: null,
                // If there was some unknown error, bail
                fatalError: !noWalletError,
              },
            });
  
            // user doesn't have a synced wallet
            //   call sync_apply to get data to sync
            //   first time sync. use any string for old hash
            if (noWalletError) {
              Lbry.sync_apply({ password })
                .then(({ hash: walletHash, data: syncApplyData }) => {
                  dispatch(doSetSync('', walletHash, syncApplyData, password));
                  handleCallback();
                })
                .catch(syncApplyError => {
                  handleCallback(syncApplyError);
                });
            }
          }
        });
    };
  }
  
  export function doSyncApply(syncHash, syncData, password) {
    return dispatch => {
      dispatch({
        type: ACTIONS.SYNC_APPLY_STARTED,
      });
  
      Lbry.sync_apply({ password, data: syncData })
        .then(({ hash: walletHash, data: walletData }) => {
          dispatch({
            type: ACTIONS.SYNC_APPLY_COMPLETED,
          });
  
          if (walletHash !== syncHash) {
            // different local hash, need to synchronise
            dispatch(doSetSync(syncHash, walletHash, walletData));
          }
        })
        .catch(() => {
          dispatch({
            type: ACTIONS.SYNC_APPLY_FAILED,
            data: {
              error: 'Invalid password specified. Please enter the password for your previously synchronised wallet.',
            },
          });
        });
    };
  }
  
  export function doCheckSync() {
    return dispatch => {
      dispatch({
        type: ACTIONS.GET_SYNC_STARTED,
      });
  
      Lbry.sync_hash().then(hash => {
        Lbryio.call('sync', 'get', { hash }, 'post')
          .then(response => {
            const data = {
              hasSyncedWallet: true,
              syncHash: response.hash,
              syncData: response.data,
              hashChanged: response.changed,
            };
            dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
          })
          .catch(() => {
            // user doesn't have a synced wallet
            dispatch({
              type: ACTIONS.GET_SYNC_COMPLETED,
              data: { hasSyncedWallet: false, syncHash: null },
            });
          });
      });
    };
  }
  
  export function doResetSync() {
    return dispatch =>
      new Promise(resolve => {
        dispatch({ type: ACTIONS.SYNC_RESET });
        resolve();
      });
  }
  
  export function doSyncEncryptAndDecrypt(oldPassword, newPassword, encrypt) {
    return dispatch => {
      const data = {};
      return Lbry.sync_hash()
        .then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
        .then(syncGetResponse => {
          data.oldHash = syncGetResponse.hash;
  
          return Lbry.sync_apply({ password: oldPassword, data: syncGetResponse.data });
        })
        .then(() => {
          if (encrypt) {
            dispatch(doWalletEncrypt(newPassword));
          } else {
            dispatch(doWalletDecrypt());
          }
        })
        .then(() => Lbry.sync_apply({ password: newPassword }))
        .then(syncApplyResponse => {
          if (syncApplyResponse.hash !== data.oldHash) {
            return dispatch(doSetSync(data.oldHash, syncApplyResponse.hash, syncApplyResponse.data));
          }
        })
        .catch(console.error); // eslint-disable-line
    };
  }
  
  export function doSetSyncLock(lock) {
    return {
      type: ACTIONS.SET_SYNC_LOCK,
      data: lock,
    };
  }
  
  export function doSetPrefsReady() {
    return {
      type: ACTIONS.SET_PREFS_READY,
      data: true,
    };
  }
  